Before I reinvent the wheel, does anyone have a method that is able to standardise a Windows file path to a POSIX (i.e. forward-slash separated) one and vice versa?
For my ObjoScript language, I’m implementing file handling. Since it’s cross-platform, I would like all paths passed to ObjoScript’s file system class to be in POSIX format. Behind the scenes, this uses
FolderItem to manipulate the host’s file system.
If the interpreter is running on Linux or macOS then there is no conversion - this would work:
var file = FSItem("some/POSIX/path.txt")
But on Windows that would fail because I should be passing
Has anyone already implemented a method to do this before I attack it and try to catch the edge cases?
Not me, though I was challenged with adding such a feature to FindAnyFile just a week ago (didn’t do it for it being not that easy). The challenge is that referring to the right volume may be difficult. As long as it’s all on the boot volume, it might be easier.
Also be aware that there’s two types used on Win: Classic DOS, like “C:\Users\user\Desktop” and UNC (“\\user\Desktop”). UNC may be the smarter choice. See also: File path formats on Windows systems | Microsoft Learn
And if you can limit your paths to relative ones (e.g. relativ to the project folder), then you might have it much easier, too. Replacing / with \ may all you need in that case, by using DOS paths.
Oh, you will have to replace or drop some more illegal chars (colon, double quote and some more - google it) on the windows side, though. I suggest you only do that at runtime on the Windows side, and leave the chars in your stored string, so that they remain valid when used on macOS.
If you need to make relativ paths, I have a function for that, see below.
Function RelativePOSIXPathTo(extends fromFile as FolderItem, toFile as FolderItem) As String
// returns the full path to toFile if a relative path can't be created
// based on code provided by Jonathan Johnson
const kUpDirPath = ".."
const kSameDirPath = "."
const pathSeparator = "/"
// Get a list of path parts by looping until the parent is nil.
dim tmpf as FolderItem
dim fromPathParts() as String
dim toPathParts() as String
tmpf = fromFile
while tmpf <> nil
tmpf = tmpf.Parent
tmpf = toFile
while tmpf <> nil
tmpf = tmpf.Parent
// If the top-level items don't match, we're on different volumes, and that ain't going to work
if fromPathParts (fromPathParts.Ubound) <> toPathParts (toPathParts.Ubound) then
// Now, remove each of the common items from the tops of both arrays.
while fromPathParts.Ubound >= 0 and toPathParts.Ubound >= 0 _
and fromPathParts(fromPathParts.Ubound) = toPathParts(toPathParts.Ubound)
// Now, for every "from" path part that's remaining, it's one level we must go up.
dim relativePathParts() as String
dim i as integer
if fromPathParts.Ubound < 0 then
// Just operate on the current directory
// !TT not necessary: relativePathParts.Append kSameDirPath
for i = 0 to fromPathParts.Ubound
// Now that we're up to the same parent folder, we need to traverse down into
// each "toPathPart"
while toPathParts.Ubound >= 0
// Finally, return the result
return Join (relativePathParts, pathSeparator)
That’s really helpful Thomas, thank you.
Spent the last couple of hours on this - it is very complex isn’t it with a lot of edge cases!
I know. I had the same troubles with resolving the multiple refs for external files in Arbed when reading a Xojo project (where multiple paths are stored for the various platforms, including a Finder Alias, and I need to resolve them all to check if any are pointing to the right file).
I set a kPathSeparator variable for platforms to “/” or “\” as needed. I then do a ReplaceAllB based on the platform coming from / going to.
If coming from Windows to POSIX, I preface the line with “/” and drop the “:”
If moving from POSIX to Windows, I drop the leading “/” and ask the user for the Drive letter and then swap the kPathSeparator using ReplaceAllB.
Becomes (assuming the user has chosen F: as the drive):
Of course, your handling of the drive letter is left to you to choose.