tur/io
FileSystem capability implementation using libc functions.
Implements the FileSystem capability typeclass for real I/O, the linear FileHandle stream API (file-open / file-read / file-write / file-seek / file-close), and line-oriented stdin (io/read-line, io/read-stdin). Path-level operations live in stdlib/fs.tur: fs/ is the filesystem namespace, io/ the stream and handle namespace.
Since: Phase 16
DirListing
(defopaque DirListing :ptr<void>)
FileStream
(defopaque FileStream :ptr<void>)
file-stream-open
(file-stream-open [path :cstr mode :cstr] :FileStream)
open a file as a non-linear FileStream handle.
| path | filesystem path to open (:cstr). | |
| mode | fopen mode string, e.g. "rb" or "wb" (:cstr). |
A FileStream wrapping the opened FILE*, or a FileStream wrapping NULL on error. Check with file-stream-ok? before using.
(let [s (file-stream-open "/etc/hostname" "rb")] (file-size s))
Since: I/O sweep
file-stream-ok?
(file-stream-ok? [f :FileStream] :bool)
return true if the FileStream wraps a real FILE*.
| f | a FileStream. |
true if the underlying FILE* is non-NULL, false on open failure.
(file-stream-ok? (file-stream-open "/etc/hostname" "rb")) ; => true
Since: I/O sweep
file-stream-close
(file-stream-close [f :FileStream] :int)
close a FileStream and release the underlying FILE*.
| f | a FileStream to close. |
0 on success, non-zero on error (mirrors fclose).
(file-stream-close s) ; => 0
Since: I/O sweep
file-size
(file-size [f :FileStream] :int)
return the byte size of an already-open FileStream.
| f | a FileStream (from file-stream-open). |
The file size in bytes as an int.
(file-size my-stream) ; => 1024
Real-FileSystem
(Real-FileSystem :FileSystem)
Real-FileSystem-free
(Real-FileSystem-free [fs :FileSystem] :void)
release a FileSystem capability created by Real-FileSystem.
| fs | the FileSystem pointer returned by Real-FileSystem. |
Since: Phase 16
list-dir
(list-dir [path :cstr] :DirListing)
list all non-hidden entries in a directory.
| path | the directory path to list (:cstr). |
A NULL-terminated array of strdup'd entry name strings, or NULL on error. The caller must free each string and then the array itself (or use free-dir-listing).
(let [entries (list-dir "/tmp")] ...)
Since: Phase 16
free-dir-listing
(free-dir-listing [entries :DirListing] :void)
free a directory listing returned by list-dir.
| entries | the NULL-terminated string array returned by list-dir. |
Since: Phase 16
FileHandle
(defopaque FileHandle :ptr<void> :linear)
a linear (exactly-once) handle over a stdio FILE*.
:linear (CK_LINEAR) means every bound FileHandle must be explicitly consumed by file-close; the compiler emits TUR-E0100 if a linear value is silently dropped. The read/write/seek operations take it ^borrow.
stdlib-os-surface-plan P1: a FileHandle only ever exists for a file that opened -- file-open answers (Result FileHandle IoError) -- so there is no NULL handle to test for. (It was a one-field struct around the FILE*; an opaque says the same thing and rides a Result payload as one word.)
Since: LT4
file-open
(file-open [path :cstr mode :cstr])
open a file and return a move-only FileHandle.
| path | filesystem path to open (:cstr). | |
| mode | fopen mode string, e.g. "rb", "wb", "ab" or "r+b" (:cstr). |
(Ok fh) -- a FileHandle that must be closed exactly once with file-close -- or (Err e) carrying the errno of the failed fopen.
(match (file-open "/etc/hostname" "rb") (Ok fh) (file-close fh) (Err e) (Err e))
Since: LT4
file-read
(file-read [^borrow fh :FileHandle buf :ptr<void> len :int])
read up to len bytes from fh into buf.
| fh | an open FileHandle (borrowed; not consumed). | |
| buf | destination buffer as ptr<void>. | |
| len | maximum number of bytes to read. |
(Ok n) -- the number of bytes read; 0 at end of file -- or (Err e) on a read error.
(file-read fh buf 1024) ; => (Ok 512)
Since: LT4
file-close
(file-close [fh :FileHandle])
close a FileHandle and release the underlying FILE*.
| fh | a FileHandle to close (consumed). |
(Ok nil), or (Err e) when flushing buffered writes failed -- the last place a write error can surface, so do not ignore it after writing.
(file-close fh) ; => (Ok nil)
Since: LT4
file-sync
(file-sync [^borrow fh :FileHandle])
flush a FileHandle all the way to the storage device.
Pushes the stdio buffer to the OS (fflush), then asks the OS to write its cached pages to disk (fsync; _commit on Windows), so the data survives a crash or power loss once this returns Ok. file-close alone does only the first half.
| fh | an open FileHandle (borrowed; not consumed). |
(Ok nil), or (Err e) -- the write error that flushing surfaced, or EINVAL for a handle (a pipe) that cannot be synced.
(file-sync fh) ; => (Ok nil)
Since: stdlib-os-surface P3
SeekFrom
(defdata SeekFrom :copy (SeekStart) (SeekCurrent) (SeekEnd))
the origin a file-seek offset is measured from.
SeekStart is the beginning of the file (SEEK_SET), SeekCurrent the current position (SEEK_CUR), SeekEnd the end of the file (SEEK_END). A tagged union rather than an int whence, so a caller cannot pass a stray integer.
Since: stdlib-os-surface P0
seek-from->whence
(seek-from->whence [w :SeekFrom] :int)
the C whence constant for a SeekFrom.
file-write
(file-write [^borrow fh :FileHandle buf :ptr<void> len :int])
write len bytes from buf to fh.
| fh | an open FileHandle (borrowed; not consumed). | |
| buf | source buffer as ptr<void>. | |
| len | number of bytes to write. |
(Ok n) with n = len, or (Err e) when fewer bytes could be written.
(file-write fh buf 13) ; => (Ok 13)
Since: stdlib-os-surface P0
file-write-str
(file-write-str [^borrow fh :FileHandle s :cstr])
write a NUL-terminated string to fh (no newline added).
| fh | an open FileHandle (borrowed; not consumed). | |
| s | the string to write. |
(Ok n) with n = the string's byte length, or (Err e).
(file-write-str fh "hello\n") ; => (Ok 6)
Since: stdlib-os-surface P0
file-seek-raw
(file-seek-raw [^borrow fh :FileHandle offset :int whence :int])
fseek(3) with a C whence constant (internal).
file-seek
(file-seek [^borrow fh :FileHandle offset :int whence :SeekFrom])
move fh's position to offset, measured from whence.
| fh | an open FileHandle (borrowed; not consumed). | |
| offset | byte offset (may be negative with SeekCurrent / SeekEnd). | |
| whence | SeekStart, SeekCurrent or SeekEnd. |
(Ok pos) -- the new absolute position -- or (Err e), e.g. EINVAL for a position before the start of the file or ESPIPE on a pipe.
(file-seek fh 0 (SeekEnd)) ; => (Ok file-size)
Since: stdlib-os-surface P0
file-tell
(file-tell [^borrow fh :FileHandle])
the current byte position of fh.
| fh | an open FileHandle (borrowed; not consumed). |
(Ok pos) from the start of the file, or (Err e) (ESPIPE on a pipe).
(file-tell fh) ; => (Ok 0)
Since: stdlib-os-surface P0
io/read-line
(io/read-line)
read one line from stdin.
The trailing newline (and a CR before it) is stripped. The last line of input counts even without a trailing newline. Uses getline(3) on POSIX and an fgets loop on Windows, which has no getline.
(Some line) -- a fresh heap cstr the caller owns (release with free) -- or None at end of input. End of input is not an error.
(match (io/read-line) (Some s) (println s) (None) (println "<eof>"))
Since: stdlib-os-surface P0
io/read-stdin
(io/read-stdin)
read all of stdin into one string.
Reads until end of input, so it works on a pipe or a terminal as well as a redirected file.
(Some text) -- a fresh NUL-terminated heap cstr the caller owns (release with free) -- or None on a read error. Empty input is (Some "").
(match (io/read-stdin) (Some s) (println s) (None) (println "read failed"))
Since: stdlib-os-surface P0
file-read-line-raw
(file-read-line-raw [^borrow fh :FileHandle])
one line, EOF as an errno-0 error (internal).
file-read-line
(file-read-line [^borrow fh :FileHandle])
read the next line from fh.
The bounded-memory way through a large file: one line per call, with the newline (and a CR before it) stripped. The last line counts without a trailing newline. Shares its reader with io/read-line.
| fh | an open FileHandle (borrowed; not consumed). |
(Ok (Some line)) -- a fresh heap cstr the caller owns (release with free); (Ok (None)) at end of file; (Err e) on a read error, so errors are never mistaken for end of file.
(match (file-read-line fh) (Ok o) (match o (Some s) (println s) (None) (println "<eof>")) (Err e) (println (io-error/message e)))
Since: stdlib-os-surface P2