Myrddin: File Handling

File Handling

pkg std =
        type dir
    type diriter

    /* well-known file descriptors */
    const In
    const Out
    const Err

        /* seek options */
        const Seekset   : whence 
        const Seekcur   : whence 
        const Seekend   : whence 

        /* open options */
        const Oread     : fdopt 
        const Owrite    : fdopt 
        const Ordwr     : fdopt 
        const Otrunc    : fdopt 
        const Ocreat    : fdopt 
        const Ocexec    : fdopt 
        const Oappend   : fdopt 
        const Odir  : fdopt 

        /* directory handling */
        const byentry   : (d : dir# -> diriter)
        const diropen   : (p : byte[:] -> std.result(dir#, byte[:]))
        const dirread   : (d : dir# -> std.option(byte[:]))
        const dirclose  : (d : dir# -> void)
        const dirname   : (p : byte[:] -> byte[:])
        const mkdir     : (path : byte[:], mode : int64 -> int64)
        const mkpath    : (p : byte[:] -> bool)
        const chdir     : (path : byte[:] -> bool)

        /* file handling */
        const open  : (path : byte[:], opts : fdopt -> std.result(fd, errno))
        const openmode  : (path : byte[:], opts : fdopt, mode : int64 -> std.result(fd, errno))
        const mktemp    : (base : byte[:], opts : fdopt, mode : int64 -> std.result((fd, byte[:]), errno)
        const mkdtemp   : (base : byte[:], mode : int64 -> std.result(byte[:], errno)
        const close     : (fd : fd -> int64)
        const creat     : (path : byte[:], mode : int64 -> fd)
        const read      : (fd : fd, buf : byte[:] -> std.result(size, errno))
        const pread         : (fd : fd, buf : byte[:], off : off -> std.result(size, errno))
        const readall       : (fd : fd, buf : byte[:] -> std.result(size, errno))
        const write         : (fd : fd, buf : byte[:] -> std.result(size, errno))
        const pwrite        : (fd : fd, buf : byte[:], off : off -> std.result(size, errno))
    const writeall  : (fd : fd, buf : byte[:] -> std.result(size, errno))
        const seek          : (fd : fd, delta : off, whence : whence -> off)
        const pipe          : (fds : fd[2]# -> int64)
        const dup2      : (ofd : fd, nfd : fd -> fd)
        const remove    : (path : byte[:] -> bool)
        const unlink    : (path : byte[:] -> int)

        /* path manipulation */
        const basename  : (p : byte[:] -> byte[:])
        const pathcat   : (a : byte[:], b : byte[:] -> byte[:])
        const pathjoin  : (p : byte[:][:] -> byte[:])
        const pathnorm  : (p : byte[:] -> byte[:])
        const getcwd : (-> byte[:])

        /* file properties */
        const fmtime    : (f : byte[:]  -> result(time, errno))
        const fsize : (f : byte[:]  -> result(off, errno))
        const fexists   : (f : byte[:]  -> bool)
        const fisdir    : (f : byte[:]  -> bool)

        /* convenience functions */
        const slurp : (path : byte[:] -> result(byte[:], byte[:]))
        const fslurp : (path : fd -> result(byte[:], byte[:]))
        const blat : (path : byte[:], buf : byte[:], perm : int64 -> bool)
        const fblat : (f : fd, buf : byte[:] -> bool)
;;

Data Types and Constants

Libstd's file APIs are generally relatively thin wrappers around the host OS functions. They are a portable subset of this functionality, designed for both ease of use and portability.

type dir = struct
;;

The directory struct represents the current state of a directory walk.

/* seek options */
const Seekset   : whence 
const Seekcur   : whence 
const Seekend   : whence 

These are a set of values which describe from where to seek within the file.

/* open options */
const Ordonly   : fdopt
const Owronly   : fdopt
const Ordwr     : fdopt 
const Otrunc    : fdopt 
const Ocreat    : fdopt 

These are a set of options that are passed to the open variants describing what mode to open the file in. These are bit masks which be OR'ed together to combine them.

Ordonly and Owronly request permission to read and write the file, respectively. Ordwr requests both read and write permissions. Otrunc indicates that the file should be truncated to zero bytes in length before it is opened. Ocreat indicates that the file should be created if it does not exist instead of returning an error. Odir indicates that this file should be opened as a directory.

Ocreat does not create the path leading to it, and will return an error if that path does not exist.

/* constants */
const In
const Out
const Err

The constants In, Out, and Err respectively are defined to the system stdin, stdout, and stderr file descriptors.

Directories

const diropen   : (p : byte[:] -> std.result(dir#, byte[:]))

The diropen function opens a path as a directory, and returns a pointer to an object which tracks the state of the directory, allowing for reading file names one by one from this directory.

Returns: Either a directory wrapped up in the `Ok branch of the result, or a string describing the failure reason in the `Err branch.

const dirread   : (d : dir# -> std.option(byte[:]))

The dirread reads a single entry from the directory, opened with diropen, returning it as a string. The returned string must be freed by the caller.

Returns `Some entry, or `None at the end of the directory.

const dirclose  : (d : dir# -> void)

dirclose closes a directory for reading. This frees all associated resources, returning them to the system. The directory passed in must have been opened with diropen.

Returns: Nothing.

const mkdir : (path : byte[:], mode : int64 -> errno)

mkdir creates the directory specified in path. with the mode mode. It requires the parent directory to exist and be writable. Absolute paths will be created relative to the root of the file system, and relative paths will be created relative to the current working directory, as usual.

If the directory already exists, this is counted as a failure to create the directory.

Returns: Enone on success, or the error that caused the failure if it fails.

const mkpath    : (path : byte[:] -> errno)

mkpath creates a full path specified by path. It creates all of the path components required to create the full path specified. It requires the parent of the first directory entry that is not currently in existence to be writable.

Absolute paths will be created relative to the root of the file system, and relative paths will be created relative to the current working directory, as usual.

Returns: Enone on success, or the error that caused the directory creation to fail on failure.

const chdir : (path : byte[:] -> bool)

Chdir changes the current working directory to the path specified in path. Returns True on success, false on failure.

const byentry : (d : dir# -> diriter)

Returns a directory iterator that can be used in a for loop.

Files

const open  : (path : byte[:], opts : fdopt -> result(fd, errno))
const openmode  : (path : byte[:], opts : fdopt, mode : int64 -> result(fd, errno))

Open opens the file path for I/O according to the flags reqeusted in opts, and returns a result containing the file descriptor or error. The mode is a combination of the modes specified above: Ordonly, Owronly, Ordwr, Otrunc, or Ocreat.

Openmode is similar to open, however, if Ocreate is passed, it the file requested will be created with the permissions passed in.

Returns: either a valid file descriptor on success, or an error describing why the open failed on failure.

const mktemp : (base : byte[:], opts : fdopt, mode : int64 -> std.result((fd, byte[:]), errno)

Mktemp is similar to openmode, however instead of taking a full path as its first argument, it generates a unique name for a file in temporary storage by appending a random string to the base name base. The mode provided is in addition to the implicit Ocreat | Oexcl.

Returns: Either a successful result containing the tuple of file descriptor opened and path generated, or an error describing the failure. The path is allocated with std.slalloc, and must be freed by the caller using std.slfree. The file descriptor must be closed as ususal.

const mkdtemp : (base : byte[:], mode : int64 -> std.result(byte[:], errno)

mkdtemp is to mkdir as mktemp is to openmode. Like mktemp, it generates a unique name for a new directory in temporary storage by appending a random string to the base name base.

Returns: Either a successful result containing the path generated (in this case, the directory now exists), or an error describing the failure. The path is allocated with std.slalloc, and must be freed by the caller using std.slfree.

const close : (fd : fd -> void)

Closes closes the file descriptor. If the file descriptor is valid, close is guaranteed to close it. If it is not valid, this is a no-op.

const read  : (fd : fd, buf : byte[:] -> result(size, errno))

Reads up to the length of buf from the file descriptor fd, at the current offset within the file, advancing the offset by the count of bytes read. The buffer may not be filled entirely when the read completes. For example, when reading from a console, often only one line will be returned at a time.

Conventionally, 0 bytes are returned at the end of the file.

Returns: Either the number of bytes read on success, or the error that caused a failure reading on failure.

const write : (fd : fd, buf : byte[:] -> result(size, errno))

Write writes up to the length of buf to the file descriptor, writing the bytes at the current offset. The offset is advanced by the number of bytes that were written, extending the file size if necessary. Write is not guaranteed to write the full buffer.

Returns: The number of bytes written on success, or the error that caused the failure on error.

const seek  : (fd : fd, delta : off, whence : whence -> result(off, errno))

Seek changes the current offset within the file descriptor fd by delta. This delta can be treated in three different ways, depending on the value of `whence.

If whence is Seekset, then delta is treated as an absolute value to seek to, and the offset is set to delta. If whence is Seekcur, then delta is added to the current offset. If whence is Seekend, then delta is added to the size of the file.

Returns: Either the new offset within the file on success, or the error that occurred on failure.

const pipe  : (fds : fd[2]# -> errno)

Pipe creates a unidirectional channel for communication between processes. Two file descriptors are returned in the array fd. Data written to fd[1] is available in fd[0]. The pipe may or may not be buffered.

Returns: Enone on success, otherwise, returns the error that caused this call to fail.

const dup2  : (ofd : fd, nfd : fd -> result(fd, errno))

Dup2 copies the old fd ofd to the new fd nfd. If the file descriptor nfd is already open, then it is implicitly closed by this call before the fd is copied. This is done atomically.

Returns: Either the new fd used, on success, or an error describing the failure.

const remove    : (path : byte[:] -> errno)

Remove removes the file specified from the directory in which it is contained. The user must have write permissions for the directory in order to remove files from it. If path is a directory, it must be empty.

Returns: Enone on success, otherwise the error that caused the failure.

Path Manipulation

const basename  : (p : byte[:] -> byte[:])
const dirname   : (p : byte[:] -> byte[:])

Given a string of the form "foo/bar/baz", dirname() returns the directory component of it -- in other words, everything up to the final /. It ignores trailing slashes. It

The caller is responsible for freeing the path with slfree.

For example, dirname("foo/bar//") will return "foo/bar".

const pathcat   : (a : byte[:], b : byte[:] -> byte[:])

Pathcat joins two paths together, using '/' as a directory separator. The paths are normalized before being returned. This call is shorthand for std.pathjoin([a, b][:]).

The caller is responsible for freeing the path with slfree.

Returns: A concatenated path.

const pathjoin  : (p : byte[:][:] -> byte[:])

Pathcat joins a list of paths together, using '/' as a directory separator. The paths are normalized before being returned. This call is shorthand for std.pathjoin([a, b][:]).

The caller is responsible for freeing the path with slfree.

Returns: A concatenated path.

const pathnorm  : (p : byte[:] -> byte[:])

Pathnorm does a purely lexical normalization of the path. It removes redundant components, doubled / characters, and similar. The returned path is equivalent to the original input path.

The caller is responsible for freeing the path with slfree.

Returns: A new normalized path.

const getcwd : (-> byte[:])

Returns the current working directory of the program. The caller is responsible for freeing the path with slfree.

Returns: A string representing the working directory.

File Properties

const fmtime    : (f : byte[:]  -> result(time, errno))

Returns either the last modification time of the file f, or the error that was encountered extracting this information.

const fsize : (f : byte[:]  -> result(off, errno))

Returns either the size in bytes of the file f, or the error that was encountered extracting this information.

const fexists   : (f : byte[:]  -> bool)

Returns true if the file is able to be stated, or false if this fails.

const fisdir    : (f : byte[:]  -> bool)

Returns true if the file is a directory that is able to be stated, or false if this fails.

Convenience Functions

const slurp : (path : byte[:] -> result(byte[:], errno))

Reads all bytes from path until the end of file.

Returns either the file data, or the failure encountered.

const fslurp : (fd : fd -> result(byte[:], errno))

Reads all bytes from the file descriptor fd until the end of file.

Returns either the file data, or the failure encountered.

const blat : (path : byte[:], buf : byte[:], perm : int64 -> bool)

Creates the file path with permissions perm, and writes as much of buf as it can into it.

Returns Enone if no errors were encountered. Otherwise, the error is returned.

const fblat : (f : fd, buf : byte[:] -> bool)

Writes as much of buf as it can into the file descriptor fd.

Returns Enone if no errors were encountered. Otherwise, the error is returned.