include system/inclrtl import std/[oserrors] when defined(nimPreviewSlimSystem): import std/[syncio, assertions, widestrs] ## .. importdoc:: osdirs.nim, os.nim 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): import winlean, times elif defined(posix): import posix proc c_rename(oldname, newname: cstring): cint {. importc: "rename", header: "".} else: {.error: "OS module not ported to your operating system!".} when weirdTarget: {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".} else: {.pragma: noWeirdTarget.} when defined(nimscript): # for procs already defined in scriptconfig.nim template noNimJs(body): untyped = discard elif defined(js): {.pragma: noNimJs, error: "this proc is not available on the js target".} else: {.pragma: noNimJs.} when defined(windows) and not weirdTarget: template wrapUnary*(varname, winApiProc, arg: untyped) = var varname = winApiProc(newWideCString(arg)) template wrapBinary*(varname, winApiProc, arg, arg2: untyped) = var varname = winApiProc(newWideCString(arg), arg2) proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle = result = findFirstFileW(newWideCString(a), b) template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b) template getFilename*(f: untyped): untyped = $cast[WideCString](addr(f.cFileName[0])) proc skipFindData*(f: WIN32_FIND_DATA): bool {.inline.} = # Note - takes advantage of null delimiter in the cstring const dot = ord('.') result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or f.cFileName[1].int == dot and f.cFileName[2].int == 0) type PathComponent* = enum ## Enumeration specifying a path component. ## ## See also: ## * `walkDirRec iterator`_ ## * `FileInfo object`_ pcFile, ## path refers to a file pcLinkToFile, ## path refers to a symbolic link to a file pcDir, ## path refers to a directory pcLinkToDir ## path refers to a symbolic link to a directory when defined(posix) and not weirdTarget: proc getSymlinkFileKind*(path: string): tuple[pc: PathComponent, isSpecial: bool] = # Helper function. var s: Stat assert(path != "") result = (pcLinkToFile, false) if stat(path, s) == 0'i32: if S_ISDIR(s.st_mode): result = (pcLinkToDir, false) elif not S_ISREG(s.st_mode): result = (pcLinkToFile, true) proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} = ## Moves a file (or directory if `isDir` is true) from `source` to `dest`. ## ## Returns false in case of `EXDEV` error or `AccessDeniedError` on Windows (if `isDir` is true). ## In case of other errors `OSError` is raised. ## Returns true in case of success. when defined(windows): let s = newWideCString(source) let d = newWideCString(dest) result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 else: result = c_rename(source, dest) == 0'i32 if not result: let err = osLastError() let isAccessDeniedError = when defined(windows): const AccessDeniedError = OSErrorCode(5) isDir and err == AccessDeniedError else: err == EXDEV.OSErrorCode if not isAccessDeniedError: raiseOSError(err, $(source, dest)) when not defined(windows): const maxSymlinkLen* = 1024 proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], noNimJs.} = ## Returns true if `filename` exists and is a regular file or symlink. ## ## Directories, device files, named pipes and sockets return false. ## ## See also: ## * `dirExists proc`_ ## * `symlinkExists proc`_ when defined(windows): wrapUnary(a, getFileAttributesW, filename) if a != -1'i32: result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32 else: var res: Stat return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], noNimJs.} = ## Returns true if the directory `dir` exists. If `dir` is a file, false ## is returned. Follows symlinks. ## ## See also: ## * `fileExists proc`_ ## * `symlinkExists proc`_ when defined(windows): wrapUnary(a, getFileAttributesW, dir) if a != -1'i32: result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 else: var res: Stat result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], noWeirdTarget.} = ## Returns true if the symlink `link` exists. Will return true ## regardless of whether the link points to a directory or file. ## ## See also: ## * `fileExists proc`_ ## * `dirExists proc`_ when defined(windows): wrapUnary(a, getFileAttributesW, link) if a != -1'i32: # xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK` # may also be needed. result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32 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 result = createFileW( newWideCString(path), access, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, flags, 0 )