On windows, os.relativePath returns path as is when roots are different (#12329)
* On windows, os.relativePath returns path as is when roots are different * Implement os.sameRoot without windows API * Fix compile error when compiling lib/nimhcr.nim * Fix compile error when compiling lib/nimhcr.nim on Windows
This commit is contained in:
parent
f9d95fd6a7
commit
509f53b782
2 changed files with 106 additions and 28 deletions
119
lib/pure/os.nim
119
lib/pure/os.nim
|
|
@ -232,11 +232,92 @@ proc splitPath*(path: string): tuple[head, tail: string] {.
|
||||||
result.head = ""
|
result.head = ""
|
||||||
result.tail = path
|
result.tail = path
|
||||||
|
|
||||||
|
proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raises: [].} =
|
||||||
|
## Checks whether a given `path` is absolute.
|
||||||
|
##
|
||||||
|
## On Windows, network paths are considered absolute too.
|
||||||
|
runnableExamples:
|
||||||
|
assert not "".isAbsolute
|
||||||
|
assert not ".".isAbsolute
|
||||||
|
when defined(posix):
|
||||||
|
assert "/".isAbsolute
|
||||||
|
assert not "a/".isAbsolute
|
||||||
|
assert "/a/".isAbsolute
|
||||||
|
|
||||||
|
if len(path) == 0: return false
|
||||||
|
|
||||||
|
when doslikeFileSystem:
|
||||||
|
var len = len(path)
|
||||||
|
result = (path[0] in {'/', '\\'}) or
|
||||||
|
(len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
|
||||||
|
elif defined(macos):
|
||||||
|
# according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
|
||||||
|
result = path[0] != ':'
|
||||||
|
elif defined(RISCOS):
|
||||||
|
result = path[0] == '$'
|
||||||
|
elif defined(posix):
|
||||||
|
result = path[0] == '/'
|
||||||
|
|
||||||
when FileSystemCaseSensitive:
|
when FileSystemCaseSensitive:
|
||||||
template `!=?`(a, b: char): bool = a != b
|
template `!=?`(a, b: char): bool = a != b
|
||||||
else:
|
else:
|
||||||
template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b)
|
template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b)
|
||||||
|
|
||||||
|
when doslikeFileSystem:
|
||||||
|
proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: []} =
|
||||||
|
## An absolute path from the root of the current drive (e.g. "\foo")
|
||||||
|
path.len > 0 and
|
||||||
|
(path[0] == AltSep or
|
||||||
|
(path[0] == DirSep and
|
||||||
|
(path.len == 1 or path[1] notin {DirSep, AltSep, ':'})))
|
||||||
|
|
||||||
|
proc isUNCPrefix(path: string): bool {.noSideEffect, raises: []} =
|
||||||
|
path[0] == DirSep and path[1] == DirSep
|
||||||
|
|
||||||
|
proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: []} =
|
||||||
|
## Return true if path1 and path2 have a same root.
|
||||||
|
##
|
||||||
|
## Detail of windows path formats:
|
||||||
|
## https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats
|
||||||
|
|
||||||
|
assert(isAbsolute(path1))
|
||||||
|
assert(isAbsolute(path2))
|
||||||
|
|
||||||
|
let
|
||||||
|
len1 = path1.len
|
||||||
|
len2 = path2.len
|
||||||
|
assert(len1 != 0 and len2 != 0)
|
||||||
|
|
||||||
|
if isAbsFromCurrentDrive(path1) and isAbsFromCurrentDrive(path2):
|
||||||
|
return true
|
||||||
|
elif len1 == 1 or len2 == 1:
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
if path1[1] == ':' and path2[1] == ':':
|
||||||
|
return path1[0].toLowerAscii() == path2[0].toLowerAscii()
|
||||||
|
else:
|
||||||
|
var
|
||||||
|
p1, p2: PathIter
|
||||||
|
pp1 = next(p1, path1)
|
||||||
|
pp2 = next(p2, path2)
|
||||||
|
if pp1[1] - pp1[0] == 1 and pp2[1] - pp2[0] == 1 and
|
||||||
|
isUNCPrefix(path1) and isUNCPrefix(path2):
|
||||||
|
#UNC
|
||||||
|
var h = 0
|
||||||
|
while p1.hasNext(path1) and p2.hasNext(path2) and h < 2:
|
||||||
|
pp1 = next(p1, path1)
|
||||||
|
pp2 = next(p2, path2)
|
||||||
|
let diff = pp1[1] - pp1[0]
|
||||||
|
if diff != pp2[1] - pp2[0]:
|
||||||
|
return false
|
||||||
|
for i in 0..diff:
|
||||||
|
if path1[i + pp1[0]] !=? path2[i + pp2[0]]:
|
||||||
|
return false
|
||||||
|
inc h
|
||||||
|
return h == 2
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
|
||||||
proc relativePath*(path, base: string; sep = DirSep): string {.
|
proc relativePath*(path, base: string; sep = DirSep): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1", raises: [].} =
|
noSideEffect, rtl, extern: "nos$1", raises: [].} =
|
||||||
## Converts `path` to a path relative to `base`.
|
## Converts `path` to a path relative to `base`.
|
||||||
|
|
@ -245,6 +326,10 @@ proc relativePath*(path, base: string; sep = DirSep): string {.
|
||||||
## this can be useful to ensure the relative path only contains `'/'`
|
## this can be useful to ensure the relative path only contains `'/'`
|
||||||
## so that it can be used for URL constructions.
|
## so that it can be used for URL constructions.
|
||||||
##
|
##
|
||||||
|
## On windows, if a root of `path` and a root of `base` are different,
|
||||||
|
## returns `path` as is because it is impossible to make a relative path.
|
||||||
|
## That means an absolute path can be returned.
|
||||||
|
##
|
||||||
## See also:
|
## See also:
|
||||||
## * `splitPath proc <#splitPath,string>`_
|
## * `splitPath proc <#splitPath,string>`_
|
||||||
## * `parentDir proc <#parentDir,string>`_
|
## * `parentDir proc <#parentDir,string>`_
|
||||||
|
|
@ -256,9 +341,13 @@ proc relativePath*(path, base: string; sep = DirSep): string {.
|
||||||
assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
|
assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
|
||||||
assert relativePath("", "/users/moo", '/') == ""
|
assert relativePath("", "/users/moo", '/') == ""
|
||||||
|
|
||||||
# Todo: If on Windows, path and base do not agree on the drive letter,
|
|
||||||
# return `path` as is.
|
|
||||||
if path.len == 0: return ""
|
if path.len == 0: return ""
|
||||||
|
|
||||||
|
when doslikeFileSystem:
|
||||||
|
if isAbsolute(path) and isAbsolute(base):
|
||||||
|
if not sameRoot(path, base):
|
||||||
|
return path
|
||||||
|
|
||||||
var f, b: PathIter
|
var f, b: PathIter
|
||||||
var ff = (0, -1)
|
var ff = (0, -1)
|
||||||
var bb = (0, -1) # (int, int)
|
var bb = (0, -1) # (int, int)
|
||||||
|
|
@ -645,32 +734,6 @@ proc cmpPaths*(pathA, pathB: string): int {.
|
||||||
else:
|
else:
|
||||||
result = cmpIgnoreCase(a, b)
|
result = cmpIgnoreCase(a, b)
|
||||||
|
|
||||||
proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
|
|
||||||
## Checks whether a given `path` is absolute.
|
|
||||||
##
|
|
||||||
## On Windows, network paths are considered absolute too.
|
|
||||||
runnableExamples:
|
|
||||||
assert not "".isAbsolute
|
|
||||||
assert not ".".isAbsolute
|
|
||||||
when defined(posix):
|
|
||||||
assert "/".isAbsolute
|
|
||||||
assert not "a/".isAbsolute
|
|
||||||
assert "/a/".isAbsolute
|
|
||||||
|
|
||||||
if len(path) == 0: return false
|
|
||||||
|
|
||||||
when doslikeFileSystem:
|
|
||||||
var len = len(path)
|
|
||||||
result = (path[0] in {'/', '\\'}) or
|
|
||||||
(len > 1 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':')
|
|
||||||
elif defined(macos):
|
|
||||||
# according to https://perldoc.perl.org/File/Spec/Mac.html `:a` is a relative path
|
|
||||||
result = path[0] != ':'
|
|
||||||
elif defined(RISCOS):
|
|
||||||
result = path[0] == '$'
|
|
||||||
elif defined(posix):
|
|
||||||
result = path[0] == '/'
|
|
||||||
|
|
||||||
proc unixToNativePath*(path: string, drive=""): string {.
|
proc unixToNativePath*(path: string, drive=""): string {.
|
||||||
noSideEffect, rtl, extern: "nos$1".} =
|
noSideEffect, rtl, extern: "nos$1".} =
|
||||||
## Converts an UNIX-like path to a native one.
|
## Converts an UNIX-like path to a native one.
|
||||||
|
|
|
||||||
|
|
@ -334,6 +334,21 @@ block ospaths:
|
||||||
doAssert relativePath("/foo", "/fOO", '/') == (when FileSystemCaseSensitive: "../foo" else: "")
|
doAssert relativePath("/foo", "/fOO", '/') == (when FileSystemCaseSensitive: "../foo" else: "")
|
||||||
doAssert relativePath("/foO", "/foo", '/') == (when FileSystemCaseSensitive: "../foO" else: "")
|
doAssert relativePath("/foO", "/foo", '/') == (when FileSystemCaseSensitive: "../foO" else: "")
|
||||||
|
|
||||||
|
when doslikeFileSystem:
|
||||||
|
doAssert relativePath(r"c:\foo.nim", r"C:\") == r"foo.nim"
|
||||||
|
doAssert relativePath(r"c:\foo\bar\baz.nim", r"c:\foo") == r"bar\baz.nim"
|
||||||
|
doAssert relativePath(r"c:\foo\bar\baz.nim", r"d:\foo") == r"c:\foo\bar\baz.nim"
|
||||||
|
doAssert relativePath(r"\foo\baz.nim", r"\foo") == r"baz.nim"
|
||||||
|
doAssert relativePath(r"\foo\bar\baz.nim", r"\bar") == r"..\foo\bar\baz.nim"
|
||||||
|
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foo\bar") == r"baz.nim"
|
||||||
|
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foO\bar") == r"baz.nim"
|
||||||
|
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\bar\bar") == r"\\foo\bar\baz.nim"
|
||||||
|
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\foo\car") == r"\\foo\bar\baz.nim"
|
||||||
|
doAssert relativePath(r"\\foo\bar\baz.nim", r"\\goo\bar") == r"\\foo\bar\baz.nim"
|
||||||
|
doAssert relativePath(r"\\foo\bar\baz.nim", r"c:\") == r"\\foo\bar\baz.nim"
|
||||||
|
doAssert relativePath(r"\\foo\bar\baz.nim", r"\foo") == r"\\foo\bar\baz.nim"
|
||||||
|
doAssert relativePath(r"c:\foo.nim", r"\foo") == r"c:\foo.nim"
|
||||||
|
|
||||||
doAssert joinPath("usr", "") == unixToNativePath"usr/"
|
doAssert joinPath("usr", "") == unixToNativePath"usr/"
|
||||||
doAssert joinPath("", "lib") == "lib"
|
doAssert joinPath("", "lib") == "lib"
|
||||||
doAssert joinPath("", "/lib") == unixToNativePath"/lib"
|
doAssert joinPath("", "/lib") == unixToNativePath"/lib"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue