| @@ -14,12 +14,44 @@ fun read path = |
| 14 | 14 | if size input > 65536 then raise Fail "source exceeds 64 KiB" else chars input |
| 15 | 15 | end; |
| 16 | 16 | |
| 17 | | -fun write path bytes = |
| 17 | +fun write path data = |
| 18 | 18 | let |
| 19 | | - val stream = BinIO.openOut path |
| 19 | + val bytes = Byte.stringToBytes (text data) |
| 20 | + val pid = SysWord.toString (Posix.Process.pidToWord (Posix.ProcEnv.getpid ())) |
| 21 | + val prefix = OS.Path.concat (OS.Path.dir path, "." ^ OS.Path.file path ^ "." ^ pid ^ ".") |
| 22 | + val defaultMode = Posix.FileSys.S.flags [Posix.FileSys.S.irusr, Posix.FileSys.S.iwusr, |
| 23 | + Posix.FileSys.S.irgrp, Posix.FileSys.S.iwgrp, Posix.FileSys.S.iroth, Posix.FileSys.S.iwoth] |
| 24 | + val mode = Posix.FileSys.S.intersect [Posix.FileSys.ST.mode (Posix.FileSys.stat path), |
| 25 | + Posix.FileSys.S.flags [Posix.FileSys.S.irwxu, Posix.FileSys.S.irwxg, Posix.FileSys.S.irwxo]] |
| 26 | + handle exn as OS.SysErr (_, SOME error) => |
| 27 | + if error = Posix.Error.noent then defaultMode else raise exn |
| 28 | + fun reserve n = |
| 29 | + if n = 128 then raise Fail "cannot reserve output file" |
| 30 | + else |
| 31 | + let |
| 32 | + val temporary = prefix ^ Int.toString n |
| 33 | + in |
| 34 | + (temporary, Posix.FileSys.createf |
| 35 | + (temporary, Posix.FileSys.O_WRONLY, Posix.FileSys.O.excl, mode)) |
| 36 | + end handle exn as OS.SysErr (_, SOME error) => |
| 37 | + if error = Posix.Error.exist then reserve (n + 1) else raise exn |
| 38 | + val (temporary, fd) = reserve 0 |
| 39 | + val closed = ref false |
| 40 | + fun close () = (Posix.IO.close fd; closed := true) |
| 41 | + fun discard () = |
| 42 | + ((if !closed then () else close ()) handle _ => (); |
| 43 | + OS.FileSys.remove temporary handle _ => ()) |
| 44 | + fun loop offset = |
| 45 | + if offset = Word8Vector.length bytes then () |
| 46 | + else |
| 47 | + let |
| 48 | + val count = Posix.IO.writeVec (fd, Word8VectorSlice.slice (bytes, offset, NONE)) |
| 49 | + in |
| 50 | + if count = 0 then raise Fail "output write made no progress" else loop (offset + count) |
| 51 | + end |
| 20 | 52 | in |
| 21 | | - (BinIO.output (stream, Byte.stringToBytes (text bytes)); BinIO.closeOut stream) |
| 22 | | - handle exn => ((BinIO.closeOut stream handle _ => ()); raise exn) |
| 53 | + (loop 0; close (); OS.FileSys.rename {old = temporary, new = path}) |
| 54 | + handle exn => (discard (); raise exn) |
| 23 | 55 | end; |
| 24 | 56 | |
| 25 | 57 | fun output (ok, message) = |