oscommon.nim 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. include system/inclrtl
  2. import std/[oserrors]
  3. when defined(nimPreviewSlimSystem):
  4. import std/[syncio, assertions, widestrs]
  5. from std/staticos import PathComponent
  6. ## .. importdoc:: osdirs.nim, os.nim
  7. const
  8. weirdTarget* = defined(nimscript) or defined(js)
  9. supportedSystem* = weirdTarget or defined(windows) or defined(posix)
  10. type
  11. ReadDirEffect* = object of ReadIOEffect ## Effect that denotes a read
  12. ## operation from the directory
  13. ## structure.
  14. WriteDirEffect* = object of WriteIOEffect ## Effect that denotes a write
  15. ## operation to
  16. ## the directory structure.
  17. when weirdTarget:
  18. discard
  19. elif defined(windows):
  20. import std/[winlean, times]
  21. elif defined(posix):
  22. import std/posix
  23. proc c_rename(oldname, newname: cstring): cint {.
  24. importc: "rename", header: "<stdio.h>".}
  25. when weirdTarget:
  26. {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".}
  27. else:
  28. {.pragma: noWeirdTarget.}
  29. when defined(nimscript):
  30. # for procs already defined in scriptconfig.nim
  31. template noNimJs(body): untyped = discard
  32. elif defined(js):
  33. {.pragma: noNimJs, error: "this proc is not available on the js target".}
  34. else:
  35. {.pragma: noNimJs.}
  36. when defined(windows) and not weirdTarget:
  37. template wrapUnary*(varname, winApiProc, arg: untyped) =
  38. var varname = winApiProc(newWideCString(arg))
  39. template wrapBinary*(varname, winApiProc, arg, arg2: untyped) =
  40. var varname = winApiProc(newWideCString(arg), arg2)
  41. proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle =
  42. result = findFirstFileW(newWideCString(a), b)
  43. template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b)
  44. template getFilename*(f: untyped): untyped =
  45. $cast[WideCString](addr(f.cFileName[0]))
  46. proc skipFindData*(f: WIN32_FIND_DATA): bool {.inline.} =
  47. # Note - takes advantage of null delimiter in the cstring
  48. const dot = ord('.')
  49. result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or
  50. f.cFileName[1].int == dot and f.cFileName[2].int == 0)
  51. when supportedSystem:
  52. when defined(posix) and not weirdTarget:
  53. proc getSymlinkFileKind*(path: string):
  54. tuple[pc: PathComponent, isSpecial: bool] =
  55. # Helper function.
  56. var s: Stat
  57. assert(path != "")
  58. result = (pcLinkToFile, false)
  59. if stat(path, s) == 0'i32:
  60. if S_ISDIR(s.st_mode):
  61. result = (pcLinkToDir, false)
  62. elif not S_ISREG(s.st_mode):
  63. result = (pcLinkToFile, true)
  64. proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} =
  65. ## Moves a file (or directory if `isDir` is true) from `source` to `dest`.
  66. ##
  67. ## Returns false in case of `EXDEV` error or `AccessDeniedError` on Windows (if `isDir` is true).
  68. ## In case of other errors `OSError` is raised.
  69. ## Returns true in case of success.
  70. when defined(windows):
  71. let s = newWideCString(source)
  72. let d = newWideCString(dest)
  73. result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32
  74. else:
  75. result = c_rename(source, dest) == 0'i32
  76. if not result:
  77. let err = osLastError()
  78. let isAccessDeniedError =
  79. when defined(windows):
  80. const AccessDeniedError = OSErrorCode(5)
  81. isDir and err == AccessDeniedError
  82. else:
  83. err == EXDEV.OSErrorCode
  84. if not isAccessDeniedError:
  85. raiseOSError(err, $(source, dest))
  86. when not defined(windows):
  87. const maxSymlinkLen* = 1024
  88. proc fileExists*(filename: string): bool {.rtl, extern: "nos$1",
  89. tags: [ReadDirEffect], noNimJs, sideEffect.} =
  90. ## Returns true if `filename` exists and is a regular file or symlink.
  91. ##
  92. ## Directories, device files, named pipes and sockets return false.
  93. ##
  94. ## See also:
  95. ## * `dirExists proc`_
  96. ## * `symlinkExists proc`_
  97. when defined(windows):
  98. wrapUnary(a, getFileAttributesW, filename)
  99. if a != -1'i32:
  100. result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32
  101. else:
  102. var res: Stat
  103. return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode)
  104. proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect],
  105. noNimJs, sideEffect.} =
  106. ## Returns true if the directory `dir` exists. If `dir` is a file, false
  107. ## is returned. Follows symlinks.
  108. ##
  109. ## See also:
  110. ## * `fileExists proc`_
  111. ## * `symlinkExists proc`_
  112. when defined(windows):
  113. wrapUnary(a, getFileAttributesW, dir)
  114. if a != -1'i32:
  115. result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32
  116. else:
  117. var res: Stat
  118. result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode)
  119. proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1",
  120. tags: [ReadDirEffect],
  121. noWeirdTarget, sideEffect.} =
  122. ## Returns true if the symlink `link` exists. Will return true
  123. ## regardless of whether the link points to a directory or file.
  124. ##
  125. ## See also:
  126. ## * `fileExists proc`_
  127. ## * `dirExists proc`_
  128. when defined(windows):
  129. wrapUnary(a, getFileAttributesW, link)
  130. if a != -1'i32:
  131. # xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK`
  132. # may also be needed.
  133. result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32
  134. else:
  135. var res: Stat
  136. result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode)
  137. when defined(windows) and not weirdTarget:
  138. proc openHandle*(path: string, followSymlink=true, writeAccess=false): Handle =
  139. var flags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL
  140. if not followSymlink:
  141. flags = flags or FILE_FLAG_OPEN_REPARSE_POINT
  142. let access = if writeAccess: GENERIC_WRITE else: 0'i32
  143. result = createFileW(
  144. newWideCString(path), access,
  145. FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE,
  146. nil, OPEN_EXISTING, flags, 0
  147. )