123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- include system/inclrtl
- import std/[oserrors]
- when defined(nimPreviewSlimSystem):
- import std/[syncio, assertions, widestrs]
- 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: "<stdio.h>".}
- 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:
- when useWinUnicode:
- 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]))
- else:
- template findFirstFile*(a, b: untyped): untyped = findFirstFileA(a, b)
- template findNextFile*(a, b: untyped): untyped = findNextFileA(a, b)
- template getFilename*(f: untyped): untyped = $cast[cstring](addr f.cFileName)
- 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):
- when useWinUnicode:
- let s = newWideCString(source)
- let d = newWideCString(dest)
- result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
- else:
- result = moveFileExA(source, dest, 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):
- when useWinUnicode:
- wrapUnary(a, getFileAttributesW, filename)
- else:
- var a = getFileAttributesA(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):
- when useWinUnicode:
- wrapUnary(a, getFileAttributesW, dir)
- else:
- var a = getFileAttributesA(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):
- when useWinUnicode:
- wrapUnary(a, getFileAttributesW, link)
- else:
- var a = getFileAttributesA(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
- 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
- )
|