diff options
author | Dominik Picheta <dominikpicheta@googlemail.com> | 2018-07-07 10:12:07 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-07 10:12:07 +0100 |
commit | 53ce58f05024743cd1dc347b2e0cd0147bdbdb1d (patch) | |
tree | f6ebc7ebb75a3c1ae3650b715ca2e2dc635485fc | |
parent | 65942449912097a3bf781152d0c1db3a52855e69 (diff) | |
parent | d65429d857f927b6110217611feb94b18ee7f2a4 (diff) | |
download | Nim-53ce58f05024743cd1dc347b2e0cd0147bdbdb1d.tar.gz |
Merge pull request #6587 from FedericoCeratto/normalizePath
Add normalizePath and tests
-rw-r--r-- | lib/pure/os.nim | 45 | ||||
-rw-r--r-- | tests/stdlib/tos.nim | 62 |
2 files changed, 105 insertions, 2 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 599745176..6cf5e1fb7 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -298,8 +298,8 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [].} = proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", tags: [ReadDirEffect].} = - ## Returns the full (`absolute`:idx:) path of the file `filename`, - ## raises OSError in case of an error. + ## Returns the full (`absolute`:idx:) path of an existing file `filename`, + ## raises OSError in case of an error. Follows symlinks. when defined(windows): var bufsize = MAX_PATH.int32 when useWinUnicode: @@ -338,6 +338,47 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", result = $r c_free(cast[pointer](r)) +proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} = + ## Normalize a path. + ## + ## Consecutive directory separators are collapsed, including an initial double slash. + ## + ## On relative paths, double dot (..) sequences are collapsed if possible. + ## On absolute paths they are always collapsed. + ## + ## Warning: URL-encoded and Unicode attempts at directory traversal are not detected. + ## Triple dot is not handled. + let isAbs = isAbsolute(path) + var stack: seq[string] = @[] + for p in split(path, {DirSep}): + case p + of "", ".": + continue + of "..": + if stack.len == 0: + if isAbs: + discard # collapse all double dots on absoluta paths + else: + stack.add(p) + elif stack[^1] == "..": + stack.add(p) + else: + discard stack.pop() + else: + stack.add(p) + + if isAbs: + path = DirSep & join(stack, $DirSep) + elif stack.len > 0: + path = join(stack, $DirSep) + else: + path = "." + +proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [].} = + ## Returns a normalized path for the current OS. See `<#normalizePath>`_ + result = path + normalizePath(result) + when defined(Windows): proc openHandle(path: string, followSymlink=true, writeAccess=false): Handle = var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index c7643b701..6ccc29bf6 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -43,6 +43,7 @@ true true true true + ''' """ # test os path creation, iteration, and deletion @@ -137,8 +138,69 @@ import times let tm = fromUnix(0) + 100.microseconds writeFile("a", "") setLastModificationTime("a", tm) + when defined(macosx): echo "true" else: echo getLastModificationTime("a") == tm removeFile("a") + +when defined(Linux) or defined(osx): + + block normalizedPath: + 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" + +else: + + block normalizedPath: + 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" |