oscommon.nim 7.0 KB

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