[ospaths] simplify getConfigDir and introduce normalizePathEnd to make (#8680)

sure path endings are normalized with 0 or 1 trailing sep, taking care
of edge cases
This commit is contained in:
Timothee Cour
2018-08-26 08:36:11 -07:00
committed by Andreas Rumpf
parent 96d44fdd0a
commit b4edfa613b

View File

@@ -449,6 +449,31 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
elif defined(posix):
result = path[0] == '/'
proc normalizePathEnd(path: var string, trailingSep = false) =
## ensures ``path`` has exactly 0 or 1 trailing `DirSep`, depending on
## ``trailingSep``, and taking care of edge cases: it preservers whether
## a path is absolute or relative, and makes sure trailing sep is `DirSep`,
## not `AltSep`.
if path.len == 0: return
var i = path.len
while i >= 1 and path[i-1] in {DirSep, AltSep}: dec(i)
if trailingSep:
# foo// => foo
path.setLen(i)
# foo => foo/
path.add DirSep
elif i>0:
# foo// => foo
path.setLen(i)
else:
# // => / (empty case was already taken care of)
path = $DirSep
proc normalizePathEnd(path: string, trailingSep = false): string =
result = path
result.normalizePathEnd(trailingSep)
proc unixToNativePath*(path: string, drive=""): string {.
noSideEffect, rtl, extern: "nos$1".} =
## Converts an UNIX-like path to a native one.
@@ -530,12 +555,12 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1",
## "~/.config/", otherwise.
##
## An OS-dependent trailing slash is always present at the end of the
## returned string; `\\` on Windows and `/` on all other OSs.
## returned string; `\` on Windows and `/` on all other OSs.
when defined(windows):
result = string(getEnv("APPDATA")) & "\\"
result = getEnv("APPDATA").string
else:
result = string(getEnv("XDG_CONFIG_HOME", "")) & "/"
if result == "/": result = string(getEnv("HOME")) & "/.config/"
result = getEnv("XDG_CONFIG_HOME", getEnv("HOME").string / ".config").string
result.normalizePathEnd(trailingSep = true)
proc getTempDir*(): string {.rtl, extern: "nos$1",
tags: [ReadEnvEffect, ReadIOEffect].} =
@@ -649,3 +674,24 @@ when isMainModule:
when defined(posix):
assert quoteShell("") == "''"
block normalizePathEndTest:
# handle edge cases correctly: shouldn't affect whether path is
# absolute/relative
doAssert "".normalizePathEnd(true) == ""
doAssert "".normalizePathEnd(false) == ""
doAssert "/".normalizePathEnd(true) == $DirSep
doAssert "/".normalizePathEnd(false) == $DirSep
when defined(posix):
doAssert "//".normalizePathEnd(false) == "/"
doAssert "foo.bar//".normalizePathEnd == "foo.bar"
doAssert "bar//".normalizePathEnd(trailingSep = true) == "bar/"
when defined(Windows):
doAssert r"C:\foo\\".normalizePathEnd == r"C:\foo"
doAssert r"C:\foo".normalizePathEnd(trailingSep = true) == r"C:\foo\"
# this one is controversial: we could argue for returning `D:\` instead,
# but this is simplest.
doAssert r"D:\".normalizePathEnd == r"D:"
doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\"
doAssert "/".normalizePathEnd == r"\"