Import Upstream version 20180207
[hcoop/debian/mlton.git] / basis-library / posix / io.sml
CommitLineData
7f918cf1
CE
1(* Copyright (C) 2017 Matthew Fluet.
2 * Copyright (C) 1999-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 PosixIO: POSIX_IO =
11struct
12
13structure Prim = PrimitiveFFI.Posix.IO
14open Prim
15structure FileDesc = PrePosix.FileDesc
16structure PId = PrePosix.PId
17
18structure Error = PosixError
19structure SysCall = Error.SysCall
20structure FS = PosixFileSys
21
22type file_desc = FileDesc.t
23type pid = PId.t
24
25local
26 val a: C_Fd.t array = Array.array (2, C_Fd.fromInt 0)
27 val get = fn i => FileDesc.fromRep (Array.sub (a, i))
28in
29 fun pipe () =
30 SysCall.syscall
31 (fn () =>
32 (Prim.pipe a,
33 fn _ => {infd = get 0,
34 outfd = get 1}))
35end
36
37fun dup fd =
38 (FileDesc.fromRep o SysCall.simpleResult)
39 (fn () => Prim.dup (FileDesc.toRep fd))
40
41fun dup2 {new, old} =
42 SysCall.simple
43 (fn () => Prim.dup2 (FileDesc.toRep old, FileDesc.toRep new))
44
45fun close fd =
46 SysCall.simpleRestart
47 (fn () => Prim.close (FileDesc.toRep fd))
48
49structure FD =
50 struct
51 structure Flags = BitFlags(structure S = C_Int)
52 open FD Flags
53 val cloexec = CLOEXEC
54 end
55
56structure O = PosixFileSys.O
57
58datatype open_mode = datatype PosixFileSys.open_mode
59
60fun dupfd {base, old} =
61 (FileDesc.fromRep o SysCall.simpleResultRestart)
62 (fn () => Prim.fcntl3 (FileDesc.toRep old, F_DUPFD, FileDesc.toRep base))
63
64fun getfd fd =
65 SysCall.simpleResultRestart
66 (fn () => Prim.fcntl2 (FileDesc.toRep fd, F_GETFD))
67
68fun setfd (fd, flags): unit =
69 SysCall.simpleRestart
70 (fn () => Prim.fcntl3 (FileDesc.toRep fd, F_SETFD, flags))
71
72fun getfl fd : O.flags * open_mode =
73 let
74 val n = SysCall.simpleResultRestart
75 (fn () => Prim.fcntl2 (FileDesc.toRep fd, F_GETFL))
76 val flags = C_Int.andb (n, C_Int.notb O_ACCMODE)
77 val mode = C_Int.andb (n, O_ACCMODE)
78 in (flags, PosixFileSys.flagsToOpenMode mode)
79 end
80
81fun setfl (fd, flags: O.flags): unit =
82 SysCall.simpleRestart
83 (fn () => Prim.fcntl3 (FileDesc.toRep fd, F_SETFL, flags))
84
85datatype whence = SEEK_SET | SEEK_CUR | SEEK_END
86
87val whenceToInt =
88 fn SEEK_SET => Prim.SEEK_SET
89 | SEEK_CUR => Prim.SEEK_CUR
90 | SEEK_END => Prim.SEEK_END
91
92fun lseek (fd, n: Position.int, w: whence): Position.int =
93 SysCall.simpleResult'
94 ({errVal = C_Off.fromInt ~1}, fn () =>
95 Prim.lseek (FileDesc.toRep fd, n, whenceToInt w))
96
97fun fsync fd : unit =
98 SysCall.simple
99 (fn () => Prim.fsync (FileDesc.toRep fd))
100
101val whenceToInt =
102 fn SEEK_SET => Prim.FLock.SEEK_SET
103 | SEEK_CUR => Prim.FLock.SEEK_CUR
104 | SEEK_END => Prim.FLock.SEEK_END
105
106fun intToWhence n =
107 if n = Prim.FLock.SEEK_SET
108 then SEEK_SET
109 else if n = Prim.FLock.SEEK_CUR
110 then SEEK_CUR
111 else if n = Prim.FLock.SEEK_END
112 then SEEK_END
113 else raise Fail "Posix.IO.intToWhence"
114
115datatype lock_type = F_RDLCK | F_WRLCK | F_UNLCK
116
117val lockTypeToInt =
118 fn F_RDLCK => Prim.FLock.F_RDLCK
119 | F_WRLCK => Prim.FLock.F_WRLCK
120 | F_UNLCK => Prim.FLock.F_UNLCK
121
122fun intToLockType n =
123 if n = Prim.FLock.F_RDLCK
124 then F_RDLCK
125 else if n = Prim.FLock.F_WRLCK
126 then F_WRLCK
127 else if n = Prim.FLock.F_UNLCK
128 then F_UNLCK
129 else raise Fail "Posix.IO.intToLockType"
130
131structure FLock =
132 struct
133 open FLock
134
135 type flock = {ltype: lock_type,
136 whence: whence,
137 start: Position.int,
138 len: Position.int,
139 pid: pid option}
140
141 fun flock l = l
142 val ltype: flock -> lock_type = #ltype
143 val whence: flock -> whence = #whence
144 val start: flock -> Position.int = #start
145 val len: flock -> Position.int = #len
146 val pid: flock -> pid option = #pid
147 end
148
149local
150 structure P = Prim.FLock
151 fun make
152 (cmd, usepid)
153 (fd, {ltype, whence, start, len, ...}: FLock.flock)
154 : FLock.flock =
155 SysCall.syscallRestart
156 (fn () =>
157 ((P.setType (lockTypeToInt ltype)
158 ; P.setWhence (whenceToInt whence)
159 ; P.setStart start
160 ; P.setLen len
161 ; P.fcntl (FileDesc.toRep fd, cmd)), fn _ =>
162 {ltype = intToLockType (P.getType ()),
163 whence = intToWhence (P.getWhence ()),
164 start = P.getStart (),
165 len = P.getLen (),
166 pid = if usepid then SOME (PId.fromRep (P.getPId ()))
167 else NONE}))
168in
169 val getlk = make (FLock.F_GETLK, true)
170 val setlk = make (FLock.F_SETLK, false)
171 val setlkw = make (FLock.F_SETLKW, false)
172end
173
174(* Adapted from SML/NJ sources. *)
175(* posix-bin-prim-io.sml
176 *
177 * COPYRIGHT (c) 1995 AT&T Bell Laboratories.
178 *
179 * This implements the UNIX version of the OS specific binary primitive
180 * IO structure. The Text IO version is implemented by a trivial translation
181 * of these operations (see posix-text-prim-io.sml).
182 *
183 *)
184local
185 val pos0 = Position.fromInt 0
186 fun isReg fd = FS.ST.isReg(FS.fstat fd)
187 fun posFns (closed, fd) =
188 if (isReg fd)
189 then let
190 val pos = ref pos0
191 fun getPos () = !pos
192 fun setPos p = (if !closed
193 then raise IO.ClosedStream
194 else ();
195 pos := lseek(fd,p,SEEK_SET))
196 fun endPos () = (if !closed
197 then raise IO.ClosedStream
198 else ();
199 FS.ST.size(FS.fstat fd))
200 fun verifyPos () = let
201 val curPos = lseek(fd, pos0, SEEK_CUR)
202 in
203 pos := curPos; curPos
204 end
205 val _ = verifyPos ()
206 in
207 {pos = pos,
208 getPos = SOME getPos,
209 setPos = SOME setPos,
210 endPos = SOME endPos,
211 verifyPos = SOME verifyPos}
212 end
213 else {pos = ref pos0,
214 getPos = NONE,
215 setPos = NONE,
216 endPos = NONE,
217 verifyPos = NONE}
218
219 fun make {RD, WR, fromVector, readArr, setMode, toArraySlice, toVectorSlice,
220 vectorLength, writeArr, writeVec} =
221 let
222 val primReadArr = fn (fd, buf, i, sz) =>
223 readArr (FileDesc.toRep fd, buf, C_Int.fromInt i, C_Size.fromInt sz)
224 val primWriteArr = fn (fd, buf, i, sz) =>
225 writeArr (FileDesc.toRep fd, buf, C_Int.fromInt i, C_Size.fromInt sz)
226 val primWriteVec = fn (fd, buf, i, sz) =>
227 writeVec (FileDesc.toRep fd, buf, C_Int.fromInt i, C_Size.fromInt sz)
228 val setMode =
229 fn fd =>
230 if let
231 open Primitive.MLton.Platform.OS
232 in
233 case host of
234 MinGW => true
235 | _ => false
236 end
237 then setMode (FileDesc.toRep fd)
238 else ()
239 fun readArr (fd, sl): int =
240 let
241 val (buf, i, sz) = ArraySlice.base (toArraySlice sl)
242 val bytesRead =
243 SysCall.simpleResultRestart'
244 ({errVal = C_SSize.castFromFixedInt ~1}, fn () =>
245 primReadArr (fd, buf, i, sz))
246 val bytesRead = C_SSize.toInt bytesRead
247 in
248 bytesRead
249 end
250 fun readVec (fd, n) =
251 let
252 val buf = Array.alloc n
253 val bytesRead =
254 SysCall.simpleResultRestart'
255 ({errVal = C_SSize.castFromFixedInt ~1}, fn () =>
256 primReadArr (fd, buf, 0, n))
257 val bytesRead = C_SSize.toInt bytesRead
258 in
259 fromVector
260 (if n = bytesRead
261 then Vector.unsafeFromArray buf
262 else ArraySlice.vector (ArraySlice.slice (buf, 0, SOME bytesRead)))
263 end
264 fun writeArr (fd, sl): int =
265 let
266 val (buf, i, sz) = ArraySlice.base (toArraySlice sl)
267 val bytesWrote =
268 SysCall.simpleResultRestart'
269 ({errVal = C_SSize.castFromFixedInt ~1}, fn () =>
270 primWriteArr (fd, buf, i, sz))
271 val bytesWrote = C_SSize.toInt bytesWrote
272 in
273 bytesWrote
274 end
275 fun writeVec (fd, sl): int =
276 let
277 val (buf, i, sz) = VectorSlice.base (toVectorSlice sl)
278 val bytesWrote =
279 SysCall.simpleResultRestart'
280 ({errVal = C_SSize.castFromFixedInt ~1}, fn () =>
281 primWriteVec (fd, buf, i, sz))
282 val bytesWrote = C_SSize.toInt bytesWrote
283 in
284 bytesWrote
285 end
286 fun mkReader {fd, name, initBlkMode} =
287 let
288 val closed = ref false
289 val {pos, getPos, setPos, endPos, verifyPos} =
290 posFns (closed, fd)
291 val blocking = ref initBlkMode
292 fun blockingOn () =
293 (setfl(fd, O.flags[]); blocking := true)
294 fun blockingOff () =
295 (setfl(fd, O.nonblock); blocking := false)
296 fun ensureOpen () =
297 if !closed then raise IO.ClosedStream else ()
298 fun incPos k = pos := Position.+ (!pos, Position.fromInt k)
299 val readVec = fn n =>
300 let val v = readVec (fd, n)
301 in incPos (vectorLength v); v
302 end
303 val readArr = fn x =>
304 let val k = readArr (fd, x)
305 in incPos k; k
306 end
307 fun blockWrap f x =
308 (ensureOpen ();
309 if !blocking then () else blockingOn ();
310 f x)
311 fun noBlockWrap f x =
312 (ensureOpen ();
313 if !blocking then blockingOff () else ();
314 (SOME (f x)
315 handle (e as PosixError.SysErr (_, cause)) =>
316 if cause = SOME PosixError.again then NONE else raise e))
317 val close =
318 fn () => if !closed then () else (closed := true; close fd)
319 val avail =
320 if isReg fd
321 then fn () => if !closed
322 then SOME 0
323 else SOME (Position.toInt
324 (Position.-
325 (FS.ST.size (FS.fstat fd),
326 !pos)))
327 else fn () => if !closed then SOME 0 else NONE
328 val () = setMode fd
329 in
330 RD {avail = avail,
331 block = NONE,
332 canInput = NONE,
333 chunkSize = Int32.toInt Primitive.Controls.bufSize,
334 close = close,
335 endPos = endPos,
336 getPos = getPos,
337 ioDesc = SOME (FS.fdToIOD fd),
338 name = name,
339 readArr = SOME (blockWrap readArr),
340 readArrNB = SOME (noBlockWrap readArr),
341 readVec = SOME (blockWrap readVec),
342 readVecNB = SOME (noBlockWrap readVec),
343 setPos = setPos,
344 verifyPos = verifyPos}
345 end
346 fun mkWriter {fd, name, initBlkMode, appendMode, chunkSize} =
347 let
348 val closed = ref false
349 val {pos, getPos, setPos, endPos, verifyPos} =
350 posFns (closed, fd)
351 fun incPos k = (pos := Position.+ (!pos, Position.fromInt k); k)
352 val blocking = ref initBlkMode
353 val appendFlgs = O.flags(if appendMode then [O.append] else [])
354 fun updateStatus () =
355 let
356 val flgs = if !blocking
357 then appendFlgs
358 else O.flags [O.nonblock, appendFlgs]
359 in
360 setfl(fd, flgs)
361 end
362 fun ensureOpen () =
363 if !closed then raise IO.ClosedStream else ()
364 fun ensureBlock x =
365 if !blocking then () else (blocking := x; updateStatus ())
366 fun putV x = incPos (writeVec x)
367 fun putA x = incPos (writeArr x)
368 fun write (put, block) arg =
369 (ensureOpen (); ensureBlock block; put (fd, arg))
370 fun handleBlock writer arg =
371 SOME(writer arg)
372 handle (e as PosixError.SysErr (_, cause)) =>
373 if cause = SOME PosixError.again then NONE else raise e
374 val close =
375 fn () => if !closed then () else (closed := true; close fd)
376 val () = setMode fd
377 in
378 WR {block = NONE,
379 canOutput = NONE,
380 chunkSize = chunkSize,
381 close = close,
382 endPos = endPos,
383 getPos = getPos,
384 ioDesc = SOME (FS.fdToIOD fd),
385 name = name,
386 setPos = setPos,
387 verifyPos = verifyPos,
388 writeArr = SOME (write (putA, true)),
389 writeArrNB = SOME (handleBlock (write (putA, false))),
390 writeVec = SOME (write (putV, true)),
391 writeVecNB = SOME (handleBlock (write (putV, false)))}
392 end
393 in
394 {mkReader = mkReader,
395 mkWriter = mkWriter,
396 readArr = readArr,
397 readVec = readVec,
398 writeArr = writeArr,
399 writeVec = writeVec}
400 end
401in
402 val {mkReader = mkBinReader, mkWriter = mkBinWriter,
403 readArr, readVec, writeArr, writeVec} =
404 make {RD = BinPrimIO.RD,
405 WR = BinPrimIO.WR,
406 fromVector = Word8Vector.fromPoly,
407 readArr = readWord8,
408 setMode = Prim.setbin,
409 toArraySlice = Word8ArraySlice.toPoly,
410 toVectorSlice = Word8VectorSlice.toPoly,
411 vectorLength = Word8Vector.length,
412 writeArr = writeWord8Arr,
413 writeVec = writeWord8Vec}
414 val {mkReader = mkTextReader, mkWriter = mkTextWriter, ...} =
415 make {RD = TextPrimIO.RD,
416 WR = TextPrimIO.WR,
417 fromVector = fn v => v,
418 readArr = readChar8,
419 setMode = Prim.settext,
420 toArraySlice = CharArraySlice.toPoly,
421 toVectorSlice = CharVectorSlice.toPoly,
422 vectorLength = CharVector.length,
423 writeArr = writeChar8Arr,
424 writeVec = writeChar8Vec}
425end
426
427end