Implement Unix file regularity check (#20448) (#20628)

* Implement Unix file regularity check

* update std/dirs also
This commit is contained in:
Andrey Makarov 2022-10-25 08:42:47 +03:00 • committed by GitHub
commit 8ed2431db0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 107 additions and 45 deletions

View file

@ -77,14 +77,17 @@ type
when defined(posix) and not weirdTarget:
proc getSymlinkFileKind*(path: string): PathComponent =
proc getSymlinkFileKind*(path: string):
tuple[pc: PathComponent, isRegular: bool] =
# Helper function.
var s: Stat
assert(path != "")
if stat(path, s) == 0'i32 and S_ISDIR(s.st_mode):
result = pcLinkToDir
else:
result = pcLinkToFile
result = (pcLinkToFile, true)
if stat(path, s) == 0'i32:
if S_ISDIR(s.st_mode):
result = (pcLinkToDir, true)
elif not S_ISREG(s.st_mode):
result = (pcLinkToFile, false)
proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} =
## Moves a file (or directory if `isDir` is true) from `source` to `dest`.

View file

@ -154,16 +154,21 @@ proc staticWalkDir(dir: string; relative: bool): seq[
tuple[kind: PathComponent, path: string]] =
discard
iterator walkDir*(dir: string; relative = false, checkDir = false):
iterator walkDir*(dir: string; relative = false, checkDir = false,
onlyRegular = false):
tuple[kind: PathComponent, path: string] {.tags: [ReadDirEffect].} =
## Walks over the directory `dir` and yields for each directory or file in
## `dir`. The component type and full path for each item are returned.
##
## Walking is not recursive. If ``relative`` is true (default: false)
## the resulting path is shortened to be relative to ``dir``.
##
## If `checkDir` is true, `OSError` is raised when `dir`
## doesn't exist.
## Walking is not recursive.
## * If `relative` is true (default: false)
## the resulting path is shortened to be relative to ``dir``,
## otherwise the full path is returned.
## * If `checkDir` is true, `OSError` is raised when `dir`
## doesn't exist.
## * If `onlyRegular` is true, then (besides all directories) only *regular*
## files (**without** special "file" objects like FIFOs, device files,
## etc) will be yielded on Unix.
##
## **Example:**
##
@ -234,24 +239,30 @@ iterator walkDir*(dir: string; relative = false, checkDir = false):
y = path
var k = pcFile
template resolveSymlink() =
var isRegular: bool
(k, isRegular) = getSymlinkFileKind(path)
if onlyRegular and not isRegular: continue
template kSetGeneric() = # pure Posix component `k` resolution
if lstat(path.cstring, s) < 0'i32: continue # don't yield
elif S_ISDIR(s.st_mode):
k = pcDir
elif S_ISLNK(s.st_mode):
k = getSymlinkFileKind(path)
resolveSymlink()
elif onlyRegular and not S_ISREG(s.st_mode): continue
when defined(linux) or defined(macosx) or
defined(bsd) or defined(genode) or defined(nintendoswitch):
case x.d_type
of DT_DIR: k = pcDir
of DT_LNK:
if dirExists(path): k = pcLinkToDir
else: k = pcLinkToFile
resolveSymlink()
of DT_UNKNOWN:
kSetGeneric()
else: # e.g. DT_REG etc
discard # leave it as pcFile
else: # DT_REG or special "files" like FIFOs
if onlyRegular and x.d_type != DT_REG: continue
else: discard # leave it as pcFile
else: # assuming that field `d_type` is not present
kSetGeneric()
@ -259,15 +270,13 @@ iterator walkDir*(dir: string; relative = false, checkDir = false):
iterator walkDirRec*(dir: string,
yieldFilter = {pcFile}, followFilter = {pcDir},
relative = false, checkDir = false): string {.tags: [ReadDirEffect].} =
relative = false, checkDir = false, onlyRegular = false):
string {.tags: [ReadDirEffect].} =
## Recursively walks over the directory `dir` and yields for each file
## or directory in `dir`.
##
## If ``relative`` is true (default: false) the resulting path is
## shortened to be relative to ``dir``, otherwise the full path is returned.
##
## If `checkDir` is true, `OSError` is raised when `dir`
## doesn't exist.
## Options `relative`, `checkdir`, `onlyRegular` are explained in
## [walkDir iterator] description.
##
## .. warning:: Modifying the directory structure while the iterator
## is traversing may result in undefined behavior!
@ -301,7 +310,8 @@ iterator walkDirRec*(dir: string,
var checkDir = checkDir
while stack.len > 0:
let d = stack.pop()
for k, p in walkDir(dir / d, relative = true, checkDir = checkDir):
for k, p in walkDir(dir / d, relative = true, checkDir = checkDir,
onlyRegular = onlyRegular):
let rel = d / p
if k in {pcDir, pcLinkToDir} and k in followFilter:
stack.add rel