Commit | Line | Data |
---|---|---|
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 | ||
10 | structure PosixIO: POSIX_IO = | |
11 | struct | |
12 | ||
13 | structure Prim = PrimitiveFFI.Posix.IO | |
14 | open Prim | |
15 | structure FileDesc = PrePosix.FileDesc | |
16 | structure PId = PrePosix.PId | |
17 | ||
18 | structure Error = PosixError | |
19 | structure SysCall = Error.SysCall | |
20 | structure FS = PosixFileSys | |
21 | ||
22 | type file_desc = FileDesc.t | |
23 | type pid = PId.t | |
24 | ||
25 | local | |
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)) | |
28 | in | |
29 | fun pipe () = | |
30 | SysCall.syscall | |
31 | (fn () => | |
32 | (Prim.pipe a, | |
33 | fn _ => {infd = get 0, | |
34 | outfd = get 1})) | |
35 | end | |
36 | ||
37 | fun dup fd = | |
38 | (FileDesc.fromRep o SysCall.simpleResult) | |
39 | (fn () => Prim.dup (FileDesc.toRep fd)) | |
40 | ||
41 | fun dup2 {new, old} = | |
42 | SysCall.simple | |
43 | (fn () => Prim.dup2 (FileDesc.toRep old, FileDesc.toRep new)) | |
44 | ||
45 | fun close fd = | |
46 | SysCall.simpleRestart | |
47 | (fn () => Prim.close (FileDesc.toRep fd)) | |
48 | ||
49 | structure FD = | |
50 | struct | |
51 | structure Flags = BitFlags(structure S = C_Int) | |
52 | open FD Flags | |
53 | val cloexec = CLOEXEC | |
54 | end | |
55 | ||
56 | structure O = PosixFileSys.O | |
57 | ||
58 | datatype open_mode = datatype PosixFileSys.open_mode | |
59 | ||
60 | fun dupfd {base, old} = | |
61 | (FileDesc.fromRep o SysCall.simpleResultRestart) | |
62 | (fn () => Prim.fcntl3 (FileDesc.toRep old, F_DUPFD, FileDesc.toRep base)) | |
63 | ||
64 | fun getfd fd = | |
65 | SysCall.simpleResultRestart | |
66 | (fn () => Prim.fcntl2 (FileDesc.toRep fd, F_GETFD)) | |
67 | ||
68 | fun setfd (fd, flags): unit = | |
69 | SysCall.simpleRestart | |
70 | (fn () => Prim.fcntl3 (FileDesc.toRep fd, F_SETFD, flags)) | |
71 | ||
72 | fun 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 | ||
81 | fun setfl (fd, flags: O.flags): unit = | |
82 | SysCall.simpleRestart | |
83 | (fn () => Prim.fcntl3 (FileDesc.toRep fd, F_SETFL, flags)) | |
84 | ||
85 | datatype whence = SEEK_SET | SEEK_CUR | SEEK_END | |
86 | ||
87 | val whenceToInt = | |
88 | fn SEEK_SET => Prim.SEEK_SET | |
89 | | SEEK_CUR => Prim.SEEK_CUR | |
90 | | SEEK_END => Prim.SEEK_END | |
91 | ||
92 | fun 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 | ||
97 | fun fsync fd : unit = | |
98 | SysCall.simple | |
99 | (fn () => Prim.fsync (FileDesc.toRep fd)) | |
100 | ||
101 | val whenceToInt = | |
102 | fn SEEK_SET => Prim.FLock.SEEK_SET | |
103 | | SEEK_CUR => Prim.FLock.SEEK_CUR | |
104 | | SEEK_END => Prim.FLock.SEEK_END | |
105 | ||
106 | fun 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 | ||
115 | datatype lock_type = F_RDLCK | F_WRLCK | F_UNLCK | |
116 | ||
117 | val lockTypeToInt = | |
118 | fn F_RDLCK => Prim.FLock.F_RDLCK | |
119 | | F_WRLCK => Prim.FLock.F_WRLCK | |
120 | | F_UNLCK => Prim.FLock.F_UNLCK | |
121 | ||
122 | fun 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 | ||
131 | structure 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 | ||
149 | local | |
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})) | |
168 | in | |
169 | val getlk = make (FLock.F_GETLK, true) | |
170 | val setlk = make (FLock.F_SETLK, false) | |
171 | val setlkw = make (FLock.F_SETLKW, false) | |
172 | end | |
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 | *) | |
184 | local | |
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 | |
401 | in | |
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} | |
425 | end | |
426 | ||
427 | end |