Skip to content

Commit f1a5515

Browse files
authored
Hexer: important bugfixes (#1499)
1 parent 213b2be commit f1a5515

File tree

9 files changed

+308
-32
lines changed

9 files changed

+308
-32
lines changed

lib/std/dirs.nim

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
## This module implements directory operations for creating, removing,
2+
## and iterating over directories.
3+
##
4+
## **See also:**
5+
## * `paths module <paths.html>`_ for path handling
6+
## * `syncio module <syncio.html>`_ for file I/O
7+
8+
import std/[oserrors]
9+
import std/paths # separate import for better symbol resolution
10+
import private/oscommons
11+
12+
export PathComponent
13+
export paths.Path, paths.initPath, paths.`/`, paths.`$`
14+
15+
when defined(windows):
16+
import windows/winlean
17+
from widestrs import newWideCString, toWideCString
18+
19+
else:
20+
import posix/posix
21+
22+
when defined(windows):
23+
import "../../vendor/errorcodes/src" / errorcodes_windows
24+
else:
25+
import "../../vendor/errorcodes/src" / errorcodes_posix
26+
27+
var errno {.importc: "errno", header: "<errno.h>".}: cint
28+
29+
proc tryCreateFinalDir*(dir: Path): ErrorCode =
30+
## Tries to create the final directory in a path.
31+
## In other words, it tries to create a single new directory, not a nested one.
32+
## It returns the OS's error code making it easy to distinguish between
33+
## "could not create" and "already exists".
34+
var dirStr = $dir
35+
when defined(windows):
36+
if createDirectoryW(newWideCString(dirStr).rawData) != 0'i32:
37+
result = Success
38+
else:
39+
result = windowsToErrorCode getLastError()
40+
else:
41+
if mkdir(dirStr.toCString, 0o777) == 0'i32:
42+
result = Success
43+
else:
44+
result = posixToErrorCode(errno)
45+
46+
proc createDir*(dir: Path) {.raises.} =
47+
## Creates a new directory `dir`. If the directory already exists, no error is raised.
48+
## This can be used to create a nested directory structure directly.
49+
for d in parentDirs(dir, fromRoot=false, inclusive=true):
50+
let res = tryCreateFinalDir(d)
51+
if res == Success or res == NameExists:
52+
discard "fine"
53+
else:
54+
raise res
55+
56+
proc tryRemoveFinalDir*(dir: Path): ErrorCode =
57+
## Tries to remove the final directory in a path.
58+
## In other words, it tries to remove a single directory, not a nested one.
59+
## It returns the OS's error code making it easy to distinguish between
60+
## "could not remove" and "does not exist".
61+
var dirStr = $dir
62+
when defined(windows):
63+
if removeDirectoryW(newWideCString(dirStr).rawData) != 0'i32:
64+
result = Success
65+
else:
66+
result = windowsToErrorCode getLastError()
67+
else:
68+
if rmdir(dirStr.toCString) == 0'i32:
69+
result = Success
70+
else:
71+
result = posixToErrorCode(errno)
72+
73+
proc removeDir*(dir: Path) {.raises.} =
74+
## Removes the directory `dir`. If the directory does not exist, no error is raised.
75+
let res = tryRemoveFinalDir(dir)
76+
if res == Success or res == NameNotFound:
77+
discard "fine"
78+
else:
79+
raise res
80+
81+
proc tryRemoveFile*(file: Path): ErrorCode =
82+
var fileStr = $file
83+
when defined(windows):
84+
if deleteFileW(newWideCString(fileStr).rawData).isSuccess:
85+
result = Success
86+
else:
87+
result = windowsToErrorCode getLastError()
88+
else:
89+
if unlink(fileStr.toCString) == 0'i32:
90+
result = Success
91+
else:
92+
result = posixToErrorCode(errno)
93+
94+
proc removeFile*(file: Path) {.raises.} =
95+
## Removes the file `file`.
96+
## If the file does not exist, no error is raised.
97+
let res = tryRemoveFile(file)
98+
if res == Success or res == NameNotFound:
99+
discard "fine"
100+
else:
101+
raise res
102+
103+
#[
104+
iterator walkDir*(dir: Path,
105+
relative = false,
106+
checkDir = false): tuple[kind: PathComponent, path: Path] =
107+
## Walks over all entries in the directory `dir`.
108+
##
109+
## Yields tuples of `(kind, path)` where `kind` is one of:
110+
## * `pcFile` - regular file
111+
## * `pcDir` - directory
112+
## * `pcLinkToFile` - symbolic link to a file
113+
## * `pcLinkToDir` - symbolic link to a directory
114+
##
115+
## If `relative` is true, yields relative paths (just the filename/dirname),
116+
## otherwise yields full paths.
117+
##
118+
## If `checkDir` is true, raises an error if `dir` doesn't exist or isn't a directory.
119+
##
120+
## Special directories "." and ".." are skipped.
121+
##
122+
## See also:
123+
## * `walkDirRec iterator`_
124+
## * `dirExists proc <oscommons.html#dirExists,string>`_
125+
when defined(windows):
126+
var findData: WIN32_FIND_DATA
127+
var dirStr = $dir
128+
let searchPath = dirStr & (when defined(windows): '\\' else: '/') & "*"
129+
let handle = findFirstFile(searchPath, findData)
130+
if handle == INVALID_HANDLE_VALUE:
131+
if checkDir:
132+
raiseOSError(osLastError())
133+
else:
134+
defer:
135+
discard findClose(handle)
136+
137+
while true:
138+
if not skipFindData(findData):
139+
# Use same pattern as oscommons for getting filename
140+
var filename = ""
141+
var i = 0
142+
while findData.cFileName[i].int16 != 0'i16:
143+
filename.add char(findData.cFileName[i].int and 0xFF)
144+
inc i
145+
146+
let fullPath = if relative: initPath(filename) else: dir / initPath(filename)
147+
148+
let isDir = (findData.dwFileAttributes.uint32 and FILE_ATTRIBUTE_DIRECTORY) != 0'u32
149+
let isLink = (findData.dwFileAttributes.uint32 and FILE_ATTRIBUTE_REPARSE_POINT) != 0'u32
150+
151+
var kind: PathComponent
152+
if isLink:
153+
kind = if isDir: pcLinkToDir else: pcLinkToFile
154+
else:
155+
kind = if isDir: pcDir else: pcFile
156+
157+
yield (kind, fullPath)
158+
159+
if findNextFileW(handle, findData) == 0'i32:
160+
break
161+
162+
else: # POSIX
163+
var dirStr = $dir
164+
let d = opendir(dirStr.toCString)
165+
if d == nil:
166+
if checkDir:
167+
raiseOSError(osLastError())
168+
else:
169+
defer:
170+
discard closedir(d)
171+
172+
while true:
173+
let entry = readdir(d)
174+
if entry == nil:
175+
break
176+
177+
let name = $cast[cstring](addr entry.d_name[0])
178+
# Skip "." and ".."
179+
if name == "." or name == "..":
180+
continue
181+
182+
let fullPath = if relative:
183+
initPath(name)
184+
else:
185+
dir / initPath(name)
186+
187+
let fullPathStr = $fullPath
188+
# Determine the kind
189+
if symlinkExists(fullPathStr):
190+
let (pc, _) = getSymlinkFileKind(fullPathStr)
191+
yield (pc, fullPath)
192+
elif dirExists(fullPathStr):
193+
yield (pcDir, fullPath)
194+
elif fileExists(fullPathStr):
195+
yield (pcFile, fullPath)
196+
else:
197+
# Unknown type, treat as file
198+
yield (pcFile, fullPath)
199+
200+
]#
201+
202+
proc getCurrentDir*(): Path {.raises.} =
203+
## Returns the current working directory as a `Path`.
204+
##
205+
## Raises an error if unable to retrieve the current directory.
206+
##
207+
## See also:
208+
## * `setCurrentDir proc`_
209+
when defined(windows):
210+
const bufSize = 1024'i32
211+
var buffer = newWideCString("", bufSize)
212+
let res = getCurrentDirectoryW(bufSize, buffer.rawData)
213+
if res == 0'i32:
214+
raiseOSError(osLastError())
215+
result = paths.initPath($buffer)
216+
else:
217+
const bufSize = 1024
218+
var buffer = newString(bufSize)
219+
if getcwd(buffer.toCString(), bufSize) == nil:
220+
raiseOSError(osLastError())
221+
var i = 0
222+
while i < buffer.len and buffer[i] != '\0': inc i
223+
shrink(buffer, i)
224+
result = paths.initPath(buffer)
225+
226+
proc setCurrentDir*(dir: Path) {.raises.} =
227+
## Sets the current working directory to `dir`.
228+
##
229+
## Raises errors if the directory doesn't exist or lacks permissions.
230+
##
231+
## See also:
232+
## * `getCurrentDir proc`_
233+
var dirStr = $dir
234+
when defined(windows):
235+
if setCurrentDirectoryW(newWideCString(dirStr).rawData) == 0'i32:
236+
raiseOSError(osLastError())
237+
else:
238+
if chdir(dirStr.toCString) != 0'i32:
239+
raiseOSError(osLastError())

lib/std/posix/posix.nim

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,25 @@ when defined(posix):
106106
importc, header: "<time.h>", sideEffect.}
107107

108108
proc getcwd*(a1: cstring, a2: int): cstring {.importc, header: "<unistd.h>", sideEffect.}
109+
proc chdir*(path: cstring): cint {.importc, header: "<unistd.h>", sideEffect.}
109110

110111
when not defined(nintendoswitch):
111112
proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "<unistd.h>".}
112113

113114
proc symlink*(a1, a2: cstring): cint {.importc, header: "<unistd.h>".}
114115
else:
115116
proc symlink*(a1, a2: cstring): cint = -1
117+
118+
# Directory operations
119+
type
120+
DIR* {.importc: "DIR", header: "<dirent.h>".} = object
121+
Dirent* {.importc: "struct dirent", header: "<dirent.h>".} = object
122+
d_name* {.importc: "d_name".}: array[256, char]
123+
124+
proc opendir*(name: cstring): ptr DIR {.importc, header: "<dirent.h>", sideEffect.}
125+
proc closedir*(dirp: ptr DIR): cint {.importc, header: "<dirent.h>", sideEffect.}
126+
proc readdir*(dirp: ptr DIR): ptr Dirent {.importc, header: "<dirent.h>", sideEffect.}
127+
128+
proc mkdir*(path: cstring, mode: Mode): cint {.importc, header: "<sys/stat.h>", sideEffect.}
129+
proc rmdir*(path: cstring): cint {.importc, header: "<unistd.h>", sideEffect.}
130+
proc unlink*(path: cstring): cint {.importc, header: "<unistd.h>", sideEffect.}

lib/std/widestrs.nim

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ when not (defined(cpu16) or defined(cpu8)):
4040
proc toWideCString*(x: WideCStringObj): WideCString {.inline.} =
4141
result = x.data
4242

43+
proc rawData*(x: WideCStringObj): WideCString {.inline.} =
44+
result = x.data
45+
4346
proc ord(arg: Utf16Char): int = int(cast[uint16](arg))
4447

4548
proc len*(w: WideCString): int =

lib/std/windows/winlean.nim

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,8 @@ when defined(windows):
200200
proc findNextFileW*(hFindFile: Handle,
201201
lpFindFileData: var WIN32_FIND_DATA): int32 {.
202202
stdcall, dynlib: "kernel32", importc: "FindNextFileW", sideEffect.}
203+
proc findClose*(hFindFile: Handle): WINBOOL {.
204+
stdcall, dynlib: "kernel32", importc: "FindClose", sideEffect.}
203205

204206
proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString,
205207
bFailIfExists: WINBOOL): WINBOOL {.
@@ -227,7 +229,8 @@ when defined(windows):
227229
importc: "CreateDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
228230
proc removeDirectoryW*(lpPathName: WideCString): int32 {.
229231
importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall, sideEffect.}
230-
232+
proc deleteFileW*(lpFileName: WideCString): WINBOOL {.
233+
importc: "DeleteFileW", dynlib: "kernel32", stdcall, sideEffect.}
231234

232235
proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString,
233236
flags: DWORD): int32 {.

src/hexer/hexer_context.nim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import std / [tables, sets, syncio]
1111

1212
include nifprelude
13+
import lifter
1314
import ".." / nimony / [nimony_model, typenav, langmodes]
1415

1516
export RcField, DataField
@@ -45,6 +46,7 @@ type
4546

4647
localDeclCounters*: int
4748
activeChecks*: set[CheckMode]
49+
liftingCtx*: ref LiftingCtx
4850

4951
proc getTmpId*(e: var EContext): int {.inline.} =
5052
result = e.tmpId

src/hexer/lifter.nim

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ to type `(T, T)`, etc.
1717
import std/[assertions, tables]
1818

1919
include nifprelude
20-
import nifindexes, symparser, treemangler, hexer_context
20+
import nifindexes, symparser, treemangler
2121
import ".." / nimony / [nimony_model, decls, programs, typenav, expreval, xints, builtintypes, typekeys, typeprops]
2222

2323
type
@@ -710,7 +710,7 @@ proc getDestructor*(c: var LiftingCtx; typ: TypeCursor; info: PackedLineInfo): S
710710
when isMainModule:
711711
import std/os
712712
setupProgramForTesting getCurrentDir() / "nimcache", "test.nim", ".nif"
713-
let res = tryLoadHook(attachedDestroy, pool.syms.getOrIncl(StringName))
713+
let res = tryLoadHook(attachedDestroy, pool.syms.getOrIncl(StringName), false)
714714
if res != SymId(0):
715715
echo pool.syms[res]
716716
else:

0 commit comments

Comments
 (0)