Import Upstream version 20180207
[hcoop/debian/mlton.git] / basis-library / posix / file-sys.sml
CommitLineData
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
10structure 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