-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path04_system.fth
333 lines (296 loc) · 7.86 KB
/
04_system.fth
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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
1024 constant |filebuf.data|
6 cells |filebuf.data| + constant |filebuf|
: filebuf.fid 0 cells + ;
: filebuf.prev 1 cells + ;
: filebuf.next 2 cells + ;
: filebuf.file? 3 cells + ;
: filebuf.head 4 cells + ;
: filebuf.len 5 cells + ;
: filebuf.data 6 cells + ;
\ create a "dummy" filebuf on the stack
create filebufs 3 cells allot \ only include header fields
-1 filebufs filebuf.fid !
filebufs filebufs filebuf.prev !
filebufs filebufs filebuf.next !
: filebuf-new ( fid file? addr -- )
tuck filebuf.file? !
tuck filebuf.fid !
dup filebuf.data over filebuf.head !
0 over filebuf.len !
\ link it into the list as the "prev" of the sentinel head
filebufs over filebuf.next !
filebufs filebuf.prev @ over filebuf.prev !
dup filebufs filebuf.prev !
dup filebuf.prev @ filebuf.next !
;
: filebuf-allot ( fid file? -- )
here
|filebuf| allot
filebuf-new
;
: filebuf-allocate ( fid file? -- err )
|filebuf| allocate ?dup
if nip nip
else filebuf-new 0
then
;
\ file 0 (stdin) is already open, so add a buffer for it
0 0 filebuf-allot
: filebuf-delete ( filebuf -- err )
\ link this filebuf's prev and next to each other
dup filebuf.next @ over filebuf.prev @ filebuf.next !
dup filebuf.prev @ over filebuf.next @ filebuf.prev !
free
;
: find-filebuf ( fid -- filebuf | false )
filebufs filebuf.next @
begin dup filebufs <>
while
2dup filebuf.fid @ =
if nip exit then
filebuf.next @
repeat
2drop false
;
create iovec 2 cells allot
: filebuf-refill? ( filebuf -- err )
dup >r
filebuf.len @
if r> drop 0 exit \ don't refill if the buffer has any data
then
r@ filebuf.data r@ filebuf.head ! \ reset the head
r@ filebuf.data iovec !
|filebuf.data| iovec 4 + !
r@ filebuf.fid @ iovec 1 r> filebuf.len fd-read \ actually read from the file
;
: filebuf-peek ( filebuf -- char|-1 )
dup filebuf.len @ =0
if drop -1
else filebuf.head @ c@
then
;
: filebuf-consume ( filebuf -- )
1 over filebuf.head +!
-1 swap filebuf.len +!
;
: filebuf-refill-if-file ( filebuf -- err )
dup filebuf.file? @
if filebuf-refill?
else drop 0
then
;
: is-cr? ( c -- ) 13 = ;
: is-lf? ( c -- ) 10 = ;
: is-term? ( c -- ? ) dup is-cr? swap is-lf? or ;
: filebuf-consume-term ( filebuf -- err )
>r
r@ filebuf-refill-if-file
?dup if r> drop exit then
r@ filebuf-peek
dup is-term? if r@ filebuf-consume then
is-lf? if r> drop 0 exit then
\ if we saw an \r, try consuming one more \n
r@ filebuf-refill-if-file
?dup if r> drop exit then
r@ filebuf-peek is-lf?
if r> filebuf-consume 0
else r> drop 0
then
;
\ options bitmask
1 constant fd-allow-read
2 constant fd-allow-write
4 constant fd-create
: fd-oflags ( options -- oflags )
fd-allow-write fd-create or and
if 9 \ creat | trunc
else 0
then
;
: fd-rights ( options -- drights )
>r
0
r@ fd-allow-read and
if 1 or \ fd-read
then
r> fd-allow-write and
if 64 or \ fd-write
then
0
;
fd-allow-read constant r/o
fd-allow-write constant w/o
46 constant relative-path-char
47 constant separator-char
create namelengthbuf 2 cells allot
: namelength ( -- u ) namelengthbuf cell + @ ;
\ is this path a child of the parent?
: is-parent-directory? ( path-addr path-u dir-addr dir-u -- ? )
rot over <= \ if the path length is <= the path length, it can't be a parent
if 2drop 2drop false exit
then
begin ?dup
while
-rot
over c@ over c@ <>
if drop 2drop false exit
then
1+ -rot 1+ -rot 1-
repeat
2drop true
;
: normalize-directory-name ( c-addr u -- c-addr u )
relative-path-char remove-start
separator-char remove-start
1- \ remove null terminator
;
variable parent-fd
variable parent-namelength
: get-preopened-relative-path ( c-addr u -- fid c-addr u )
3 \ this is the first preopened descriptor
begin
dup namelengthbuf fd-prestat-get =0
while
>r
namelength allocate throw \ reserve space to hold the name
r@ over namelength fd-prestat-dir-name throw
>r 2dup r@ namelength normalize-directory-name is-parent-directory? \ validate whether this is a parent
r> free throw \ free the name buffer either way
if
r@ parent-fd ! \ track that this is a valid parent
namelength 1- parent-namelength !
then
r> 1+ \ try the next
repeat drop
parent-namelength @ dup 1 >
if /string \ remove the parent from the string
else drop
then
separator-char remove-start \ and any leading directory separators
parent-fd @ -rot \ and return the parent fd AND the pathname
;
variable >fd
: open-fd-by-path ( c-addr u options -- fid err )
>r \ hold onto options for l8r
get-preopened-relative-path ( fid path-addr path-u )
0 -rot ( fid dirflags path-addr path-u )
r@ fd-oflags r> fd-rights 0 0 0 ( ... oflags drights-base drights-inheriting fdflags )
>fd path-open
>fd @ swap ( fid err )
;
\ double-aligned buffer to hold an fdstat
dalign here 8 cells allot constant >fdstat
: is-fd-file? ( fid -- ? err )
>fdstat fd-fdstat-get
?dup if 0 swap exit then \ rethrow error
>fdstat c@ 4 = 0 \ this is the offset of filetype, and the value of "normal file"
;
: open-file ( c-addr u fam -- fid err )
dup >r
open-fd-by-path
?dup if r> drop exit then \ rethrow error
r> fd-allow-read and
if
dup is-fd-file?
?dup if r> drop nip exit then
over swap filebuf-allocate
else 0
then
;
: create-file ( c-addr u fam -- fid err )
fd-create or open-file
;
: close-file ( fid -- err )
dup find-filebuf ?dup
if filebuf-delete ?dup
if nip exit
then
then
fd-close
;
: read-line ( c-addr u1 fid -- u2 more? err )
find-filebuf
?dup =0 if -7 exit then \ return an error if this file is unbuffered
>r tuck \ store filebuf and OG length for later
begin \ copy while we gotta
r@ filebuf-refill?
?dup if r> drop exit then \ rethrow error
r@ filebuf-peek
over \ while we are still reading to the buffer
over -1 <> and \ and the last char wasn't EOF
over is-term? =0 and \ and we haven't found a line terminator
while
r@ filebuf-consume ( u1 c-addr u c )
rot tuck c! swap \ write to the buffer ( u1 c-addr u )
1 /string
repeat ( u1 c-addr u2 last-char )
rot drop -rot - ( last-char u )
swap is-term? over <>0 or ( u more? )
\ consume one set of trailing terminators
r> filebuf-consume-term
;
create ciovec 2 cells allot
variable >bytes-written
: write-file ( c-addr u fid -- err )
rot ciovec ! swap ( fid u )
begin ?dup
while
dup ciovec 4 + ! \ save how many bytes to write
over ciovec 1 >bytes-written fd-write \ write bytes
?dup if nip nip exit then \ rethrow error
>bytes-written @
dup ciovec +! \ however many bytes we wrote, move that far forward in the buffer
- \ and write that many fewer bytes next iteration
repeat
drop 0
;
variable emit-buffer
: emit-file ( c fid -- err )
swap emit-buffer !
emit-buffer 1 rot write-file
;
: write-line ( c-addr u fid -- err )
dup >r
write-file ?dup =0
if 13 r> emit-file
else r> drop
then
;
: accept ( c-addr u1 -- u2 )
0 read-line throw drop
;
: emit ( c -- ) 1 emit-file throw ;
: type ( c-addr u -- ) 1 write-file throw ;
\ command-line arguments
variable argc
0 argc !
variable argv
: init-args ( -- )
argc @ if exit then
\ using argv to hold the buffer size temporarily
argc argv args-sizes-get throw
\ allot space for both argv and the strings it contains
here argc @ cells argv @ + aligned allot argv !
\ populate the args
argv @ dup argc @ cells + args-get throw
;
: arg ( n -- c-addr u )
dup argc @ >=
if drop 0 0 exit then
cells argv @ + @ ( c-addr )
\ find null terminator
dup begin dup c@ while 1+ repeat
over -
;
: shift-args ( -- )
argc @ 1 <= if exit then
argv @ 2 cells + \ copy from argv[2]
argv @ 1 cells + \ into argv[1]
argc @ 2 - cells \ copying this many bytes
move
-1 argc +!
;
: next-arg ( -- c-addr u )
1 arg shift-args
;
: bye ( -- ) 0 proc-exit ;