summary refs log tree commit diff stats
path: root/lib/std/private/oscommon.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std/private/oscommon.nim')
-rw-r--r--lib/std/private/oscommon.nim186
1 files changed, 186 insertions, 0 deletions
diff --git a/lib/std/private/oscommon.nim b/lib/std/private/oscommon.nim
new file mode 100644
index 000000000..c49d52ef2
--- /dev/null
+++ b/lib/std/private/oscommon.nim
@@ -0,0 +1,186 @@
+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 std/[winlean, times]
+elif defined(posix):
+  import std/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:
+  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, sideEffect.} =
+  ## 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, sideEffect.} =
+  ## 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, sideEffect.} =
+  ## 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
+      )