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 "some\POSIX\path.txt" to FolderItem.
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
fromPathParts.Append tmpf.Name
tmpf = tmpf.Parent
wend
tmpf = toFile
while tmpf <> nil
toPathParts.Append tmpf.Name
tmpf = tmpf.Parent
wend
// 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
return toFile.POSIXPath
end if
// 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)
fromPathParts.Remove(fromPathParts.Ubound)
toPathParts.Remove(toPathParts.Ubound)
wend
// 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
else
for i = 0 to fromPathParts.Ubound
relativePathParts.Append kUpDirPath
next
end if
// Now that we're up to the same parent folder, we need to traverse down into
// each "toPathPart"
while toPathParts.Ubound >= 0
relativePathParts.Append toPathParts.Pop
wend
// Finally, return the result
return Join (relativePathParts, pathSeparator)
End Function
```
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).