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
181
182
183
184
185
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
)
|