diff --git a/changelog.md b/changelog.md index 34ecd66a093de..2b41a097e7e55 100644 --- a/changelog.md +++ b/changelog.md @@ -53,6 +53,10 @@ - Added `os.normalizePathEnd` for additional path sanitization. +- Added `checkError` parameter to `os.walkPattern`, `os.walkFiles`, + `os.walkDirs`, `os.walkDir` and `os.walkDirRec`. If it is true, raises + `OSError` in case of an error. + ## Library changes - `asyncdispatch.drain` now properly takes into account `selector.hasPendingOperations` diff --git a/lib/pure/os.nim b/lib/pure/os.nim index a329d232a65f3..c2ec5adbb8ca4 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1809,7 +1809,7 @@ template defaultWalkFilter(item): bool = ## files and directories true -template walkCommon(pattern: string, filter) = +template walkCommon(pattern: string, filter: typed, checkError: bool) = ## Common code for getting the files and directories with the ## specified `pattern` when defined(windows): @@ -1833,6 +1833,8 @@ template walkCommon(pattern: string, filter) = let errCode = getLastError() if errCode == ERROR_NO_MORE_FILES: break else: raiseOSError(errCode.OSErrorCode) + elif checkError: + raiseOSError(osLastError(), pattern) else: # here we use glob var f: Glob @@ -1840,7 +1842,7 @@ template walkCommon(pattern: string, filter) = f.gl_offs = 0 f.gl_pathc = 0 f.gl_pathv = nil - res = glob(pattern, 0, nil, addr(f)) + res = glob(pattern, if checkError: GLOB_ERR else: 0, nil, addr(f)) defer: globfree(addr(f)) if res == 0: for i in 0.. f.gl_pathc - 1: @@ -1848,48 +1850,62 @@ template walkCommon(pattern: string, filter) = let path = $f.gl_pathv[i] if filter(path): yield path + elif checkError: + raiseOSError(osLastError(), pattern) -iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} = +iterator walkPattern*(pattern: string; checkError = false): string {. + tags: [ReadDirEffect], noNimScript.} = ## 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. ## + ## In case of an error, raises `OSError` if `checkError` is true, + ## otherwise the error is ignored and yields nothing. + ## ## See also: ## * `walkFiles iterator <#walkFiles.i,string>`_ ## * `walkDirs iterator <#walkDirs.i,string>`_ ## * `walkDir iterator <#walkDir.i,string>`_ ## * `walkDirRec iterator <#walkDirRec.i,string>`_ - walkCommon(pattern, defaultWalkFilter) + walkCommon(pattern, defaultWalkFilter, checkError) -iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} = +iterator walkFiles*(pattern: string; checkError = false): string {. + tags: [ReadDirEffect], noNimScript.} = ## 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. ## + ## In case of an error, raises `OSError` if `checkError` is true, + ## otherwise the error is ignored and yields nothing. + ## ## See also: ## * `walkPattern iterator <#walkPattern.i,string>`_ ## * `walkDirs iterator <#walkDirs.i,string>`_ ## * `walkDir iterator <#walkDir.i,string>`_ ## * `walkDirRec iterator <#walkDirRec.i,string>`_ - walkCommon(pattern, isFile) + walkCommon(pattern, isFile, checkError) -iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} = +iterator walkDirs*(pattern: string; checkError = false): string {. + tags: [ReadDirEffect], noNimScript.} = ## 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. ## + ## In case of an error, raises `OSError` if `checkError` is true, + ## otherwise the error is ignored and yields nothing. + ## ## See also: ## * `walkPattern iterator <#walkPattern.i,string>`_ ## * `walkFiles iterator <#walkFiles.i,string>`_ ## * `walkDir iterator <#walkDir.i,string>`_ ## * `walkDirRec iterator <#walkDirRec.i,string>`_ - walkCommon(pattern, isDir) + walkCommon(pattern, isDir, checkError) proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", tags: [ReadDirEffect], noNimScript.} = @@ -1973,7 +1989,9 @@ proc staticWalkDir(dir: string; relative: bool): seq[ tuple[kind: PathComponent, path: string]] = discard -iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: string] {. +iterator walkDir*(dir: string; + relative=false; + checkError=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. @@ -1998,6 +2016,9 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: ## dirA/fileA1.txt ## dirA/fileA2.txt ## + ## When `dir` cannot be open, raises `OSError` if `checkError` is true, + ## otherwise the error is ignored and yields nothing. + ## ## See also: ## * `walkPattern iterator <#walkPattern.i,string>`_ ## * `walkFiles iterator <#walkFiles.i,string>`_ @@ -2030,6 +2051,8 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: let errCode = getLastError() if errCode == ERROR_NO_MORE_FILES: break else: raiseOSError(errCode.OSErrorCode) + elif checkError: + raiseOSError(osLastError(), dir) else: var d = opendir(dir) if d != nil: @@ -2064,10 +2087,13 @@ iterator walkDir*(dir: string; relative=false): tuple[kind: PathComponent, path: elif S_ISLNK(s.st_mode): k = getSymlinkFileKind(path) yield (k, y) + elif checkError: + raiseOSError(osLastError(), dir) iterator walkDirRec*(dir: string, yieldFilter = {pcFile}, followFilter = {pcDir}, - relative = false): string {.tags: [ReadDirEffect].} = + relative = false, + checkError = false): string {.tags: [ReadDirEffect].} = ## Recursively walks over the directory `dir` and yields for each file ## or directory in `dir`. ## @@ -2096,6 +2122,9 @@ iterator walkDirRec*(dir: string, ## ``pcLinkToDir`` follow symbolic links to directories ## --------------------- --------------------------------------------- ## + ## When `dir` or any directory in `dir` cannot be open, raises `OSError` + ## if `checkError` is true, otherwise the error is ignored and yield nothing + ## from the directory. ## ## See also: ## * `walkPattern iterator <#walkPattern.i,string>`_ @@ -2106,7 +2135,7 @@ iterator walkDirRec*(dir: string, var stack = @[""] while stack.len > 0: let d = stack.pop() - for k, p in walkDir(dir / d, relative = true): + for k, p in walkDir(dir / d, relative = true, checkError): let rel = d / p if k in {pcDir, pcLinkToDir} and k in followFilter: stack.add rel