hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit b2f00d7ab64d1058f3f5be593563d096b4e0d710
parent 307334353f0b9af87e4cad88f1289addcb3553be
Author: Drew DeVault <sir@cmpwn.com>
Date:   Wed, 24 Feb 2021 11:02:44 -0500

fs: new module

Diffstat:
Afs/fs.ha | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Afs/types.ha | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Afs/util.ha | 35+++++++++++++++++++++++++++++++++++
3 files changed, 213 insertions(+), 0 deletions(-)

diff --git a/fs/fs.ha b/fs/fs.ha @@ -0,0 +1,51 @@ +use io; + +// Closes a filesystem. The fs cannot be used after this function is called. +export fn close(fs: *fs) void = { + match (fs.close) { + null => void, + f: *closefunc => f(fs), + }; +}; + +// Opens a file. +export fn open(fs: *fs, path: path, mode: io::mode) (*io::stream | error) = { + return match (fs.open) { + null => io::unsupported, + f: *openfunc => f(fs, path, mode), + }; +}; + +// Creates a new file. +export fn create(fs: *fs, path: path, mode: io::mode) (*io::stream | error) = { + return match (fs.create) { + null => io::unsupported, + f: *createfunc => f(fs, path, mode), + }; +}; + +// Returns an iterator for a path, which yields the contents of a directory. +// Pass empty string to yield from the root. +export fn iter(fs: *fs, path: path) (*iterator | error) = { + return match (fs.iter) { + null => io::unsupported, + f: *iterfunc => f(fs, path), + }; +}; + +// Obtains information about a file or directory. If the target is a symlink, +// information is returned about the link, not its target. +export fn stat(fs: *fs, path: path) (filestat | error) = { + return match (fs.stat) { + null => io::unsupported, + f: *statfunc => f(fs, path), + }; +}; + +// Opens a new filesystem for a subdirectory. +export fn subdir(fs: *fs, path: path) (*fs | error) = { + return match (fs.subdir) { + null => io::unsupported, + f: *subdirfunc => f(fs, path), + }; +}; diff --git a/fs/types.ha b/fs/types.ha @@ -0,0 +1,127 @@ +use io; + +// A path or path component. +export type path = (str | []u8); + +// An entry was requested which does not exist. +export type noentry = void!; + +// The user does not have permission to use this resource. +export type noaccess = void!; + +// All possible fs error types. +export type error = (noentry | noaccess | io::error)!; + +// File mode information. These bits do not necessarily reflect the underlying +// operating system's mode representation, though they were chosen to be +// consistent with typical Unix file permissions. All implementations shall +// support at least OWNER_RW, DIR, and REG. +export type mode = enum uint { + // Read, write, and execute permissions for the file owner + OWNER_RWX = 0o700, + // Read and write permissions for the file owner + OWNER_RW = 0o500, + // Read permissions for the file owner + OWNER_R = 0o400, + // Write permissions for the file owner + OWNER_W = 0o200, + // Execute permissions for the file owner + OWNER_X = 0o100, + + // Read, write, and execute permissions for group members + GROUP_RWX = 0o070, + // Read and write permissions for group members + GROUP_RW = 0o050, + // Read permissions for group members + GROUP_R = 0o040, + // Write permissions for group members + GROUP_W = 0o020, + // Execute permissions for group members + GROUP_X = 0o010, + + // Read, write, and execute permissions for other users + OTHER_RWX = 0o007, + // Read and write permissions for other users + OTHER_RW = 0o005, + // Read permissions for other users + OTHER_R = 0o004, + // Write permissions for other users + OTHER_W = 0o002, + // Execute permissions for other users + OTHER_X = 0o001, + + // Entry has the set-uid bit set + SETUID = 0o4000, + // Entry has the set-gid bit set + SETGID = 0o2000, + // Entry has the sticky bit set + STICKY = 0o1000, + + // Entry is a FIFO (named pipe) + FIFO = 0o010000, + // Entry is a directory + DIR = 0o040000, + // Entry is a character device + CHR = 0o020000, + // Entry is a block device + BLK = 0o060000, + // Entry is a regular file + REG = 0o100000, + // Entry is a symbolic link + LINK = 0o120000, + // Entry is a Unix socket + SOCK = 0o140000, +}; + +// A mask defining what items are populated in the stat structure. +export type stat_mask = enum uint { + UID = 1 << 0, + GID = 1 << 1, + SIZE = 1 << 2, +}; + +// Information about a file or directory. The mask field defines what other +// fields are set; mode is always set. +export type filestat = struct { + mask: stat_mask, + mode: mode, + uid: uint, + gid: uint, + sz: size, + // TODO: atime et al +}; + +export type closefunc = fn(fs: *fs) void; +export type openfunc = fn(fs: *fs, path: path, mode: io::mode) (*io::stream | error); +export type createfunc = fn(fs: *fs, path: path, mode: io::mode) (*io::stream | error); +export type iterfunc = fn(fs: *fs, path: path) (*iterator | error); +export type statfunc = fn(fs: *fs, path: path) (filestat | error); +export type subdirfunc = fn(fs: *fs, path: path) (*fs | error); + +// An abstract implementation of a filesystem. +export type fs = struct { + // Frees resources associated with this filesystem. + close: nullable *closefunc, + + // Opens a file. + open: nullable *openfunc, + + // Creates a new file. + create: nullable *createfunc, + + // Returns an iterator for a path, which yields the contents of a + // directory. Pass empty string to yield from the root. + iter: nullable *iterfunc, + + // Obtains information about a file or directory. If the target is a + // symlink, information is returned about the link, not its target. + stat: nullable *statfunc, + + // Opens a new filesystem for a subdirectory. + subdir: nullable *subdirfunc, +}; + +// TODO +export type iterator = struct { + placeholder: int, +}; diff --git a/fs/util.ha b/fs/util.ha @@ -0,0 +1,35 @@ +use io; + +// Returns a human-friendly representation of an error. +export fn errstr(err: error) const str = match (err) { + noentry => "File or directory not found", + noaccess => "Permission denied", + err: io::error => io::errstr(err), +}; + +// Converts a mode into a Unix-like mode string (e.g. "-rw-r--r--") +export fn modestr(mode: mode) const str = { + // TODO: blocked on bufio::fixed + abort(); +}; + +// Returns true if this item is a regular file. +export fn is_file(mode: mode) bool = mode & mode::REG == mode::REG; + +// Returns true if this item is a FIFO (named pipe). +export fn is_fifo(mode: mode) bool = mode & mode::FIFO == mode::FIFO; + +// Returns true if this item is a directory. +export fn is_dir(mode: mode) bool = mode & mode::DIR == mode::DIR; + +// Returns true if this item is a character device. +export fn is_chdev(mode: mode) bool = mode & mode::CHR == mode::CHR; + +// Returns true if this item is a block device. +export fn is_blockdev(mode: mode) bool = mode & mode::BLK == mode::BLK; + +// Returns true if this item is a symbolic link. +export fn is_link(mode: mode) bool = mode & mode::LINK == mode::LINK; + +// Returns true if this item is a Unix socket. +export fn is_socket(mode: mode) bool = mode & mode::SOCK == mode::SOCK;