Commit | Line | Data |
---|---|---|
7f918cf1 CE |
1 | (* Copyright (C) 2017 Matthew Fluet. |
2 | * Copyright (C) 1999-2006, 2008 Henry Cejtin, Matthew Fluet, Suresh | |
3 | * Jagannathan, and Stephen Weeks. | |
4 | * Copyright (C) 1997-2000 NEC Research Institute. | |
5 | * | |
6 | * MLton is released under a BSD-style license. | |
7 | * See the file MLton-LICENSE for details. | |
8 | *) | |
9 | ||
10 | structure PosixFileSys: POSIX_FILE_SYS_EXTRA = | |
11 | struct | |
12 | structure Prim = PrimitiveFFI.Posix.FileSys | |
13 | open Prim | |
14 | structure FileDesc = PrePosix.FileDesc | |
15 | structure GId = PrePosix.GId | |
16 | structure PId = PrePosix.PId | |
17 | structure Stat = Prim.Stat | |
18 | structure UId = PrePosix.UId | |
19 | ||
20 | structure Error = PosixError | |
21 | structure SysCall = Error.SysCall | |
22 | ||
23 | (* Patch to make Time look like it deals with C_Time.t | |
24 | * instead of LargeInt.int. | |
25 | *) | |
26 | structure Time = | |
27 | struct | |
28 | open Time | |
29 | ||
30 | val fromSeconds = fromSeconds o C_Time.toLargeInt | |
31 | ||
32 | fun toSeconds t = | |
33 | C_Time.fromLargeInt (Time.toSeconds t) | |
34 | handle Overflow => Error.raiseSys Error.inval | |
35 | end | |
36 | ||
37 | type file_desc = FileDesc.t | |
38 | type gid = GId.t | |
39 | type uid = UId.t | |
40 | ||
41 | val fdToWord = C_Fd.castToSysWord o FileDesc.toRep | |
42 | val wordToFD = FileDesc.fromRep o C_Fd.castFromSysWord | |
43 | ||
44 | val fdToIOD = PreOS.IODesc.fromRep o FileDesc.toRep | |
45 | val iodToFD = SOME o FileDesc.fromRep o PreOS.IODesc.toRep | |
46 | ||
47 | (*------------------------------------*) | |
48 | (* dirstream *) | |
49 | (*------------------------------------*) | |
50 | ||
51 | local | |
52 | structure Prim = Prim.Dirstream | |
53 | datatype dirstream = DS of C_DirP.t option ref | |
54 | ||
55 | fun get (DS r) = | |
56 | case !r of | |
57 | NONE => Error.raiseSys Error.badf | |
58 | | SOME d => d | |
59 | in | |
60 | type dirstream = dirstream | |
61 | ||
62 | fun opendir s = | |
63 | let | |
64 | val s = NullString.nullTerm s | |
65 | in | |
66 | SysCall.syscall' | |
67 | ({errVal = C_DirP.castFromSysWord 0w0}, fn () => | |
68 | (Prim.openDir s, fn d => | |
69 | DS (ref (SOME d)))) | |
70 | end | |
71 | ||
72 | fun readdir d = | |
73 | let | |
74 | val d = get d | |
75 | fun loop () = | |
76 | let | |
77 | val res = | |
78 | SysCall.syscallErr | |
79 | ({clear = true, restart = false, | |
80 | errVal = CUtil.C_Pointer.null}, fn () => | |
81 | {return = Prim.readDir d, | |
82 | post = fn cs => SOME cs, | |
83 | handlers = [(Error.cleared, fn () => NONE), | |
84 | (* MinGW sets errno to ENOENT when it | |
85 | * returns NULL. | |
86 | *) | |
87 | (Error.noent, fn () => NONE)]}) | |
88 | in | |
89 | case res of | |
90 | NONE => NONE | |
91 | | SOME cs => | |
92 | let | |
93 | val s = CUtil.C_String.toString cs | |
94 | in | |
95 | if s = "." orelse s = ".." | |
96 | then loop () | |
97 | else SOME s | |
98 | end | |
99 | end | |
100 | in loop () | |
101 | end | |
102 | ||
103 | fun rewinddir d = | |
104 | let val d = get d | |
105 | in Prim.rewindDir d | |
106 | end | |
107 | ||
108 | fun closedir (DS r) = | |
109 | case !r of | |
110 | NONE => () | |
111 | | SOME d => (SysCall.simple (fn () => Prim.closeDir d); r := NONE) | |
112 | end | |
113 | ||
114 | fun chdir s = | |
115 | SysCall.simple (fn () => Prim.chdir (NullString.nullTerm s)) | |
116 | ||
117 | local | |
118 | val size: int ref = ref 1 | |
119 | fun make () = Array.alloc (!size) | |
120 | val buffer = ref (make ()) | |
121 | ||
122 | fun extractToChar (a, c) = | |
123 | let | |
124 | val n = Array.length a | |
125 | (* find the null terminator *) | |
126 | fun loop i = | |
127 | if i >= n | |
128 | then raise Fail "extractToChar didn't find terminator" | |
129 | else if c = Array.sub (a, i) | |
130 | then i | |
131 | else loop (i + 1) | |
132 | in | |
133 | ArraySlice.vector (ArraySlice.slice (a, 0, SOME (loop 0))) | |
134 | end | |
135 | ||
136 | fun extract a = extractToChar (a, #"\000") | |
137 | in | |
138 | fun getcwd () = | |
139 | let | |
140 | val res = | |
141 | SysCall.syscallErr | |
142 | ({clear = false, restart = false, | |
143 | errVal = CUtil.C_Pointer.null}, fn () => | |
144 | {return = Prim.getcwd (!buffer, C_Size.fromInt (!size)), | |
145 | post = fn _ => true, | |
146 | handlers = [(Error.range, fn _ => false)]}) | |
147 | in | |
148 | if res | |
149 | then extract (!buffer) | |
150 | else (size := 2 * !size | |
151 | ; buffer := make () | |
152 | ; getcwd ()) | |
153 | end | |
154 | end | |
155 | ||
156 | val stdin : file_desc = FileDesc.fromRep 0 | |
157 | val stdout : file_desc = FileDesc.fromRep 1 | |
158 | val stderr : file_desc = FileDesc.fromRep 2 | |
159 | ||
160 | structure S = | |
161 | struct | |
162 | structure Flags = BitFlags(structure S = C_Mode) | |
163 | open S Flags | |
164 | type mode = C_Mode.t | |
165 | val ifblk = IFBLK | |
166 | val ifchr = IFCHR | |
167 | val ifdir = IFDIR | |
168 | val ififo = IFIFO | |
169 | val iflnk = IFLNK | |
170 | val ifmt = IFMT | |
171 | val ifreg = IFREG | |
172 | val ifsock = IFSOCK | |
173 | val irgrp = IRGRP | |
174 | val iroth = IROTH | |
175 | val irusr = IRUSR | |
176 | val irwxg = IRWXG | |
177 | val irwxo = IRWXO | |
178 | val irwxu = IRWXU | |
179 | val isgid = ISGID | |
180 | val isuid = ISUID | |
181 | val isvtx = ISVTX | |
182 | val iwgrp = IWGRP | |
183 | val iwoth = IWOTH | |
184 | val iwusr = IWUSR | |
185 | val ixgrp = IXGRP | |
186 | val ixoth = IXOTH | |
187 | val ixusr = IXUSR | |
188 | end | |
189 | ||
190 | structure O = | |
191 | struct | |
192 | structure Flags = BitFlags(structure S = C_Int) | |
193 | open O Flags | |
194 | val append = APPEND | |
195 | val binary = BINARY | |
196 | val creat = CREAT | |
197 | val dsync = DSYNC | |
198 | val excl = EXCL | |
199 | val noctty = NOCTTY | |
200 | val nonblock = NONBLOCK | |
201 | val rdonly = RDONLY | |
202 | val rdwr = RDWR | |
203 | val rsync = RSYNC | |
204 | val sync = SYNC | |
205 | val text = TEXT | |
206 | val trunc = TRUNC | |
207 | val wronly = WRONLY | |
208 | end | |
209 | ||
210 | datatype open_mode = O_RDONLY | O_WRONLY | O_RDWR | |
211 | ||
212 | fun flagsToOpenMode f = | |
213 | if f = O.rdonly then O_RDONLY | |
214 | else if f = O.wronly then O_WRONLY | |
215 | else if f = O.rdwr then O_RDWR | |
216 | else raise Fail "flagsToOpenMode: unknown flag" | |
217 | ||
218 | val openModeToFlags = | |
219 | fn O_RDONLY => O.rdonly | |
220 | | O_WRONLY => O.wronly | |
221 | | O_RDWR => O.rdwr | |
222 | ||
223 | fun createf (pathname, openMode, flags, mode) = | |
224 | let | |
225 | val pathname = NullString.nullTerm pathname | |
226 | val flags = O.Flags.flags [openModeToFlags openMode, | |
227 | flags, | |
228 | O.creat] | |
229 | val flags = C_Int.castFromSysWord (O.Flags.toWord flags) | |
230 | val fd = | |
231 | SysCall.simpleResult | |
232 | (fn () => Prim.open3 (pathname, flags, mode)) | |
233 | in | |
234 | FileDesc.fromRep fd | |
235 | end | |
236 | ||
237 | fun openf (pathname, openMode, flags) = | |
238 | let | |
239 | val pathname = NullString.nullTerm pathname | |
240 | val flags = O.Flags.flags [openModeToFlags openMode, flags] | |
241 | val flags = C_Int.castFromSysWord (O.Flags.toWord flags) | |
242 | val fd = | |
243 | SysCall.simpleResult | |
244 | (fn () => Prim.open3 (pathname, flags, C_Mode.castFromSysWord 0wx0)) | |
245 | in | |
246 | FileDesc.fromRep fd | |
247 | end | |
248 | ||
249 | fun creat (s, m) = createf (s, O_WRONLY, O.trunc, m) | |
250 | ||
251 | val umask = Prim.umask | |
252 | ||
253 | ||
254 | local | |
255 | fun wrap p arg = (SysCall.simple (fn () => p arg); ()) | |
256 | fun wrapRestart p arg = (SysCall.simpleRestart (fn () => p arg); ()) | |
257 | fun wrapOldNew p = | |
258 | wrap (fn {old,new} => p (NullString.nullTerm old, | |
259 | NullString.nullTerm new)) | |
260 | in | |
261 | val link = wrapOldNew Prim.link | |
262 | val mkdir = wrap (fn (p, m) => Prim.mkdir (NullString.nullTerm p, m)) | |
263 | val mkfifo = wrap (fn (p, m) => Prim.mkfifo (NullString.nullTerm p, m)) | |
264 | val unlink = wrap (Prim.unlink o NullString.nullTerm) | |
265 | val rmdir = wrap (Prim.rmdir o NullString.nullTerm) | |
266 | val rename = wrapOldNew Prim.rename | |
267 | val symlink = wrapOldNew Prim.symlink | |
268 | val chmod = | |
269 | wrap | |
270 | (fn (p, m) => | |
271 | Prim.chmod (NullString.nullTerm p, m)) | |
272 | val fchmod = | |
273 | wrap | |
274 | (fn (fd, m) => | |
275 | Prim.fchmod (FileDesc.toRep fd, m)) | |
276 | val chown = | |
277 | wrap | |
278 | (fn (s, uid, gid) => | |
279 | Prim.chown (NullString.nullTerm s, UId.toRep uid, GId.toRep gid)) | |
280 | val fchown = | |
281 | wrap | |
282 | (fn (fd, uid, gid) => | |
283 | Prim.fchown (FileDesc.toRep fd, UId.toRep uid, GId.toRep gid)) | |
284 | val ftruncate = | |
285 | wrapRestart | |
286 | (fn (fd, n) => | |
287 | Prim.ftruncate (FileDesc.toRep fd, n)) | |
288 | end | |
289 | ||
290 | local | |
291 | val size: int = 1024 | |
292 | val buf : char array = Array.array (size, #"\000") | |
293 | in | |
294 | fun readlink (path: string): string = | |
295 | let | |
296 | val path = NullString.nullTerm path | |
297 | in | |
298 | SysCall.syscall' | |
299 | ({errVal = C_SSize.castFromFixedInt ~1}, fn () => | |
300 | (Prim.readlink (path, buf, C_Size.fromInt size), fn len => | |
301 | ArraySlice.vector (ArraySlice.slice (buf, 0, SOME (C_SSize.toInt len))))) | |
302 | end | |
303 | end | |
304 | ||
305 | type dev = C_Dev.t | |
306 | val wordToDev = C_Dev.castFromSysWord | |
307 | val devToWord = C_Dev.castToSysWord | |
308 | ||
309 | type ino = C_INo.t | |
310 | val wordToIno = C_INo.castFromSysWord | |
311 | val inoToWord = C_INo.castToSysWord | |
312 | ||
313 | structure ST = | |
314 | struct | |
315 | datatype stat = | |
316 | T of {dev: dev, | |
317 | ino: ino, | |
318 | mode: S.mode, | |
319 | nlink: int, | |
320 | uid: uid, | |
321 | gid: gid, | |
322 | size: Position.int, | |
323 | atime: Time.time, | |
324 | mtime: Time.time, | |
325 | ctime: Time.time} | |
326 | ||
327 | fun fromC (): stat = | |
328 | T {dev = Stat.getDev (), | |
329 | ino = Stat.getINo (), | |
330 | mode = Stat.getMode (), | |
331 | nlink = C_NLink.toInt (Stat.getNLink ()), | |
332 | uid = UId.fromRep (Stat.getUId ()), | |
333 | gid = GId.fromRep (Stat.getGId ()), | |
334 | size = Stat.getSize (), | |
335 | atime = Time.fromSeconds (Stat.getATime ()), | |
336 | mtime = Time.fromSeconds (Stat.getMTime ()), | |
337 | ctime = Time.fromSeconds (Stat.getCTime ())} | |
338 | ||
339 | local | |
340 | fun make sel (T r) = sel r | |
341 | in | |
342 | val mode = make #mode | |
343 | val ino = make #ino | |
344 | val dev = make #dev | |
345 | val nlink = make #nlink | |
346 | val uid = make #uid | |
347 | val gid = make #gid | |
348 | val size = make #size | |
349 | val atime = make #atime | |
350 | val mtime = make #mtime | |
351 | val ctime = make #ctime | |
352 | end | |
353 | ||
354 | local | |
355 | fun make prim s = prim (mode s) <> C_Int.zero | |
356 | in | |
357 | val isDir = make Prim.ST.isDir | |
358 | val isChr = make Prim.ST.isChr | |
359 | val isBlk = make Prim.ST.isBlk | |
360 | val isReg = make Prim.ST.isReg | |
361 | val isFIFO = make Prim.ST.isFIFO | |
362 | val isLink = make Prim.ST.isLink | |
363 | val isSock = make Prim.ST.isSock | |
364 | end | |
365 | end | |
366 | ||
367 | local | |
368 | fun make prim arg = | |
369 | SysCall.syscall (fn () => (prim arg, fn _ => ST.fromC ())) | |
370 | in | |
371 | val stat = (make Prim.Stat.stat) o NullString.nullTerm | |
372 | val lstat = (make Prim.Stat.lstat) o NullString.nullTerm | |
373 | val fstat = (make Prim.Stat.fstat) o FileDesc.toRep | |
374 | end | |
375 | ||
376 | datatype access_mode = A_READ | A_WRITE | A_EXEC | |
377 | ||
378 | val conv_access_mode = | |
379 | fn A_READ => A.R_OK | |
380 | | A_WRITE => A.W_OK | |
381 | | A_EXEC => A.X_OK | |
382 | ||
383 | fun access (path: string, mode: access_mode list): bool = | |
384 | let | |
385 | val mode = List.foldl C_Int.orb 0 (A.F_OK :: (map conv_access_mode mode)) | |
386 | val path = NullString.nullTerm path | |
387 | in | |
388 | SysCall.syscallErr | |
389 | ({clear = false, restart = false, errVal = C_Int.fromInt ~1}, fn () => | |
390 | {return = Prim.access (path, mode), | |
391 | post = fn _ => true, | |
392 | handlers = [(Error.acces, fn () => false), | |
393 | (Error.loop, fn () => false), | |
394 | (Error.nametoolong, fn () => false), | |
395 | (Error.noent, fn () => false), | |
396 | (Error.notdir, fn () => false), | |
397 | (Error.rofs, fn () => false)]}) | |
398 | end | |
399 | ||
400 | local | |
401 | structure U = Prim.Utimbuf | |
402 | in | |
403 | fun utime (f: string, opt: {actime: Time.time, | |
404 | modtime: Time.time} option): unit = | |
405 | let | |
406 | val (a, m) = | |
407 | case opt of | |
408 | NONE => let val t = Time.now () | |
409 | in (t, t) | |
410 | end | |
411 | | SOME {actime = a, modtime = m} => (a, m) | |
412 | val a = Time.toSeconds a | |
413 | val m = Time.toSeconds m | |
414 | val f = NullString.nullTerm f | |
415 | in | |
416 | SysCall.syscallRestart | |
417 | (fn () => | |
418 | (U.setAcTime a | |
419 | ; U.setModTime m | |
420 | ; (U.utime f, fn _ => | |
421 | ()))) | |
422 | end | |
423 | end | |
424 | ||
425 | local | |
426 | local | |
427 | open Prim.PC | |
428 | infixr 5 ::? | |
429 | fun (n,s) ::? l = | |
430 | if n = C_Int.fromInt ~1 | |
431 | then l | |
432 | else (n,s) :: l | |
433 | in | |
434 | val properties = | |
435 | (TWO_SYMLINKS,"2_SYMLINKS") ::? | |
436 | (ALLOC_SIZE_MIN,"ALLOC_SIZE_MIN") ::? | |
437 | (ASYNC_IO,"ASYNC_IO") ::? | |
438 | (CHOWN_RESTRICTED,"CHOWN_RESTRICTED") ::? | |
439 | (FILESIZEBITS,"FILESIZEBITS") ::? | |
440 | (LINK_MAX,"LINK_MAX") ::? | |
441 | (MAX_CANON,"MAX_CANON") ::? | |
442 | (MAX_INPUT,"MAX_INPUT") ::? | |
443 | (NAME_MAX,"NAME_MAX") ::? | |
444 | (NO_TRUNC,"NO_TRUNC") ::? | |
445 | (PATH_MAX,"PATH_MAX") ::? | |
446 | (PIPE_BUF,"PIPE_BUF") ::? | |
447 | (PRIO_IO,"PRIO_IO") ::? | |
448 | (REC_INCR_XFER_SIZE,"REC_INCR_XFER_SIZE") ::? | |
449 | (REC_MAX_XFER_SIZE,"REC_MAX_XFER_SIZE") ::? | |
450 | (REC_MIN_XFER_SIZE,"REC_MIN_XFER_SIZE") ::? | |
451 | (REC_XFER_ALIGN,"REC_XFER_ALIGN") ::? | |
452 | (SYMLINK_MAX,"SYMLINK_MAX") ::? | |
453 | (SYNC_IO,"SYNC_IO") ::? | |
454 | (VDISABLE,"VDISABLE") ::? | |
455 | [] | |
456 | end | |
457 | ||
458 | fun convertProperty s = | |
459 | case List.find (fn (_, s') => s = s') properties of | |
460 | NONE => Error.raiseSys Error.inval | |
461 | | SOME (n, _) => n | |
462 | ||
463 | fun make prim (f, s) = | |
464 | SysCall.syscallErr | |
465 | ({clear = true, restart = false, errVal = C_Long.fromInt ~1}, fn () => | |
466 | {return = prim (f, convertProperty s), | |
467 | post = fn ret => SOME (SysWord.fromLargeInt (C_Long.toLarge ret)), | |
468 | handlers = [(Error.cleared, fn () => NONE)]}) | |
469 | in | |
470 | val pathconf = make (fn (path, s) => Prim.pathconf (NullString.nullTerm path, s)) | |
471 | val fpathconf = make (fn (fd, s) => Prim.fpathconf (FileDesc.toRep fd, s)) | |
472 | end | |
473 | end |