diff options
author | Timothee Cour <timothee.cour2@gmail.com> | 2020-04-21 14:53:55 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-21 23:53:55 +0200 |
commit | 7ce0358351d8e241463e744d75e78c4cd810d19f (patch) | |
tree | 7ee05b7135d445d4fa87e3ea10c19306e6fdd507 /lib/pure | |
parent | dd24004fabaf315463b89d38ac553b20080fd1cd (diff) | |
download | Nim-7ce0358351d8e241463e744d75e78c4cd810d19f.tar.gz |
fix #13222: make relativePath more robust and flexible (#13451)
* * relativePath(foo) now works * relativePath(rel, abs) and relativePath(abs, rel) now work (fixes #13222) * relativePath, absolutePath, getCurrentDir now available in more targets (eg: vm, nodejs etc) * fix bug: isAbsolutePath now works with -d:js; add tests * workaround https://github.com/nim-lang/Nim/issues/13469 * remove `relativePath(path)` overload for now * add back changelog after rebase
Diffstat (limited to 'lib/pure')
-rw-r--r-- | lib/pure/os.nim | 158 |
1 files changed, 90 insertions, 68 deletions
diff --git a/lib/pure/os.nim b/lib/pure/os.nim index e1e5f31b4..2c3e91be8 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -98,6 +98,8 @@ type include "includes/osseps" +proc absolutePathInternal(path: string): string {.gcsafe.} + 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 @@ -297,8 +299,12 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raise result = path[0] != ':' elif defined(RISCOS): result = path[0] == '$' - elif defined(posix): + elif defined(posix) or defined(js): + # `or defined(js)` wouldn't be needed pending https://github.com/nim-lang/Nim/issues/13469 + # This works around the problem for posix, but windows is still broken with nim js -d:nodejs result = path[0] == '/' + else: + doAssert false # if ever hits here, adapt as needed when FileSystemCaseSensitive: template `!=?`(a, b: char): bool = a != b @@ -360,8 +366,8 @@ when doslikeFileSystem: else: return false -proc relativePath*(path, base: string; sep = DirSep): string {. - noSideEffect, rtl, extern: "nos$1", raises: [].} = +proc relativePath*(path, base: string, sep = DirSep): string {. + rtl, extern: "nos$1".} = ## Converts `path` to a path relative to `base`. ## ## The `sep` (default: `DirSep <#DirSep>`_) is used for the path normalizations, @@ -390,6 +396,12 @@ proc relativePath*(path, base: string; sep = DirSep): string {. var path = path path.normalizePathAux base.normalizePathAux + let a1 = isAbsolute(path) + let a2 = isAbsolute(base) + if a1 and not a2: + base = absolutePathInternal(base) + elif a2 and not a1: + path = absolutePathInternal(path) when doslikeFileSystem: if isAbsolute(path) and isAbsolute(base): @@ -1293,60 +1305,67 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noNimScript.} = else: result = getLastModificationTime(a) > getLastModificationTime(b) -proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [], noNimScript.} = - ## Returns the `current working directory`:idx: i.e. where the built - ## binary is run. - ## - ## So the path returned by this proc is determined at run time. - ## - ## See also: - ## * `getHomeDir proc <#getHomeDir>`_ - ## * `getConfigDir proc <#getConfigDir>`_ - ## * `getTempDir proc <#getTempDir>`_ - ## * `setCurrentDir proc <#setCurrentDir,string>`_ - ## * `currentSourcePath template <system.html#currentSourcePath.t>`_ - ## * `getProjectPath proc <macros.html#getProjectPath>`_ - when defined(windows): - var bufsize = MAX_PATH.int32 - when useWinUnicode: - var res = newWideCString("", bufsize) - while true: - var L = getCurrentDirectoryW(bufsize, res) - if L == 0'i32: - raiseOSError(osLastError()) - elif L > bufsize: - res = newWideCString("", L) - bufsize = L - else: - result = res$L - break +when not defined(nimscript): + proc getCurrentDir*(): string {.rtl, extern: "nos$1", tags: [].} = + ## Returns the `current working directory`:idx: i.e. where the built + ## binary is run. + ## + ## So the path returned by this proc is determined at run time. + ## + ## See also: + ## * `getHomeDir proc <#getHomeDir>`_ + ## * `getConfigDir proc <#getConfigDir>`_ + ## * `getTempDir proc <#getTempDir>`_ + ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `currentSourcePath template <system.html#currentSourcePath.t>`_ + ## * `getProjectPath proc <macros.html#getProjectPath>`_ + when defined(nodejs): + var ret: cstring + {.emit: "`ret` = process.cwd();".} + return $ret + elif defined(js): + doAssert false, "use -d:nodejs to have `getCurrentDir` defined" + elif defined(windows): + var bufsize = MAX_PATH.int32 + when useWinUnicode: + var res = newWideCString("", bufsize) + while true: + var L = getCurrentDirectoryW(bufsize, res) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + res = newWideCString("", L) + bufsize = L + else: + result = res$L + break + else: + result = newString(bufsize) + while true: + var L = getCurrentDirectoryA(bufsize, result) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + result = newString(L) + bufsize = L + else: + setLen(result, L) + break else: + var bufsize = 1024 # should be enough result = newString(bufsize) while true: - var L = getCurrentDirectoryA(bufsize, result) - if L == 0'i32: - raiseOSError(osLastError()) - elif L > bufsize: - result = newString(L) - bufsize = L - else: - setLen(result, L) + if getcwd(result, bufsize) != nil: + setLen(result, c_strlen(result)) break - else: - var bufsize = 1024 # should be enough - result = newString(bufsize) - while true: - if getcwd(result, bufsize) != nil: - setLen(result, c_strlen(result)) - break - else: - let err = osLastError() - if err.int32 == ERANGE: - bufsize = bufsize shl 1 - doAssert(bufsize >= 0) - result = newString(bufsize) else: - raiseOSError(osLastError()) + let err = osLastError() + if err.int32 == ERANGE: + bufsize = bufsize shl 1 + doAssert(bufsize >= 0) + result = newString(bufsize) + else: + raiseOSError(osLastError()) proc setCurrentDir*(newDir: string) {.inline, tags: [], noNimScript.} = ## Sets the `current working directory`:idx:; `OSError` @@ -1366,23 +1385,26 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [], noNimScript.} = else: if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir) -when not weirdTarget: - proc absolutePath*(path: string, root = getCurrentDir()): string {.noNimScript.} = - ## Returns the absolute path of `path`, rooted at `root` (which must be absolute; - ## default: current directory). - ## If `path` is absolute, return it, ignoring `root`. - ## - ## See also: - ## * `normalizedPath proc <#normalizedPath,string>`_ - ## * `normalizePath proc <#normalizePath,string>`_ - runnableExamples: - assert absolutePath("a") == getCurrentDir() / "a" - if isAbsolute(path): path - else: - if not root.isAbsolute: - raise newException(ValueError, "The specified root is not absolute: " & root) - joinPath(root, path) +proc absolutePath*(path: string, root = getCurrentDir()): string = + ## Returns the absolute path of `path`, rooted at `root` (which must be absolute; + ## default: current directory). + ## If `path` is absolute, return it, ignoring `root`. + ## + ## See also: + ## * `normalizedPath proc <#normalizedPath,string>`_ + ## * `normalizePath proc <#normalizePath,string>`_ + runnableExamples: + assert absolutePath("a") == getCurrentDir() / "a" + + if isAbsolute(path): path + else: + if not root.isAbsolute: + raise newException(ValueError, "The specified root is not absolute: " & root) + joinPath(root, path) + +proc absolutePathInternal(path: string): string = + absolutePath(path, getCurrentDir()) proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} = ## Normalize a path. |