os.nim: use the new pathnorm.normalizePath implementation

This commit is contained in:
Araq 2018-12-14 08:57:55 +01:00
commit ce9815bcf5
4 changed files with 67 additions and 85 deletions

View file

@ -111,6 +111,9 @@ proc enumToString*(enums: openArray[enum]): string =
(default value: true) that can be set to `false` for better Posix (default value: true) that can be set to `false` for better Posix
interoperability. (Bug #9619.) interoperability. (Bug #9619.)
- `os.joinPath` and `os.normalizePath` handle edge cases like ``"a/b/../../.."``
differently.
### Language additions ### Language additions

View file

@ -984,36 +984,37 @@ proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [], noNimScr
## ##
## Warning: URL-encoded and Unicode attempts at directory traversal are not detected. ## Warning: URL-encoded and Unicode attempts at directory traversal are not detected.
## Triple dot is not handled. ## Triple dot is not handled.
let isAbs = isAbsolute(path) path = pathnorm.normalizePath(path)
var stack: seq[string] = @[] when false:
for p in split(path, {DirSep}): let isAbs = isAbsolute(path)
case p var stack: seq[string] = @[]
of "", ".": for p in split(path, {DirSep}):
continue case p
of "..": of "", ".":
if stack.len == 0: continue
if isAbs: of "..":
discard # collapse all double dots on absoluta paths if stack.len == 0:
else: if isAbs:
discard # collapse all double dots on absoluta paths
else:
stack.add(p)
elif stack[^1] == "..":
stack.add(p) stack.add(p)
elif stack[^1] == "..": else:
stack.add(p) discard stack.pop()
else: else:
discard stack.pop() stack.add(p)
else:
stack.add(p)
if isAbs: if isAbs:
path = DirSep & join(stack, $DirSep) path = DirSep & join(stack, $DirSep)
elif stack.len > 0: elif stack.len > 0:
path = join(stack, $DirSep) path = join(stack, $DirSep)
else: else:
path = "." path = "."
proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [], noNimScript.} = proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [], noNimScript.} =
## Returns a normalized path for the current OS. See `<#normalizePath>`_ ## Returns a normalized path for the current OS. See `<#normalizePath>`_
result = path result = pathnorm.normalizePath(path)
normalizePath(result)
when defined(Windows) and not defined(nimscript): when defined(Windows) and not defined(nimscript):
proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle = proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle =

View file

@ -66,19 +66,26 @@ proc addNormalizePath*(x: string; result: var string; state: var int; dirSep = D
if (state shr 1 == 0) and isSlash(x, b): if (state shr 1 == 0) and isSlash(x, b):
result.add dirSep result.add dirSep
state = state or 1 state = state or 1
elif result.len > (state and 1) and isDotDot(x, b): elif isDotDot(x, b):
var d = result.len if (state shr 1) >= 1:
# f/.. var d = result.len
while (d-1) > (state and 1) and result[d-1] notin {DirSep, AltSep}: # f/..
dec d while (d-1) > (state and 1) and result[d-1] notin {DirSep, AltSep}:
if d > 0: setLen(result, d-1) dec d
if d > 0:
setLen(result, d-1)
dec state, 2
else:
if result.len > 0 and result[^1] notin {DirSep, AltSep}:
result.add dirSep
result.add substr(x, b[0], b[1])
elif isDot(x, b): elif isDot(x, b):
discard "discard the dot" discard "discard the dot"
elif b[1] >= b[0]: elif b[1] >= b[0]:
if result.len > 0 and result[^1] notin {DirSep, AltSep}: if result.len > 0 and result[^1] notin {DirSep, AltSep}:
result.add dirSep result.add dirSep
result.add substr(x, b[0], b[1]) result.add substr(x, b[0], b[1])
inc state, 2 inc state, 2
proc normalizePath*(path: string; dirSep = DirSep): string = proc normalizePath*(path: string; dirSep = DirSep): string =
## Example: ## Example:

View file

@ -190,61 +190,32 @@ block walkDirRec:
removeDir("walkdir_test") removeDir("walkdir_test")
block normalizedPath: block normalizedPath:
when defined(posix): block relative:
block relative: doAssert normalizedPath(".") == ""
doAssert normalizedPath(".") == "." doAssert normalizedPath("..") == ".."
doAssert normalizedPath("..") == ".." doAssert normalizedPath("../") == ".."
doAssert normalizedPath("../") == ".." doAssert normalizedPath("../..") == unixToNativePath"../.."
doAssert normalizedPath("../..") == "../.." doAssert normalizedPath("../a/..") == ".."
doAssert normalizedPath("../a/..") == ".." doAssert normalizedPath("../a/../") == ".."
doAssert normalizedPath("../a/../") == ".." doAssert normalizedPath("./") == ""
doAssert normalizedPath("./") == "."
block absolute: block absolute:
doAssert normalizedPath("/") == "/" doAssert normalizedPath("/") == unixToNativePath"/"
doAssert normalizedPath("/.") == "/" doAssert normalizedPath("/.") == unixToNativePath"/"
doAssert normalizedPath("/..") == "/" doAssert normalizedPath("/..") == unixToNativePath"/.."
doAssert normalizedPath("/../") == "/" doAssert normalizedPath("/../") == unixToNativePath"/.."
doAssert normalizedPath("/../..") == "/" doAssert normalizedPath("/../..") == unixToNativePath"/../.."
doAssert normalizedPath("/../../") == "/" doAssert normalizedPath("/../../") == unixToNativePath"/../.."
doAssert normalizedPath("/../../../") == "/" doAssert normalizedPath("/../../../") == unixToNativePath"/../../.."
doAssert normalizedPath("/a/b/../../foo") == "/foo" doAssert normalizedPath("/a/b/../../foo") == unixToNativePath"/foo"
doAssert normalizedPath("/a/b/../../../foo") == "/foo" doAssert normalizedPath("/a/b/../../../foo") == unixToNativePath"/../foo"
doAssert normalizedPath("/./") == "/" doAssert normalizedPath("/./") == unixToNativePath"/"
doAssert normalizedPath("//") == "/" doAssert normalizedPath("//") == unixToNativePath"/"
doAssert normalizedPath("///") == "/" doAssert normalizedPath("///") == unixToNativePath"/"
doAssert normalizedPath("/a//b") == "/a/b" doAssert normalizedPath("/a//b") == unixToNativePath"/a/b"
doAssert normalizedPath("/a///b") == "/a/b" doAssert normalizedPath("/a///b") == unixToNativePath"/a/b"
doAssert normalizedPath("/a/b/c/..") == "/a/b" doAssert normalizedPath("/a/b/c/..") == unixToNativePath"/a/b"
doAssert normalizedPath("/a/b/c/../") == "/a/b" doAssert normalizedPath("/a/b/c/../") == unixToNativePath"/a/b"
else:
block relative:
doAssert normalizedPath(".") == "."
doAssert normalizedPath("..") == ".."
doAssert normalizedPath("..\\") == ".."
doAssert normalizedPath("..\\..") == "..\\.."
doAssert normalizedPath("..\\a\\..") == ".."
doAssert normalizedPath("..\\a\\..\\") == ".."
doAssert normalizedPath(".\\") == "."
block absolute:
doAssert normalizedPath("\\") == "\\"
doAssert normalizedPath("\\.") == "\\"
doAssert normalizedPath("\\..") == "\\"
doAssert normalizedPath("\\..\\") == "\\"
doAssert normalizedPath("\\..\\..") == "\\"
doAssert normalizedPath("\\..\\..\\") == "\\"
doAssert normalizedPath("\\..\\..\\..\\") == "\\"
doAssert normalizedPath("\\a\\b\\..\\..\\foo") == "\\foo"
doAssert normalizedPath("\\a\\b\\..\\..\\..\\foo") == "\\foo"
doAssert normalizedPath("\\.\\") == "\\"
doAssert normalizedPath("\\\\") == "\\"
doAssert normalizedPath("\\\\\\") == "\\"
doAssert normalizedPath("\\a\\\\b") == "\\a\\b"
doAssert normalizedPath("\\a\\\\\\b") == "\\a\\b"
doAssert normalizedPath("\\a\\b\\c\\..") == "\\a\\b"
doAssert normalizedPath("\\a\\b\\c\\..\\") == "\\a\\b"
block isHidden: block isHidden:
when defined(posix): when defined(posix):