diff options
author | ringabout <43030857+ringabout@users.noreply.github.com> | 2022-10-25 17:56:11 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-25 17:56:11 +0800 |
commit | 69eaa4f14cdb1276650141adb6b2e96f478e0856 (patch) | |
tree | ce7bf7046f52abfac2b1ddfddcf6b570674d4a63 /lib/std | |
parent | daf35c6d1b4bc1377bf278aa265dab30c9f5867e (diff) | |
download | Nim-69eaa4f14cdb1276650141adb6b2e96f478e0856.tar.gz |
clean up `std/os` related modules (#20651)
* clean up `std/os` related modules * use `cmpPaths` * reset * cleanup
Diffstat (limited to 'lib/std')
-rw-r--r-- | lib/std/dirs.nim | 55 | ||||
-rw-r--r-- | lib/std/paths.nim | 55 | ||||
-rw-r--r-- | lib/std/private/oscommon.nim | 30 | ||||
-rw-r--r-- | lib/std/private/osdirs.nim | 18 | ||||
-rw-r--r-- | lib/std/private/ospaths2.nim | 74 |
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 |