Skip to content

Commit

Permalink
Merge pull request #6587 from FedericoCeratto/normalizePath
Browse files Browse the repository at this point in the history
Add normalizePath and tests
  • Loading branch information
dom96 authored Jul 7, 2018
2 parents 6594244 + d65429d commit 53ce58f
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 2 deletions.
45 changes: 43 additions & 2 deletions lib/pure/os.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
62 changes: 62 additions & 0 deletions tests/stdlib/tos.nim
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ true
true
true
true
'''
"""
# test os path creation, iteration, and deletion
Expand Down Expand Up @@ -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"

0 comments on commit 53ce58f

Please sign in to comment.