summary refs log tree commit diff stats
path: root/lib/std/private/oscommon.nim
blob: dd66d137e50d8aa79d600518c6b31113bd10359d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
include system/inclrtl

import ospaths2
import std/[oserrors]

when defined(nimPreviewSlimSystem):
  import std/[syncio, assertions, widestrs]

const weirdTarget* = defined(nimscript) or defined(js)


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 getCommandLine*(): untyped = getCommandLineW()

    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 getCommandLine*(): untyped = getCommandLineA()

    template getFilename*(f: untyped): untyped = $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): PathComponent =
    # Helper function.
    var s: Stat
    assert(path != "")
    if stat(path, s) == 0'i32 and S_ISDIR(s.st_mode):
      result = pcLinkToDir
    else:
      result = pcLinkToFile

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)