summary refs log tree commit diff stats
path: root/lib/std
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/dirs.nim55
-rw-r--r--lib/std/paths.nim55
-rw-r--r--lib/std/private/oscommon.nim30
-rw-r--r--lib/std/private/osdirs.nim18
-rw-r--r--lib/std/private/ospaths2.nim74
5 files changed, 140 insertions, 92 deletions
diff --git a/lib/std/dirs.nim b/lib/std/dirs.nim
index 304075a6f..770ea9a01 100644
--- a/lib/std/dirs.nim
+++ b/lib/std/dirs.nim
@@ -1,7 +1,7 @@
 from paths import Path, ReadDirEffect, WriteDirEffect
 
 from std/private/osdirs import dirExists, createDir, existsOrCreateDir, removeDir,
-                               moveDir, walkPattern, walkFiles, walkDirs, walkDir,
+                               moveDir, walkDir, setCurrentDir,
                                walkDirRec, PathComponent
 
 export PathComponent
@@ -76,51 +76,6 @@ proc moveDir*(source, dest: Path) {.inline, tags: [ReadIOEffect, WriteIOEffect].
   ## * `createDir proc`_
   moveDir(source.string, dest.string)
 
-iterator walkPattern*(pattern: Path): Path {.tags: [ReadDirEffect].} =
-  ## Iterate over all the files and directories that match the `pattern`.
-  ##
-  ## On POSIX this uses the `glob`:idx: call.
-  ## `pattern` is OS dependent, but at least the `"*.ext"`
-  ## notation is supported.
-  ##
-  ## See also:
-  ## * `walkFiles iterator`_
-  ## * `walkDirs iterator`_
-  ## * `walkDir iterator`_
-  ## * `walkDirRec iterator`_
-  for p in walkPattern(pattern.string):
-    yield Path(p)
-
-iterator walkFiles*(pattern: Path): Path {.tags: [ReadDirEffect].} =
-  ## Iterate over all the files that match the `pattern`.
-  ##
-  ## On POSIX this uses the `glob`:idx: call.
-  ## `pattern` is OS dependent, but at least the `"*.ext"`
-  ## notation is supported.
-  ##
-  ## See also:
-  ## * `walkPattern iterator`_
-  ## * `walkDirs iterator`_
-  ## * `walkDir iterator`_
-  ## * `walkDirRec iterator`_
-  for p in walkFiles(pattern.string):
-    yield Path(p)
-
-iterator walkDirs*(pattern: Path): Path {.tags: [ReadDirEffect].} =
-  ## Iterate over all the directories that match the `pattern`.
-  ##
-  ## On POSIX this uses the `glob`:idx: call.
-  ## `pattern` is OS dependent, but at least the `"*.ext"`
-  ## notation is supported.
-  ##
-  ## See also:
-  ## * `walkPattern iterator`_
-  ## * `walkFiles iterator`_
-  ## * `walkDir iterator`_
-  ## * `walkDirRec iterator`_
-  for p in walkDirs(pattern.string):
-    yield Path(p)
-
 iterator walkDir*(dir: Path; relative = false, checkDir = false,
                  onlyRegular = false):
     tuple[kind: PathComponent, path: Path] {.tags: [ReadDirEffect].} =
@@ -179,3 +134,11 @@ iterator walkDirRec*(dir: Path,
   for p in walkDirRec(dir.string, yieldFilter, followFilter, relative,
                       checkDir, onlyRegular):
     yield Path(p)
+
+proc setCurrentDir*(newDir: Path) {.inline, tags: [].} =
+  ## Sets the `current working directory`:idx:; `OSError`
+  ## is raised if `newDir` cannot been set.
+  ##
+  ## See also:
+  ## * `getCurrentDir proc`_
+  osdirs.setCurrentDir(newDir.string)
diff --git a/lib/std/paths.nim b/lib/std/paths.nim
index b3cd7de83..37466587b 100644
--- a/lib/std/paths.nim
+++ b/lib/std/paths.nim
@@ -1,6 +1,9 @@
 import std/private/osseps
 export osseps
 
+import std/envvars
+import std/private/osappdirs
+
 import pathnorm
 
 from std/private/ospaths2 import  joinPath, splitPath,
@@ -17,6 +20,13 @@ export ReadDirEffect, WriteDirEffect
 type
   Path* = distinct string
 
+func `==`*(x, y: Path): bool {.inline.} =
+  ## Compares two paths.
+  ##
+  ## On a case-sensitive filesystem this is done
+  ## case-sensitively otherwise case-insensitively.
+  result = cmpPaths(x.string, y.string) == 0
+
 template endsWith(a: string, b: set[char]): bool =
   a.len > 0 and a[^1] in b
 
@@ -208,17 +218,6 @@ func addFileExt*(filename: Path, ext: string): Path {.inline.} =
   ## * `changeFileExt proc`_
   result = Path(addFileExt(filename.string, ext))
 
-func cmpPaths*(pathA, pathB: Path): int {.inline.} =
-  ## 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
-  result = cmpPaths(pathA.string, pathB.string)
-
 func unixToNativePath*(path: Path, drive=Path("")): Path {.inline.} =
   ## Converts an UNIX-like path to a native one.
   ##
@@ -246,14 +245,6 @@ proc getCurrentDir*(): Path {.inline, tags: [].} =
   ## * `getProjectPath proc <macros.html#getProjectPath>`_
   result = Path(ospaths2.getCurrentDir())
 
-proc setCurrentDir*(newDir: Path) {.inline, tags: [].} =
-  ## Sets the `current working directory`:idx:; `OSError`
-  ## is raised if `newDir` cannot been set.
-  ##
-  ## See also:
-  ## * `getCurrentDir proc`_
-  ospaths2.setCurrentDir(newDir.string)
-
 proc normalizeExe*(file: var Path) {.borrow.}
 
 proc normalizePath*(path: var Path) {.borrow.}
@@ -268,3 +259,29 @@ proc absolutePath*(path: Path, root = getCurrentDir()): Path =
   ## See also:
   ## * `normalizePath proc`_
   result = Path(absolutePath(path.string, root.string))
+
+proc expandTildeImpl(path: string): string {.
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  if len(path) == 0 or path[0] != '~':
+    result = path
+  elif len(path) == 1:
+    result = getHomeDir()
+  elif (path[1] in {DirSep, AltSep}):
+    result = joinPath(getHomeDir(), path.substr(2))
+  else:
+    # TODO: handle `~bob` and `~bob/` which means home of bob
+    result = path
+
+proc expandTilde*(path: Path): Path {.inline,
+  tags: [ReadEnvEffect, ReadIOEffect].} =
+  ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing
+  ## ``~`` with `getHomeDir()`_ (otherwise returns ``path`` unmodified).
+  ##
+  ## Windows: this is still supported despite the Windows platform not having this
+  ## convention; also, both ``~/`` and ``~\`` are handled.
+  runnableExamples:
+    import std/appdirs
+    assert expandTilde(Path("~") / Path("appname.cfg")) == getHomeDir() / Path("appname.cfg")
+    assert expandTilde(Path("~/foo/bar")) == getHomeDir() / Path("foo/bar")
+    assert expandTilde(Path("/foo/bar")) == Path("/foo/bar")
+  result = Path(expandTildeImpl(path.string))
diff --git a/lib/std/private/oscommon.nim b/lib/std/private/oscommon.nim
index dbed1ba96..8d0df3086 100644
--- a/lib/std/private/oscommon.nim
+++ b/lib/std/private/oscommon.nim
@@ -1,6 +1,5 @@
 include system/inclrtl
 
-import ospaths2
 import std/[oserrors]
 
 when defined(nimPreviewSlimSystem):
@@ -9,6 +8,15 @@ when defined(nimPreviewSlimSystem):
 const weirdTarget* = defined(nimscript) or defined(js)
 
 
+type
+  ReadDirEffect* = object of ReadIOEffect   ## Effect that denotes a read
+                                            ## operation from the directory
+                                            ## structure.
+  WriteDirEffect* = object of WriteIOEffect ## Effect that denotes a write
+                                            ## operation to
+                                            ## the directory structure.
+
+
 when weirdTarget:
   discard
 elif defined(windows):
@@ -181,3 +189,23 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
   else:
     var res: Stat
     result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
+
+when defined(windows) and not weirdTarget:
+  proc openHandle*(path: string, followSymlink=true, writeAccess=false): Handle =
+    var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
+    if not followSymlink:
+      flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
+    let access = if writeAccess: GENERIC_WRITE else: 0'i32
+
+    when useWinUnicode:
+      result = createFileW(
+        newWideCString(path), access,
+        FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
+        nil, OPEN_EXISTING, flags, 0
+        )
+    else:
+      result = createFileA(
+        path, access,
+        FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
+        nil, OPEN_EXISTING, flags, 0
+        )
diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim
index 2f98257e2..5c8d61159 100644
--- a/lib/std/private/osdirs.nim
+++ b/lib/std/private/osdirs.nim
@@ -548,3 +548,21 @@ proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWei
     # Fallback to copy & del
     copyDir(source, dest)
     removeDir(source)
+
+proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} =
+  ## Sets the `current working directory`:idx:; `OSError`
+  ## is raised if `newDir` cannot been set.
+  ##
+  ## See also:
+  ## * `getHomeDir proc`_
+  ## * `getConfigDir proc`_
+  ## * `getTempDir proc`_
+  ## * `getCurrentDir proc`_
+  when defined(windows):
+    when useWinUnicode:
+      if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
+        raiseOSError(osLastError(), newDir)
+    else:
+      if setCurrentDirectoryA(newDir) == 0'i32: raiseOSError(osLastError(), newDir)
+  else:
+    if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir)
diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim
index 8092549d8..75c34ecf5 100644
--- a/lib/std/private/ospaths2.nim
+++ b/lib/std/private/ospaths2.nim
@@ -4,6 +4,9 @@ import std/private/since
 import strutils, pathnorm
 import std/oserrors
 
+import oscommon
+export ReadDirEffect, WriteDirEffect
+
 when defined(nimPreviewSlimSystem):
   import std/[syncio, assertions, widestrs]
 
@@ -34,13 +37,6 @@ else:
 
 proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.}
 
-type
-  ReadDirEffect* = object of ReadIOEffect   ## Effect that denotes a read
-                                            ## operation from the directory
-                                            ## structure.
-  WriteDirEffect* = object of WriteIOEffect ## Effect that denotes a write
-                                            ## operation to
-                                            ## the directory structure.
 
 import std/private/osseps
 export osseps
@@ -891,25 +887,6 @@ when not defined(nimscript):
           else:
             raiseOSError(osLastError())
 
-proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} =
-  ## Sets the `current working directory`:idx:; `OSError`
-  ## is raised if `newDir` cannot been set.
-  ##
-  ## See also:
-  ## * `getHomeDir proc`_
-  ## * `getConfigDir proc`_
-  ## * `getTempDir proc`_
-  ## * `getCurrentDir proc`_
-  when defined(windows):
-    when useWinUnicode:
-      if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32:
-        raiseOSError(osLastError(), newDir)
-    else:
-      if setCurrentDirectoryA(newDir) == 0'i32: raiseOSError(osLastError(), newDir)
-  else:
-    if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir)
-
-
 proc absolutePath*(path: string, root = getCurrentDir()): string =
   ## Returns the absolute path of `path`, rooted at `root` (which must be absolute;
   ## default: current directory).
@@ -1004,3 +981,48 @@ proc normalizeExe*(file: var string) {.since: (1, 3, 5).} =
   when defined(posix):
     if file.len > 0 and DirSep notin file and file != "." and file != "..":
       file = "./" & file
+
+proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1",
+  tags: [ReadDirEffect], noWeirdTarget.} =
+  ## 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:
+  ## * `sameFileContent proc`_
+  when defined(windows):
+    var success = true
+    var f1 = openHandle(path1)
+    var f2 = openHandle(path2)
+
+    var lastErr: OSErrorCode
+    if f1 != INVALID_HANDLE_VALUE and f2 != INVALID_HANDLE_VALUE:
+      var fi1, fi2: BY_HANDLE_FILE_INFORMATION
+
+      if getFileInformationByHandle(f1, addr(fi1)) != 0 and
+         getFileInformationByHandle(f2, addr(fi2)) != 0:
+        result = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber and
+                 fi1.nFileIndexHigh == fi2.nFileIndexHigh and
+                 fi1.nFileIndexLow == fi2.nFileIndexLow
+      else:
+        lastErr = osLastError()
+        success = false
+    else:
+      lastErr = osLastError()
+      success = false
+
+    discard closeHandle(f1)
+    discard closeHandle(f2)
+
+    if not success: raiseOSError(lastErr, $(path1, path2))
+  else:
+    var a, b: Stat
+    if stat(path1, a) < 0'i32 or stat(path2, b) < 0'i32:
+      raiseOSError(osLastError(), $(path1, path2))
+    else:
+      result = a.st_dev == b.st_dev and a.st_ino == b.st_ino