oscommon.nim 6.2 KB

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