☰

# std/private/ospaths2

Theme:

- Manual
- Standard library
- Index
- Compiler docs
- Fusion docs
- devel, stable

Search:
Source Edit

# Imports

since, strutils, pathnorm, oserrors, oscommon, syncio, assertions, widestrs, posix, ansi_c, osseps

# Procs

    proc `/`(head, tail: string): string {.noSideEffect, inline, ...raises: [],
                                           tags: [], forbids: [].}

  The same as joinPath(head, tail) proc.

  See also:

  - /../ proc
  - joinPath(head, tail) proc
  - joinPath(parts: varargs[string]) proc
  - splitPath proc
  - uri.combine proc
  - uri./ proc

  Example:

      when defined(posix):
        assert "usr" / "" == "usr"
        assert "" / "lib" == "lib"
        assert "" / "/lib" == "/lib"
        assert "usr/" / "/lib/" == "usr/lib/"
        assert "usr" / "lib" / "../bin" == "usr/bin"

  Source Edit

    proc `/../`(head, tail: string): string {.noSideEffect, ...raises: [], tags: [],
        forbids: [].}

  The same as `parentDir(head) / tail`, unless there is no parent directory. Then `head / tail` is performed instead.

  See also:

  - / proc
  - parentDir proc

  Example:

      when defined(posix):
        assert "a/b/c" /../ "d/e" == "a/b/d/e"
        assert "a" /../ "d/e" == "a/d/e"

  Source Edit

    proc absolutePath(path: string; root = when supportedSystem:
      getCurrentDir()
    else:
      ""): string {....raises: [ValueError], tags: [], forbids: [].}

  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
  - normalizePath proc

  Example:

      assert absolutePath("a") == getCurrentDir() / "a"

  Source Edit

    proc addFileExt(filename, ext: string): string {.noSideEffect, ...gcsafe,
        extern: "nos$1", raises: [], tags: [], forbids: [].}

  Adds the file extension `ext` to `filename`, unless `filename` already has an extension.

  `Ext` should be given without the leading `'.'`, because some filesystems may use a different character. (Although I know of none such beast.)

  See also:

  - searchExtPos proc
  - splitFile proc
  - extractFilename proc
  - lastPathPart proc
  - changeFileExt proc

  Example:

      assert addFileExt("foo.bar", "baz") == "foo.bar"
      assert addFileExt("foo.bar", "") == "foo.bar"
      assert addFileExt("foo", "baz") == "foo.baz"

  Source Edit

    proc changeFileExt(filename, ext: string): string {.noSideEffect, ...gcsafe,
        extern: "nos$1", raises: [], tags: [], forbids: [].}

  Changes the file extension to `ext`.

  If the `filename` has no extension, `ext` will be added. If `ext` == "" then any extension is removed.

  `Ext` should be given without the leading `'.'`, because some filesystems may use a different character. (Although I know of none such beast.)

  See also:

  - searchExtPos proc
  - splitFile proc
  - extractFilename proc
  - lastPathPart proc
  - addFileExt proc

  Example:

      assert changeFileExt("foo.bar", "baz") == "foo.baz"
      assert changeFileExt("foo.bar", "") == "foo"
      assert changeFileExt("foo", "baz") == "foo.baz"

  Source Edit

    proc cmpPaths(pathA, pathB: string): int {.noSideEffect, ...gcsafe,
        extern: "nos$1", raises: [], tags: [], forbids: [].}

  Compares two paths.

  On a case-sensitive filesystem this is done case-sensitively otherwise case-insensitively. Returns:

  `0` if pathA == pathB
  `< 0` if pathA < pathB
  `> 0` if pathA > pathB

  Example:

      when defined(macosx):
        assert cmpPaths("foo", "Foo") == 0
      elif defined(posix):
        assert cmpPaths("foo", "Foo") > 0

  Source Edit

    proc extractFilename(path: string): string {.noSideEffect, ...gcsafe,
        extern: "nos$1", raises: [], tags: [], forbids: [].}

  Extracts the filename of a given `path`.

  This is the same as `name & ext` from splitFile(path) proc.

  See also:

  - searchExtPos proc
  - splitFile proc
  - lastPathPart proc
  - changeFileExt proc
  - addFileExt proc

  Example:

      assert extractFilename("foo/bar/") == ""
      assert extractFilename("foo/bar") == "bar"
      assert extractFilename("foo/bar.baz") == "bar.baz"

  Source Edit

    proc getCurrentDir(): string {....gcsafe, extern: "nos$1", tags: [],
                                   raises: [OSError], forbids: [].}

  Returns the current working directory i.e. where the built binary is run.

  So the path returned by this proc is determined at run time.

  See also:

  - osappdirs: getHomeDir proc
  - osappdirs: getConfigDir proc
  - osappdirs: getTempDir proc
  - osdirs: setCurrentDir proc
  - currentSourcePath template
  - getProjectPath proc
  Source Edit

    proc isAbsolute(path: string): bool {....gcsafe, noSideEffect, ...extern: "nos$1",
                                          raises: [], tags: [], forbids: [].}

  Checks whether a given `path` is absolute.

  On Windows, network paths are considered absolute too.

  Example:

      assert not "".isAbsolute
      assert not ".".isAbsolute
      when defined(posix):
        assert "/".isAbsolute
        assert not "a/".isAbsolute
        assert "/a/".isAbsolute

  Source Edit

    proc isRelativeTo(path: string; base: string): bool {.
        ...raises: [ValueError, OSError], tags: [RootEffect], forbids: [].}

  Returns true if `path` is relative to `base`.

  Example:

      doAssert isRelativeTo("./foo//bar", "foo")
      doAssert isRelativeTo("foo/bar", ".")
      doAssert isRelativeTo("/foo/bar.nim", "/foo/bar.nim")
      doAssert not isRelativeTo("foo/bar.nims", "foo/bar.nim")

  Source Edit

    proc isRootDir(path: string): bool {.noSideEffect, ...gcsafe, extern: "nos$1",
                                         raises: [], tags: [], forbids: [].}

  Checks whether a given `path` is a root directory.

  Example:

      assert isRootDir("")
      assert isRootDir(".")
      assert isRootDir("/")
      assert isRootDir("a")
      assert not isRootDir("/a")
      assert not isRootDir("a/b/c")

  Source Edit

    proc joinPath(head, tail: string): string {.noSideEffect, ...gcsafe,
        extern: "nos$1", raises: [], tags: [], forbids: [].}

  Joins two directory names to one.

  returns normalized path concatenation of `head` and `tail`, preserving whether or not `tail` has a trailing slash (or, if tail if empty, whether head has one).

  See also:

  - joinPath(parts: varargs[string]) proc
  - / proc
  - splitPath proc
  - uri.combine proc
  - uri./ proc

  Example:

      when defined(posix):
        assert joinPath("usr", "lib") == "usr/lib"
        assert joinPath("usr", "lib/") == "usr/lib/"
        assert joinPath("usr", "") == "usr"
        assert joinPath("usr/", "") == "usr/"
        assert joinPath("", "") == ""
        assert joinPath("", "lib") == "lib"
        assert joinPath("", "/lib") == "/lib"
        assert joinPath("usr/", "/lib") == "usr/lib"
        assert joinPath("usr/lib", "../bin") == "usr/bin"

  Source Edit

    proc joinPath(parts: varargs[string]): string {.noSideEffect, ...gcsafe,
        extern: "nos$1OpenArray", raises: [], tags: [], forbids: [].}

  The same as joinPath(head, tail) proc, but works with any number of directory parts.

  You need to pass at least one element or the proc will assert in debug builds and crash on release builds.

  See also:

  - joinPath(head, tail) proc
  - / proc
  - /../ proc
  - splitPath proc

  Example:

      when defined(posix):
        assert joinPath("a") == "a"
        assert joinPath("a", "b", "c") == "a/b/c"
        assert joinPath("usr/lib", "../../var", "log") == "var/log"

  Source Edit

    proc lastPathPart(path: string): string {.noSideEffect, ...gcsafe, extern: "nos$1",
        raises: [], tags: [], forbids: [].}

  Like extractFilename proc, but ignores trailing dir separator; aka: baseName in some other languages.

  See also:

  - searchExtPos proc
  - splitFile proc
  - extractFilename proc
  - changeFileExt proc
  - addFileExt proc

  Example:

      assert lastPathPart("foo/bar/") == "bar"
      assert lastPathPart("foo/bar") == "bar"

  Source Edit

    proc normalizedPath(path: string): string {....gcsafe, extern: "nos$1", tags: [],
        raises: [], forbids: [].}

  Returns a normalized path for the current OS.

  See also:

  - absolutePath proc
  - normalizePath proc for the in-place version

  Example:

      when defined(posix):
        assert normalizedPath("a///b//..//c///d") == "a/c/d"

  Source Edit

    proc normalizeExe(file: var string) {....raises: [], tags: [], forbids: [].}

  on posix, prepends `./` if `file` doesn't contain `/` and is not `"", ".", ".."`.

  Example:

      import std/sugar
      when defined(posix):
        doAssert "foo".dup(normalizeExe) == "./foo"
        doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar"
      doAssert "".dup(normalizeExe) == ""

  Source Edit

    proc normalizePath(path: var string) {....gcsafe, extern: "nos$1", tags: [],
                                           raises: [], forbids: [].}

  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.

  See also:

  - absolutePath proc
  - normalizedPath proc for outplace version
  - normalizeExe proc

  Example:

      when defined(posix):
        var a = "a///b//..//c///d"
        a.normalizePath()
        assert a == "a/c/d"

  Source Edit

    proc normalizePathEnd(path: string; trailingSep = false): string {....raises: [],
        tags: [], forbids: [].}

  outplace overload

  Example:

      when defined(posix):
        assert normalizePathEnd("/lib//.//", trailingSep = true) == "/lib/"
        assert normalizePathEnd("lib/./.", trailingSep = false) == "lib"
        assert normalizePathEnd(".//./.", trailingSep = false) == "."
        assert normalizePathEnd("", trailingSep = true) == "" # not / !
        assert normalizePathEnd("/", trailingSep = false) == "/" # not "" !

  Source Edit

    proc normalizePathEnd(path: var string; trailingSep = false) {....raises: [],
        tags: [], forbids: [].}

  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`. Trailing `/.` are compressed, see examples. Source Edit

    proc parentDir(path: string): string {.noSideEffect, ...gcsafe, extern: "nos$1",
                                           raises: [], tags: [], forbids: [].}

  Returns the parent directory of `path`.

  This is similar to `splitPath(path).head` when `path` doesn't end in a dir separator, but also takes care of path normalizations. The remainder can be obtained with lastPathPart(path) proc.

  See also:

  - relativePath proc
  - splitPath proc
  - tailDir proc
  - parentDirs iterator

  Example:

      assert parentDir("") == ""
      when defined(posix):
        assert parentDir("/usr/local/bin") == "/usr/local"
        assert parentDir("foo/bar//") == "foo"
        assert parentDir("//foo//bar//.") == "/foo"
        assert parentDir("./foo") == "."
        assert parentDir("/./foo//./") == "/"
        assert parentDir("a//./") == "."
        assert parentDir("a/b/c/..") == "a"

  Source Edit

    proc relativePath(path, base: string; sep = DirSep): string {....gcsafe,
        extern: "nos$1", raises: [ValueError, OSError], tags: [RootEffect],
        forbids: [].}

  Converts `path` to a path relative to `base`.

  The `sep` (default: osseps: DirSep) is used for the path normalizations, this can be useful to ensure the relative path only contains `'/'` so that it can be used for URL constructions.

  On Windows, if a root of `path` and a root of `base` are different, returns `path` as is because it is impossible to make a relative path. That means an absolute path can be returned.

  See also:

  - splitPath proc
  - parentDir proc
  - tailDir proc

  Example:

      assert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim"
      assert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim"
      when not doslikeFileSystem: # On Windows, UNC-paths start with `//`
        assert relativePath("/Users///me/bar//z.nim", "//Users/", '/') == "me/bar/z.nim"
      assert relativePath("/Users/me/bar/z.nim", "/Users/me", '/') == "bar/z.nim"
      assert relativePath("", "/users/moo", '/') == ""
      assert relativePath("foo", ".", '/') == "foo"
      assert relativePath("foo", "foo", '/') == "."

  Source Edit

    proc sameFile(path1, path2: string): bool {....gcsafe, extern: "nos$1",
        tags: [ReadDirEffect], raises: [OSError], forbids: [].}

  Returns true if both pathname arguments refer to the same physical file or directory.

  Raises `OSError` if any of the files does not exist or information about it can not be obtained.

  This proc will return true if given two alternative hard-linked or sym-linked paths to the same file or directory.

  See also:

  - os: sameFileContent proc
  Source Edit

    proc searchExtPos(path: string): int {....raises: [], tags: [], forbids: [].}

  Returns index of the `'.'` char in `path` if it signifies the beginning of the file extension. Returns -1 otherwise.

  See also:

  - splitFile proc
  - extractFilename proc
  - lastPathPart proc
  - changeFileExt proc
  - addFileExt proc

  Example:

      assert searchExtPos("a/b/c") == -1
      assert searchExtPos("c.nim") == 1
      assert searchExtPos("a/b/c.nim") == 5
      assert searchExtPos("a.b.c.nim") == 5
      assert searchExtPos(".nim") == -1
      assert searchExtPos("..nim") == -1
      assert searchExtPos("a..nim") == 2

  Source Edit

    proc splitFile(path: string): tuple[dir, name, ext: string] {.noSideEffect,
        ...gcsafe, extern: "nos$1", raises: [], tags: [], forbids: [].}

  Splits a filename into `(dir, name, extension)` tuple.

  `dir` does not end in osseps: DirSep unless it's `/`. `extension` includes the leading dot.

  If `path` has no extension, `ext` is the empty string. If `path` has no directory component, `dir` is the empty string. If `path` has no filename component, `name` and `ext` are empty strings.

  See also:

  - searchExtPos proc
  - extractFilename proc
  - lastPathPart proc
  - changeFileExt proc
  - addFileExt proc

  Example:

      var (dir, name, ext) = splitFile("usr/local/nimc.html")
      assert dir == "usr/local"
      assert name == "nimc"
      assert ext == ".html"
      (dir, name, ext) = splitFile("/usr/local/os")
      assert dir == "/usr/local"
      assert name == "os"
      assert ext == ""
      (dir, name, ext) = splitFile("/usr/local/")
      assert dir == "/usr/local"
      assert name == ""
      assert ext == ""
      (dir, name, ext) = splitFile("/tmp.txt")
      assert dir == "/"
      assert name == "tmp"
      assert ext == ".txt"

  Source Edit

    proc splitPath(path: string): tuple[head, tail: string] {.noSideEffect, ...gcsafe,
        extern: "nos$1", raises: [], tags: [], forbids: [].}

  Splits a directory into `(head, tail)` tuple, so that `head / tail == path` (except for edge cases like "/usr").

  See also:

  - joinPath(head, tail) proc
  - joinPath(parts: varargs[string]) proc
  - / proc
  - /../ proc
  - relativePath proc

  Example:

      assert splitPath("usr/local/bin") == ("usr/local", "bin")
      assert splitPath("usr/local/bin/") == ("usr/local/bin", "")
      assert splitPath("/bin/") == ("/bin", "")
      when (NimMajor, NimMinor) <= (1, 0):
        assert splitPath("/bin") == ("", "bin")
      else:
        assert splitPath("/bin") == ("/", "bin")
      assert splitPath("bin") == ("", "bin")
      assert splitPath("") == ("", "")

  Source Edit

    proc tailDir(path: string): string {.noSideEffect, ...gcsafe, extern: "nos$1",
                                         raises: [], tags: [], forbids: [].}

  Returns the tail part of `path`.

  See also:

  - relativePath proc
  - splitPath proc
  - parentDir proc

  Example:

      assert tailDir("/bin") == "bin"
      assert tailDir("bin") == ""
      assert tailDir("bin/") == ""
      assert tailDir("/usr/local/bin") == "usr/local/bin"
      assert tailDir("//usr//local//bin//") == "usr//local//bin//"
      assert tailDir("./usr/local/bin") == "usr/local/bin"
      assert tailDir("usr/local/bin") == "local/bin"

  Source Edit

    proc unixToNativePath(path: string; drive = ""): string {.noSideEffect, ...gcsafe,
        extern: "nos$1", raises: [], tags: [], forbids: [].}

  Converts an UNIX-like path to a native one.

  On an UNIX system this does nothing. Else it converts `'/'`, `'.'`, `'..'` to the appropriate things.

  On systems with a concept of "drives", `drive` is used to determine which drive label to use during absolute path conversion. `drive` defaults to the drive of the current working directory, and is ignored on systems that do not have a concept of "drives".

  Source Edit

# Iterators

    iterator parentDirs(path: string; fromRoot = false; inclusive = true): string {.
        ...raises: [], tags: [], forbids: [].}

  Walks over all parent directories of a given `path`.

  If `fromRoot` is true (default: false), the traversal will start from the file system root directory. If `inclusive` is true (default), the original argument will be included in the traversal.

  Relative paths won't be expanded by this iterator. Instead, it will traverse only the directories appearing in the relative path.

  See also:

  - parentDir proc

  Example:

      let g = "a/b/c"

      for p in g.parentDirs:
        echo p
        # a/b/c
        # a/b
        # a

      for p in g.parentDirs(fromRoot=true):
        echo p
        # a/
        # a/b/
        # a/b/c

      for p in g.parentDirs(inclusive=false):
        echo p
        # a/b
        # a

  Source Edit

# Exports

ReadDirEffect, WriteDirEffect, ExtSep, FileSystemCaseSensitive, DynlibFormat, DirSep, AltSep, PathSep, ScriptExt, doslikeFileSystem, ExeExt, CurDir, ParDir