Create a relative path

Share your advanced PureBasic knowledge/code with the community.
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Create a relative path

Post by Little John »

Works also with PB 5.20

Hi,

sometimes it is useful, to know the name of a path relative to a given directory. The following code converts an absolute path to a relative path.

Regards, Little John

//edit 2010-09-22: Slightly simplified the code.

Code: Select all

; -- Convert an absolute path to a relative one.
; Tested with PB 4.51 on Windows XP and Ubuntu 10.04

CompilerSelect #PB_Compiler_OS
   CompilerCase #PB_OS_Windows
      #Slash = '\'
      #BadSlash = '/'
   CompilerDefault
      #Slash = '/'
      #BadSlash = '\'
CompilerEndSelect

Procedure.i EqualPathLen (s1$, s2$)
   ; -- return length of identical part of the paths in s1$ and s2$
   Protected maxEqual, temp, equal, ret
   Protected *p1.Character, *p2.Character
   
   CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      s1$ = UCase(s1$)
      s2$ = UCase(s2$)
   CompilerEndIf
   
   maxEqual = Len(s1$)
   temp = Len(s2$)
   If maxEqual > temp
      maxEqual = temp
   EndIf
   
   *p1 = @s1$
   *p2 = @s2$
   equal = 0
   ret = 0
   While equal < maxEqual And *p1\c = *p2\c
      equal + 1
      If *p1\c = #Slash
         ret = equal
      EndIf   
      *p1 + SizeOf(character)
      *p2 + SizeOf(character)
   Wend
   
   ProcedureReturn ret
EndProcedure

Procedure.s RelativePath (baseDir$, absPath$)
   ; -- convert an absolute path to a relative one
   ; in : baseDir$: full name of a directory, with trailing (back)slash
   ;      absPath$: full name of a path to a directory or file
   ; out: absPath$ converted, so that it is relative to baseDir$
   Protected equal, s, i, parent$, ret$=""
   
   CompilerIf #PB_Compiler_OS = #PB_OS_Windows
      If UCase(Left(baseDir$, 1)) <> UCase(Left(absPath$, 1))
         ProcedureReturn absPath$  ; can't build a relative path
      EndIf
   CompilerEndIf

   ReplaceString(baseDir$, Chr(#BadSlash), Chr(#Slash), #PB_String_InPlace)
   ReplaceString(absPath$, Chr(#BadSlash), Chr(#Slash), #PB_String_InPlace)
   equal = EqualPathLen(baseDir$, absPath$)
   
   s = CountString(Mid(baseDir$, equal+1), Chr(#Slash))
   parent$ = ".." + Chr(#Slash)
   For i = 1 To s
      ret$ + parent$
   Next   
   
   ProcedureReturn ret$ + Mid(absPath$, equal+1)
EndProcedure


;-- Demo
CompilerSelect #PB_Compiler_OS
   CompilerCase #PB_OS_Windows
      Debug RelativePath("F:\Data\John\", "f:\Data\John\PC")
      Debug RelativePath("F:\Data\John\", "f:\Data\John\")
      Debug RelativePath("F:\Data\John\", "f:\Data\John")
      Debug RelativePath("F:\Data\John\", "f:\Data\Peter\")
      Debug RelativePath("F:\Data\John\", "f:\")
      Debug RelativePath("F:\", "f:\Data\John\")
      Debug RelativePath("F:\Data\John\", "g:\Videos")
   CompilerDefault
      Debug RelativePath("/media/Corsair/Data/John/", "/media/Corsair/Data/John/PC")
      Debug RelativePath("/media/Corsair/Data/John/", "/media/Corsair/Data/John/")
      Debug RelativePath("/media/Corsair/Data/John/", "/media/Corsair/Data/John")
      Debug RelativePath("/media/Corsair/Data/John/", "/media/Corsair/Data/Peter/")
      Debug RelativePath("/media/Corsair/Data/John/", "/media/Corsair/")
      Debug RelativePath("/media/Corsair/", "/media/Corsair/Data/John/")
      Debug RelativePath("/media/Corsair/Data/John/", "/media/Maxtor/Videos")
CompilerEndSelect
Last edited by Little John on Mon Aug 19, 2013 3:58 pm, edited 2 times in total.
User avatar
luis
Addict
Addict
Posts: 3895
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Create a relative path

Post by luis »

Very useful, thanks :-)
"Have you tried turning it off and on again ?"
Flower
User
User
Posts: 22
Joined: Fri Jan 08, 2010 8:05 am
Location: United States

Re: Create a relative path

Post by Flower »

FYI, if you're on Windows, you can use shell function PathRelativePathTo:
http://msdn.microsoft.com/en-us/library ... S.85).aspx
Registered PureBasic user since 4.50
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Re: Create a relative path

Post by blueznl »

Duh, after looking at http://msdn.microsoft.com/en-us/library ... 85%29.aspx I realized perhaps I should now and again have a look at the MSDN...
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
kvitaliy
Enthusiast
Enthusiast
Posts: 162
Joined: Mon May 10, 2010 4:02 pm

Re: Create a relative path

Post by kvitaliy »

Flower wrote:FYI, if you're on Windows, you can use shell function PathRelativePathTo:
http://msdn.microsoft.com/en-us/library ... S.85).aspx
Examples for PureBasic

Code: Select all

szOut.s=Space(#MAX_PATH)
szFrom.s = "c:\\a\\b\\path";
szTo.s = "c:\\a\\x\\y\\file";

PathRelativePathTo_(@szOut,szFrom,#FILE_ATTRIBUTE_DIRECTORY,szTo,#FILE_ATTRIBUTE_NORMAL);
Debug szOut
; OUTPUT:
; ==================
; The relative path is relative from: c:\a\b\path
; The relative path is relative To: c:\a\x\y\file
; The relative path is: ..\..\x\y\file
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: Create a relative path

Post by Little John »

Hi,

thanks for your replies, and the additional information.
Flower wrote:FYI, if you're on Windows, you can use shell function PathRelativePathTo:
http://msdn.microsoft.com/en-us/library ... S.85).aspx
I knew about the WinAPI function GetFullPathName_(), and looked it up on MSDN. On that page there is a link to another page about Naming Files, Paths, and Namespaces, which even contains a section named "Fully Qualified vs. Relative Paths" -- but nowhere a word about PathRelativePathTo_() ... :x
blueznl wrote:Duh, after looking at http://msdn.microsoft.com/en-us/library ... 85%29.aspx I realized perhaps I should now and again have a look at the MSDN...
Uuh, that's really impressive. :D

Regards, Little John
Flower
User
User
Posts: 22
Joined: Fri Jan 08, 2010 8:05 am
Location: United States

Re: Create a relative path

Post by Flower »

Little John wrote: I knew about the WinAPI function GetFullPathName_(), and looked it up on MSDN. On that page there is a link to another page about Naming Files, Paths, and Namespaces, which even contains a section named "Fully Qualified vs. Relative Paths" -- but nowhere a word about PathRelativePathTo_() ... :x
Yep, seems kernel32 documentation writer at Microsoft only knows (or intentionally ignores) shell32, user32 and other useful libraries.

Explorer.exe use shell functions heavily, but IMHO shell function is not as bullet-proof as kernel32 functions, which might be why Explorer.exe crashes so frequently :wink:

BTW, the regular shell functions are more powerful than these "Shell Lightweight Utility Functions", but usually requires a COM interface.
http://msdn.microsoft.com/en-us/library ... S.85).aspx
Registered PureBasic user since 4.50
cas
Enthusiast
Enthusiast
Posts: 597
Joined: Mon Nov 03, 2008 9:56 pm

Re: Create a relative path

Post by cas »

Flower wrote:Yep, seems kernel32 documentation writer at Microsoft only knows (or intentionally ignores) shell32, user32 and other useful libraries.
You mean 'writers', not 'writer' :lol: .
I don't know any function that is available for use but missing from MSDN. And those two that you quoted are in this groups:
PathRelativePathTo: http://msdn.microsoft.com/en-us/library ... S.85).aspx
GetFullPathName: http://msdn.microsoft.com/en-us/library ... S.85).aspx
Flower wrote:Explorer.exe use shell functions heavily, but IMHO shell function is not as bullet-proof as kernel32 functions
All functions listed on MSDN are bullet-proof. They can't sell something that is not working as expected and have market share more than 90% :shock: .
Flower wrote:which might be why Explorer.exe crashes so frequently :wink:
It crashes because of buggy 3rd party apps. :wink:
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: Create a relative path

Post by Little John »

Flower wrote:Yep, seems kernel32 documentation writer at Microsoft only knows (or intentionally ignores) shell32, user32 and other useful libraries.
Quote from a recent post on the German forum, regarding MSDN and a different topic:
Weiß bei M$ eigentlich die eine Hand, was die andere tut???
Translation by me: "Does at MS the right hand know what the left hand's doing?"

:)

Regards, Little John
cas
Enthusiast
Enthusiast
Posts: 597
Joined: Mon Nov 03, 2008 9:56 pm

Re: Create a relative path

Post by cas »

Of course they can't know, Microsoft has around 100,000 employees.
@Flower:
You wrote "kernel32 documentation writer", it is "writers", not "writer". :wink:
Flower
User
User
Posts: 22
Joined: Fri Jan 08, 2010 8:05 am
Location: United States

Re: Create a relative path

Post by Flower »

cas wrote:Of course they can't know, Microsoft has around 100,000 employees.
@Flower:
You wrote "kernel32 documentation writer", it is "writers", not "writer". :wink:
Yeah, writers, that's right.
Anyways, hope they do mention more shell functions in the related articles in the future, those function can really save some coding time... and MSDN search is kinda painful, I have to use third party search engines. :(
Registered PureBasic user since 4.50
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: Create a relative path

Post by Little John »

Back to topic. :)

A small comparison between the WinAPI function and my routine is interesting:

Code: Select all

<Include constants and procedures from the first post of this thread here>

Define ret.s{#MAX_PATH}

Debug "PathRelativePathTo_():"
Debug "----------------------"
PathRelativePathTo_(@ret, @"F:\Data\John\", #FILE_ATTRIBUTE_DIRECTORY, @"f:\Data\John\pc.txt", #FILE_ATTRIBUTE_NORMAL)
Debug ret
PathRelativePathTo_(@ret, @"F:\Data\John\", #FILE_ATTRIBUTE_DIRECTORY, @"f:\Data\John\", #FILE_ATTRIBUTE_DIRECTORY)
Debug ret
PathRelativePathTo_(@ret, @"F:\Data\John\", #FILE_ATTRIBUTE_DIRECTORY, @"f:\Data\John", #FILE_ATTRIBUTE_NORMAL)
Debug ret
PathRelativePathTo_(@ret, @"F:\Data\John\", #FILE_ATTRIBUTE_DIRECTORY, @"f:\Data\Peter\", #FILE_ATTRIBUTE_DIRECTORY)
Debug ret
PathRelativePathTo_(@ret, @"F:\Data\John\", #FILE_ATTRIBUTE_DIRECTORY, @"f:\", #FILE_ATTRIBUTE_DIRECTORY)
Debug ret
PathRelativePathTo_(@ret, @"F:\", #FILE_ATTRIBUTE_DIRECTORY, @"f:\Data\John\", #FILE_ATTRIBUTE_DIRECTORY)
Debug ret
Debug ""
Debug ""
Debug "RelativePath():"
Debug "---------------"
Debug RelativePath("F:\Data\John\", "f:\Data\John\pc.txt")
Debug RelativePath("F:\Data\John\", "f:\Data\John\")
Debug RelativePath("F:\Data\John\", "f:\Data\John")
Debug RelativePath("F:\Data\John\", "f:\Data\Peter\")
Debug RelativePath("F:\Data\John\", "f:\")
Debug RelativePath("F:\", "f:\Data\John\")
The output is (on Windows XP SP 3 x86):

Code: Select all

PathRelativePathTo_():
----------------------
\pc.txt
.
..\John
..\Peter\
..\..
.\Data\John\


RelativePath():
---------------
pc.txt

..\John
..\Peter\
..\..\
Data\John\
Conclusions:
  • The WinAPI function PathRelativePathTo produces inconsistent results.
    A returned directory name sometimes has and sometimes doesn't have a trailing backslash (last but one and last example).
  • The WinAPI function PathRelativePathTo sometimes returns wrong results (first example)!
    "\pc.txt" denotes a file named "pc.txt" in the root directory of the current drive. But looking at the original absolute name, we see that that file is not in the root directory!
  • My function doesn't have these problems. :D
Regards, Little John
cas
Enthusiast
Enthusiast
Posts: 597
Joined: Mon Nov 03, 2008 9:56 pm

Re: Create a relative path

Post by cas »

The results of PathRelativePathTo_() are always exact and consistent, same as in your own function posted here. There are no problems, your function handles it in different way so we have different outputs but both are perfectly fine and reliable in situations when they are needed.
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: Create a relative path

Post by Little John »

cas wrote:The results of PathRelativePathTo_() are always exact and consistent,
False. I just proved the contrary.
cas
Enthusiast
Enthusiast
Posts: 597
Joined: Mon Nov 03, 2008 9:56 pm

Re: Create a relative path

Post by cas »

Little John wrote:
cas wrote:The results of PathRelativePathTo_() are always exact and consistent,
False. I just proved the contrary.
False. You proved nothing. Backslash is not added because you didn't have it when you called PathRelativePathTo_() in 3rd example ( and you are talking about consistency? :lol: ) and 5th example is root path so there is no need for backslash. Look again at your code. :wink:
PathRelativePathTo_() returns technically perfect results and your code returns path that is not 'technically' correct but still works fine and i thank you for that. At least your solution is cross-platform.
Post Reply