~sircmpwn/hare

ec8cd146e8397e68375365c2c248375080ee2d4c — Drew DeVault 7 months ago a28acf4
all: docs: change [ref] to [[ref]]

The following script:

	#!/bin/sh
	echo $1
	awk '/^\/\/.*/ { gsub(/\[[a-zA-Z0-9_:]+\]/, "[&]") } { print $0 }' < $1 > $1.2
	mv $1.2 $1

Was used like so:

git ls-tree -r HEAD | awk '{ print $4 }' | grep '.ha$' | xargs -n1 ./edit

And then the results were touched up by hand.
M ascii/strcmp.ha => ascii/strcmp.ha +1 -1
@@ 27,7 27,7 @@ export fn strcmp(a: str, b: str) (int | void) = {
	};
};

// Similar to [strcmp], but compares strings irrespective of their uppercase or
// Similar to [[strcmp]], but compares strings irrespective of their uppercase or
// lowercase classification within the ASCII character set.
export fn strcasecmp(a: str, b: str) (int | void) = {
	let a = strings::iter(a), b = strings::iter(b);

M bufio/buffered.ha => bufio/buffered.ha +1 -1
@@ 113,7 113,7 @@ export fn unread(s: *io::stream, buf: []u8) void = {
	append(s.unread, buf...);
};

// Unreads a rune; see [unread].
// Unreads a rune; see [[unread]].
export fn unreadrune(s: *io::stream, rn: rune) void = {
	assert(isbuffered(s), "bufio: unread used on non-buffered stream");
	let s = s: *bufstream;

M bufio/memstream.ha => bufio/memstream.ha +6 -6
@@ 9,7 9,7 @@ type memstream = struct {
	pos: size,
};

// Creates an [io::stream] for a fixed, caller-supplied buffer. Supports either
// Creates an [[io::stream]] for a fixed, caller-supplied buffer. Supports either
// read or write, but not both. Readable streams are seekable. The program
// aborts if writes would exceed the buffer's capacity.
export fn fixed(in: []u8, mode: io::mode) *io::stream = {


@@ 48,19 48,19 @@ fn fixed_write(s: *io::stream, buf: const []u8) (size | io::error) = {

fn fixed_close(s: *io::stream) void = free(s);

// Creates an [io::stream] which dynamically allocates a buffer to store writes
// Creates an [[io::stream]] which dynamically allocates a buffer to store writes
// into. Subsequent reads will consume the buffered data. Upon failure to
// allocate sufficient memory to store writes, the program aborts.
//
// Calling [io::close] on this stream will free the buffer. Call [bufio::finish]
// Calling [[io::close]] on this stream will free the buffer. Call [[bufio::finish]]
// instead to free up resources associated with the stream, but transfer
// ownership of the buffer to the caller.
export fn dynamic(mode: io::mode) *io::stream = dynamic_from([], mode);

// Like [dynamic], but takes an existing slice as input. Writes are appended to
// Like [[dynamic]], but takes an existing slice as input. Writes are appended to
// it and reads consume bytes from the initial buffer, plus any additional
// writes. Like [dynamic], calling [io::close] will free the buffer, and
// [bufio::finish] can be used to return ownership of the buffer to the caller.
// writes. Like [[dynamic]], calling [[io::close]] will free the buffer, and
// [[bufio::finish]] can be used to return ownership of the buffer to the caller.
export fn dynamic_from(in: []u8, mode: io::mode) *io::stream = {
	let s = alloc(memstream {
		stream = io::stream {

M cmd/haredoc/hare.ha => cmd/haredoc/hare.ha +1 -1
@@ 36,7 36,7 @@ fn details_hare(decl: ast::decl) (void | error) = {
	return;
};

// Forked from [[hare::unparse]]
// Forked from [[[hare::unparse]]]
fn unparse_hare(out: *io::stream, d: ast::decl) (size | io::error) = {
	let n = 0z;
	match (d.decl) {

M cmd/haredoc/html.ha => cmd/haredoc/html.ha +1 -1
@@ 136,7 136,7 @@ fn details(decl: ast::decl) (void | error) = {
	return;
};

// Forked from [[hare::unparse]]
// Forked from [[[hare::unparse]]]
fn unparse_html(out: *io::stream, d: ast::decl) (size | io::error) = {
	// TODO: Syntax highlighting
	let n = 0z;

M compress/flate/inflate.ha => compress/flate/inflate.ha +1 -1
@@ 97,7 97,7 @@ export fn inflate(s: *io::stream) *io::stream = alloc(decompressor {
	bufstart = [],
}): *io::stream;

// Read [want] bits from the decompressor
// Read [[want]] bits from the decompressor
fn bits(d: *decompressor, want: u32) (u32 | io::error) = {
	assert(want <= 32);
	let val = d.bitbuf;

M crypto/math/bits.ha => crypto/math/bits.ha +4 -4
@@ 1,5 1,5 @@
// Rotates a 32-bit unsigned integer left by k bits. k may be negative to rotate
// right instead, or see [rotr32].
// right instead, or see [[rotr32]].
export fn rotl32(x: u32, k: int) u32 = {
	const n = 32u32;
	const s = k: u32 & (n - 1);


@@ 7,7 7,7 @@ export fn rotl32(x: u32, k: int) u32 = {
};

// Rotates a 32-bit unsigned integer right by k bits. k may be negative to
// rotate left instead, or see [rotl32].
// rotate left instead, or see [[rotl32]].
export fn rotr32(x: u32, k: int) u32 = rotl32(x, -k);

@test fn lrot32() void = {


@@ 19,7 19,7 @@ export fn rotr32(x: u32, k: int) u32 = rotl32(x, -k);
};

// Rotates a 64-bit unsigned integer left by k bits. k may be negative to rotate
// right instead, or see [rotr64].
// right instead, or see [[rotr64]].
export fn rotl64(x: u64, k: int) u64 = {
	const n = 64u64;
	const s = k: u64 & (n - 1);


@@ 27,7 27,7 @@ export fn rotl64(x: u64, k: int) u64 = {
};

// Rotates a 64-bit unsigned integer right by k bits. k may be negative to rotate
// left instead, or see [rotl64].
// left instead, or see [[rotl64]].
export fn rotr64(x: u64, k: int) u64 = rotl64(x, -k);

@test fn lrot64() void = {

M crypto/md5/md5.ha => crypto/md5/md5.ha +3 -3
@@ 20,10 20,10 @@ type digest = struct {
	ln: size,
};

// Creates a [hash::hash] which computes a MD5 hash as defined in RFC 1321. Note
// Creates a [[hash::hash]] which computes a MD5 hash as defined in RFC 1321. Note
// that MD5 is cryptographically broken and should not be used for secure
// applications. Where possible, applications are encouraged to use [sha256] or
// [sha512] instead.
// applications. Where possible, applications are encouraged to use [[sha256]] or
// [[sha512]] instead.
export fn md5() *hash::hash = {
	let md5 = alloc(digest {
		hash = hash::hash {

M crypto/random/+linux.ha => crypto/random/+linux.ha +1 -1
@@ 5,7 5,7 @@ use io;
// Fills the given buffer with cryptographically random data. If the system is
// unable to provide random data, abort. If you need to handle errors or want to
// use whatever random data the system can provide, even if less than the
// requested amont, use [stream] instead.
// requested amont, use [[stream]] instead.
export fn buffer(buf: []u8) void = {
	let n = 0z;
	for (n < len(buf)) {

M crypto/random/random.ha => crypto/random/random.ha +1 -1
@@ 7,7 7,7 @@ let _stream: io::stream = io::stream {
	...
};

// An [io::stream] which returns cryptographically random data on reads. Be
// An [[io::stream]] which returns cryptographically random data on reads. Be
// aware, it may return less than you asked for!
export let stream: *io::stream = &_stream;


M crypto/sha1/sha1.ha => crypto/sha1/sha1.ha +2 -2
@@ 23,9 23,9 @@ type digest = struct {
	ln: size,
};

// Creates a [hash::hash] which computes a SHA-1 hash. Note that this alogorithm
// Creates a [[hash::hash]] which computes a SHA-1 hash. Note that this alogorithm
// is no longer considered secure. Where possible, applications are encouraged
// to use [sha256] or [sha512] instead.
// to use [[sha256]] or [[sha512]] instead.
export fn sha1() *hash::hash = {
	let sha = alloc(digest {
		hash = hash::hash {

M crypto/sha256/sha256.ha => crypto/sha256/sha256.ha +1 -1
@@ 39,7 39,7 @@ type state = struct {
	ln: size,
};

// Creates a [hash::hash] which computes a SHA-256 hash.
// Creates a [[hash::hash]] which computes a SHA-256 hash.
export fn sha256() *hash::hash = {
	let sha = alloc(state {
		hash = hash::hash {

M crypto/sha512/sha512.ha => crypto/sha512/sha512.ha +4 -4
@@ 68,16 68,16 @@ type digest = struct {
	var: variant,
};

// Creates a [hash::hash] which computes a SHA-512 hash.
// Creates a [[hash::hash]] which computes a SHA-512 hash.
export fn sha512() *hash::hash = init(variant::SHA512, SIZE);

// Creates a [hash::hash] which computes a SHA-512/224 hash.
// Creates a [[hash::hash]] which computes a SHA-512/224 hash.
export fn sha512_224() *hash::hash = init(variant::SHA512_224, SIZE224);

// Creates a [hash::hash] which computes a SHA-512/256 hash.
// Creates a [[hash::hash]] which computes a SHA-512/256 hash.
export fn sha512_256() *hash::hash = init(variant::SHA512_256, SIZE256);

// Creates a [hash::hash] which computes a SHA-384 hash.
// Creates a [[hash::hash]] which computes a SHA-384 hash.
export fn sha384() *hash::hash = init(variant::SHA384, SIZE384);

// Internal initialization function

M dirs/xdg.ha => dirs/xdg.ha +3 -3
@@ 32,7 32,7 @@ fn lookup(prog: str, var: str, default: str) str = {
// unique path for this program to store data will be returned.
export fn config(prog: str) str = lookup(prog, "XDG_CONFIG_HOME", ".config");

// Returns an [fs::fs] for storing config files. If 'prog' is given, a unique
// Returns an [[fs::fs]] for storing config files. If 'prog' is given, a unique
// path for this program to store data will be returned.
export fn configfs(prog: str) *fs::fs = os::diropen(config(prog)) as *fs::fs;



@@ 40,7 40,7 @@ export fn configfs(prog: str) *fs::fs = os::diropen(config(prog)) as *fs::fs;
// path for this program to store data will be returned.
export fn cache(prog: str) str = lookup(prog, "XDG_CACHE_HOME", ".cache");

// Returns an [fs::fs] for cache files. If 'prog' is given, a unique path for
// Returns an [[fs::fs]] for cache files. If 'prog' is given, a unique path for
// this program to store data will be returned.
export fn cachefs(prog: str) *fs::fs = os::diropen(cache(prog)) as *fs::fs;



@@ 49,6 49,6 @@ export fn cachefs(prog: str) *fs::fs = os::diropen(cache(prog)) as *fs::fs;
export fn data(prog: str) str =
	lookup(prog, "XDG_DATA_HOME", path::join(".local", "share"));

// Returns an [fs::fs] for persistent data files. If 'prog' is given, a unique
// Returns an [[fs::fs]] for persistent data files. If 'prog' is given, a unique
// path for this program to store data will be returned.
export fn datafs(prog: str) *fs::fs = os::diropen(data(prog)) as *fs::fs;

M endian/host+aarch64.ha => endian/host+aarch64.ha +1 -1
@@ 1,2 1,2 @@
// The [endian] functions which map to the host architecture.
// The [[endian]] functions which map to the host architecture.
export const host: *endian = &little;

M endian/host+x86_64.ha => endian/host+x86_64.ha +1 -1
@@ 1,2 1,2 @@
// The [endian] functions which map to the host architecture.
// The [[endian]] functions which map to the host architecture.
export const host: *endian = &little;

M errors/rt.ha => errors/rt.ha +1 -1
@@ 1,6 1,6 @@
use rt;

// Wraps an [rt::errno] to produce an [errors::opaque]. This is a non-portable
// Wraps an [[rt::errno]] to produce an [[errors::opaque]]. This is a non-portable
// interface which is mainly provided to support internal stdlib requirements.
export fn errno(errno: rt::errno) opaque = {
	static assert(size(rt::errno) <= size(opaque_data));

M errors/string.ha => errors/string.ha +2 -2
@@ 1,7 1,7 @@
// Converts an [error] into a human-friendly string representation.
// Converts an [[error]] into a human-friendly string representation.
//
// Note that this strerror implementation lacks any context-specific information
// about the error types supported. For example, [exists] is stringified as "An
// about the error types supported. For example, [[exists]] is stringified as "An
// attempt was made to create a resource which already exists", but if source of
// the error is, say, creating a file, it would likely be more appropriate to
// use the term "file" rather than "resource". For this reason, it is preferred

M fmt/fmt.ha => fmt/fmt.ha +13 -13
@@ 49,20 49,20 @@ use types;
export type formattable =
	(...types::numeric | uintptr | str | rune | bool | nullable *void);

// Formats text for printing and writes it to [os::stdout].
// Formats text for printing and writes it to [[os::stdout]].
export fn printf(fmt: str, args: formattable...) (io::error | size) =
	fprintf(os::stdout, fmt, args...);

// Formats text for printing and writes it to [os::stdout], followed by a line
// Formats text for printing and writes it to [[os::stdout]], followed by a line
// feed.
export fn printfln(fmt: str, args: formattable...) (io::error | size) =
	fprintfln(os::stdout, fmt, args...);

// Formats text for printing and writes it to [os::stderr].
// Formats text for printing and writes it to [[os::stderr]].
export fn errorf(fmt: str, args: formattable...) (io::error | size) =
	fprintf(os::stderr, fmt, args...);

// Formats text for printing and writes it to [os::stderr], followed by a line
// Formats text for printing and writes it to [[os::stderr]], followed by a line
// feed.
export fn errorfln(fmt: str, args: formattable...) (io::error | size) =
	fprintfln(os::stderr, fmt, args...);


@@ 84,14 84,14 @@ export fn bsprintf(buf: []u8, fmt: str, args: formattable...) str = {
	return strings::fromutf8_unsafe(buf[..l]);
};

// Formats text for printing and writes it to [os::stderr], followed by a line
// Formats text for printing and writes it to [[os::stderr]], followed by a line
// feed, then exits the program with an error status.
export @noreturn fn fatal(fmt: str, args: formattable...) void = {
	fprintfln(os::stderr, fmt, args...);
	os::exit(1);
};

// Formats text for printing and writes it to an [io::stream], followed by a
// Formats text for printing and writes it to an [[io::stream]], followed by a
// line feed.
export fn fprintfln(
	s: *io::stream,


@@ 102,22 102,22 @@ export fn fprintfln(
};

// Formats values for printing using the default format modifiers and writes
// them to [os::stdout] separated by spaces 
// them to [[os::stdout]] separated by spaces
export fn print(args: formattable...) (io::error | size) =
	fprint(os::stdout, args...);

// Formats values for printing using the default format modifiers and writes
// them to [os::stdout] separated by spaces and followed by a line feed
// them to [[os::stdout]] separated by spaces and followed by a line feed
export fn println(args: formattable...) (io::error | size) =
	fprintln(os::stdout, args...);

// Formats values for printing using the default format modifiers and writes
// them to [os::stderr] separated by spaces 
// them to [[os::stderr]] separated by spaces
export fn error(args: formattable...) (io::error | size) =
	fprint(os::stderr, args...);

// Formats values for printing using the default format modifiers and writes
// them to [os::stderr] separated by spaces and followed by a line feed
// them to [[os::stderr]] separated by spaces and followed by a line feed
export fn errorln(args: formattable...) (io::error | size) =
	fprintln(os::stderr, args...);



@@ 141,13 141,13 @@ export fn bsprint(buf: []u8, args: formattable...) str = {
};

// Formats values for printing using the default format modifiers and writes
// them to an [io::stream] separated by spaces and followed by a line feed
// them to an [[io::stream]] separated by spaces and followed by a line feed
export fn fprintln(s: *io::stream, args: formattable...) (io::error | size) = {
	return fprint(s, args...)? + io::write(s, ['\n': u32: u8])?;
};

// Formats values for printing using the default format modifiers and writes
// them to an [io::stream] separated by spaces
// them to an [[io::stream]] separated by spaces
export fn fprint(s: *io::stream, args: formattable...) (io::error | size) = {
	let mod = modifiers { base = strconv::base::DEC, ... };
	let n = 0z;


@@ 188,7 188,7 @@ type modflags = enum uint {
	PLUS  = 1 << 3,
};

// Formats text for printing and writes it to an [io::stream].
// Formats text for printing and writes it to an [[io::stream]].
export fn fprintf(
	s: *io::stream,
	fmt: str,

M format/xml/parser.ha => format/xml/parser.ha +3 -3
@@ 11,7 11,7 @@ use strings;
use strio;

// Returns an XML parser which reads from a stream. The caller must call
// [parser_free] when they are finished with it.
// [[parser_free]] when they are finished with it.
//
// Hare's XML parser only supports UTF-8 encoded input files.
//


@@ 52,8 52,8 @@ export fn parser_free(par: *parser) void = {
	free(par);
};

// Scans for and returns the next [token]. Tokens are borrowed from the parser
// and are not valid on subsequent calls to [scan]; use [token_dup] to extend
// Scans for and returns the next [[token]]. Tokens are borrowed from the parser
// and are not valid on subsequent calls to [[scan]]; use [[token_dup]] to extend
// their lifetime.
export fn scan(par: *parser) (token | void | error) = {
	switch (par.state) {

M format/xml/types.ha => format/xml/types.ha +1 -1
@@ 42,7 42,7 @@ export type syntaxerr = void!; // TODO: Add line number?
// Any error which can occur during XML parsing.
export type error = (syntaxerr | utf8::invalid | io::error)!;

// Converts an [error] to a user-friendly string representation.
// Converts an [[error]] to a user-friendly string representation.
export fn strerror(err: error) const str = {
	return match (err) {
		syntaxerr => "Syntax error",

M fs/fs.ha => fs/fs.ha +2 -2
@@ 95,7 95,7 @@ export fn mkdirs(fs: *fs, path: str) (void | error) = {
	return mkdir(fs, path);
};

// Removes a directory. The target directory must be empty; see [rmdirall] to
// Removes a directory. The target directory must be empty; see [[rmdirall]] to
// remove its contents as well.
export fn rmdir(fs: *fs, path: str) (void | error) = {
	if (path == "") {


@@ 173,5 173,5 @@ export fn resolve(fs: *fs, path: str) str = {
// Returns the next directory entry from an interator, or void if none remain.
// It is a programming error to call this again after it has returned void. The
// file stat returned may only have the type bits set on the file mode; callers
// should call [fs::stat] to obtain the detailed file mode.
// should call [[fs::stat]] to obtain the detailed file mode.
export fn next(iter: *iterator) (dirent | void) = iter.next(iter);

M fs/types.ha => fs/types.ha +5 -5
@@ 5,7 5,7 @@ use strings;
use time;

// An entry of a particular type was sought, but is something else in practice.
// For example, opening a file with [iter].
// For example, opening a file with [[iter]].
export type wrongtype = void!;

// All possible fs error types.


@@ 113,8 113,8 @@ export type filestat = struct {
};

// An entry in a directory. This may be borrowed from the filesystem's internal
// state; if you want to keep this around beyond one call to [next], use
// [dirent_dup].
// state; if you want to keep this around beyond one call to [[next]], use
// [[dirent_dup]].
export type dirent = struct {
	// The name of this entry. Not fully qualified: for example,
	// "foo/bar/baz.txt" would store "baz.txt" here.


@@ 124,14 124,14 @@ export type dirent = struct {
	ftype: mode,
};

// Duplicates a [dirent] object. Call [dirent_free] to get rid of it later.
// Duplicates a [[dirent]] object. Call [[dirent_free]] to get rid of it later.
export fn dirent_dup(e: *dirent) dirent = {
	let new = *e;
	new.name = strings::dup(e.name);
	return new;
};

// Frees a [dirent] object which was duplicated with [dirent_dup].
// Frees a [[dirent]] object which was duplicated with [[dirent_dup]].
export fn dirent_free(e: *dirent) void = free(e.name);

// Flags to use for opening a file. Not all operating systems support all flags;

M fs/util.ha => fs/util.ha +3 -3
@@ 13,7 13,7 @@ export fn strerror(err: error) const str = match (err) {
};

// Converts a mode into a Unix-like mode string (e.g. "-rw-r--r--"). The string
// is statically allocated, use [strings::dup] to duplicate it or it will be
// is statically allocated, use [[strings::dup]] to duplicate it or it will be
// overwritten on subsequent calls.
export fn mode_str(m: mode) const str = {
	static let buf: [10]u8 = [0...];


@@ 80,7 80,7 @@ export fn is_link(mode: mode) bool = mode & mode::LINK == mode::LINK;
export fn is_socket(mode: mode) bool = mode & mode::SOCK == mode::SOCK;

// Reads all entries from a directory. The caller must free the return value
// with [dirents_free].
// with [[dirents_free]].
export fn readdir(fs: *fs, path: str) ([]dirent | error) = {
	let i = iter(fs, path)?;
	let ents: []dirent = [];


@@ 93,7 93,7 @@ export fn readdir(fs: *fs, path: str) ([]dirent | error) = {
	return ents;
};

// Frees a slice of [dirent]s.
// Frees a slice of [[dirent]]s.
export fn dirents_free(d: []dirent) void = {
	for (let i = 0z; i < len(d); i += 1) {
		dirent_free(&d[i]);

M getopt/getopts.ha => getopt/getopts.ha +9 -9
@@ 58,16 58,16 @@ export type help = (cmd_help | flag_help | parameter_help);

// Parses command line arguments and returns a tuple of the options specified,
// and the remaining arguments. If an error occurs, details are printed to
// [os::stderr] and [os::exit] is called with a nonzero exit status. The
// argument list must include the command name as the first item; [os::args]
// [[os::stderr]] and [[os::exit]] is called with a nonzero exit status. The
// argument list must include the command name as the first item; [[os::args]]
// fulfills this criteria.
//
// The caller provides [help] arguments to specify which command line flags and
// The caller provides [[help]] arguments to specify which command line flags and
// parameters are supported, and to provide some brief help text which describes
// their use. Provide [flag_help] to add a flag which does not take a parameter,
// and [parameter_help] to add a flag with a required parameter. The first
// [cmd_help] is used as a short, one-line summary of the command's purpose, and
// any later [cmd_help] arguments are used to provide the name of any arguments
// their use. Provide [[flag_help]] to add a flag which does not take a parameter,
// and [[parameter_help]] to add a flag with a required parameter. The first
// [[cmd_help]] is used as a short, one-line summary of the command's purpose, and
// any later [[cmd_help]] arguments are used to provide the name of any arguments
// which follow the options list.
//
// By convention, the caller should sort the list of options, first providing


@@ 105,7 105,7 @@ export type help = (cmd_help | flag_help | parameter_help);
//
// If "-h" is not among the options defined by the caller, the "-h" option will
// will cause a summary of the command usage to be printed to stderr, and
// [os::exit] will be called with a successful exit status.
// [[os::exit]] will be called with a successful exit status.
export fn parse(args: []str, help: help...) command = {
	let opts: []option = [];
	let i = 1z;


@@ 172,7 172,7 @@ export fn parse(args: []str, help: help...) command = {
	};
};

// Frees resources associated with the return value of [parse].
// Frees resources associated with the return value of [[parse]].
export fn finish(cmd: *command) void = {
	if (cmd == null) return;
	free(cmd.opts);

M hare/lex/lex.ha => hare/lex/lex.ha +3 -3
@@ 60,7 60,7 @@ export fn init(in: *io::stream, path: str, flags: flags...) lexer = {
};

// Returns the current value of the comment buffer, or empty string if unset (or
// if [flags::COMMENTS] was not enabled for this lexer).
// if [[flags::COMMENTS]] was not enabled for this lexer).
export fn comment(lex: *lexer) str = lex.comment;

// Returns the next token from the lexer.


@@ 636,8 636,8 @@ fn lex3gt(lex: *lexer, loc: location, n: rune) (token | error) = {
	return (tok, void, loc);
};

// Unlex a single token. The next call to [lex] will return this token. Only one
// unlex is supported at a time; you must call [lex] before calling [unlex]
// Unlex a single token. The next call to [[lex]] will return this token. Only one
// unlex is supported at a time; you must call [[lex]] before calling [[unlex]]
// again.
export fn unlex(lex: *lexer, tok: token) void = {
	assert(lex.un is void, "attempted to unlex more than one token");

M hare/module/scan.ha => hare/module/scan.ha +3 -3
@@ 16,7 16,7 @@ use strio;
use fmt;

// Scans the files in a directory for eligible build inputs and returns a
// [version] which includes all applicable files and their dependencies.
// [[version]] which includes all applicable files and their dependencies.
export fn scan(ctx: *context, path: str) (version | error) = {
	// TODO: Incorporate defines into the hash
	let sha = sha256::sha256();


@@ 263,7 263,7 @@ fn scan_directory(
	};
};

// Looks up a module by its identifier from HAREPATH, and returns a [version]
// Looks up a module by its identifier from HAREPATH, and returns a [[version]]
// which includes all eligible build inputs.
export fn lookup(ctx: *context, name: ast::ident) (version | error) = {
	let ipath = identpath(name);


@@ 329,7 329,7 @@ fn have_ident(sl: *[]ast::ident, id: ast::ident) bool = {
};

// Parses a set of build tags, returning void if the string is an invalid tag
// set. The caller must free the return value with [tags_free].
// set. The caller must free the return value with [[tags_free]].
export fn parsetags(in: str) ([]tag | void) = {
	let tags: []tag = [];
	// defer! tags_free(tags);

M hare/unparse/expr.ha => hare/unparse/expr.ha +1 -1
@@ 3,7 3,7 @@ use fmt;
use hare::ast;
use hare::lex;

// Unparses an [ast::expr].
// Unparses an [[ast::expr]].
//
// A known limitation of the current implementation is that precedence between
// binary operators (e.g. +) is not accounted for, so such expressions may

M hare/unparse/import.ha => hare/unparse/import.ha +1 -1
@@ 3,7 3,7 @@ use io;
use hare::ast;
use strio;

// Unparses an [ast::import].
// Unparses an [[ast::import]].
export fn import(out: *io::stream, i: ast::import) (size | io::error) = {
	let n = 0z;
	n += fmt::fprint(out, "use ")?;

M hare/unparse/type.ha => hare/unparse/type.ha +1 -1
@@ 103,7 103,7 @@ fn struct_union_type(
	return z;
};

// Unparses an [ast::_type].
// Unparses an [[ast::_type]].
export fn _type(
	out: *io::stream,
	indent: size,

M hare/unparse/unit.ha => hare/unparse/unit.ha +1 -1
@@ 2,7 2,7 @@ use io;
use fmt;
use hare::ast;

// Unparses an [ast::subunit].
// Unparses an [[ast::subunit]].
export fn subunit(out: *io::stream, s: ast::subunit) (size | io::error) = {
	let n = 0z;
	for (let i = 0z; i < len(s.imports); i += 1) {

M hash/adler32/adler32.ha => hash/adler32/adler32.ha +1 -1
@@ 9,7 9,7 @@ type state = struct {
	b: u32,
};

// Creates a [hash::hash] which computes the Adler-32 checksum algorithm.
// Creates a [[hash::hash]] which computes the Adler-32 checksum algorithm.
export fn adler32() *hash::hash = alloc(state {
	hash = hash::hash {
		stream = io::stream {

M hash/fnv/fnv.ha => hash/fnv/fnv.ha +6 -6
@@ 18,9 18,9 @@ type state64 = struct {
	v: u64,
};

// Creates a [hash::hash] which computes the FNV-1 32-bit hash function.
// Creates a [[hash::hash]] which computes the FNV-1 32-bit hash function.
//
// Unless you have a reason to use this, [fnv32a] is recommended instead.
// Unless you have a reason to use this, [[fnv32a]] is recommended instead.
export fn fnv32() *hash::hash = alloc(state32 {
	hash = hash::hash {
		stream = io::stream {


@@ 34,7 34,7 @@ export fn fnv32() *hash::hash = alloc(state32 {
	v = basis32,
}): *hash::hash;

// Creates a [hash::hash] which computes the FNV-1a 32-bit hash function.
// Creates a [[hash::hash]] which computes the FNV-1a 32-bit hash function.
export fn fnv32a() *hash::hash = alloc(state32 {
	hash = hash::hash {
		stream = io::stream {


@@ 48,9 48,9 @@ export fn fnv32a() *hash::hash = alloc(state32 {
	v = basis32,
}): *hash::hash;

// Creates a [hash::hash] which computes the FNV-1 64-bit hash function.
// Creates a [[hash::hash]] which computes the FNV-1 64-bit hash function.
//
// Unless you have a reason to use this, [fnv64a] is recommended instead.
// Unless you have a reason to use this, [[fnv64a]] is recommended instead.
export fn fnv64() *hash::hash = alloc(state64 {
	hash = hash::hash {
		stream = io::stream {


@@ 64,7 64,7 @@ export fn fnv64() *hash::hash = alloc(state64 {
	v = basis64,
}): *hash::hash;

// Creates a [hash::hash] which computes the FNV-1a 64-bit hash function.
// Creates a [[hash::hash]] which computes the FNV-1a 64-bit hash function.
export fn fnv64a() *hash::hash = alloc(state64 {
	hash = hash::hash {
		stream = io::stream {

M hash/hash.ha => hash/hash.ha +1 -1
@@ 16,7 16,7 @@ export type hash = struct {
	sz: size,
};

// Returns a writable [io::stream] for a given hash.
// Returns a writable [[io::stream]] for a given hash.
export fn writer(h: *hash) *io::stream = &h.stream;

// Writes an input to the hash function.

M io/println+linux.ha => io/println+linux.ha +2 -2
@@ 3,7 3,7 @@ use rt;
// Prints strings to stdout, separated by spaces, and followed by a newline.
//
// The output is unbuffered, and may not have good performance. This is only
// recommended for debugging purposes. See [fmt::println] instead.
// recommended for debugging purposes. See [[fmt::println]] instead.
export fn println(msgs: str...) void = {
	for (let i = 0z; i < len(msgs); i += 1) {
		let msg = msgs[i];


@@ 20,7 20,7 @@ export fn println(msgs: str...) void = {
// Prints strings to stderr, separated by spaces, and followed by a newline.
//
// The output is unbuffered, and may not have good performance. This is only
// recommended for debugging purposes. See [fmt::errorln] instead.
// recommended for debugging purposes. See [[fmt::errorln]] instead.
export fn errorln(msgs: str...) void = {
	for (let i = 0z; i < len(msgs); i += 1) {
		let msg = msgs[i];

M io/stream.ha => io/stream.ha +1 -1
@@ 87,7 87,7 @@ let _empty: io::stream = io::stream {
	...
};

// A [stream] which always reads EOF and discards any writes.
// A [[stream]] which always reads EOF and discards any writes.
export let empty: *io::stream = &_empty;

fn empty_read(s: *stream, buf: []u8) (size | EOF | error) = EOF;

M io/types.ha => io/types.ha +3 -3
@@ 6,7 6,7 @@ export type error = (errors::unsupported | errors::opaque)!;
// Indicates an end-of-file condition.
export type EOF = void;

// Converts an I/O [error] into a user-friendly string.
// Converts an I/O [[error]] into a user-friendly string.
export fn strerror(err: error) str = errors::strerror(err);

// Used to indicate if a stream should be used for reading, or writing, or both.


@@ 40,13 40,13 @@ export type closer = fn(s: *stream) void;

// The interface for a stream which has first-class support for copying data
// from another stream. Often this only works if the second stream is of the
// same underlying stream type. This is optional, [io::copy] still works even
// same underlying stream type. This is optional, [[io::copy]] still works even
// with a stream which does not implement this (it falls back to calling read
// and write in a loop).
//
// Returns the number of bytes copied, or an error if one occured. Do not close
// either stream. If the operation is unsupported for this particular pair of
// streams, return [errors::unsupported] to have [io::copy] proceed with its
// streams, return [[errors::unsupported]] to have [[io::copy]] proceed with its
// fallback implementation.
export type copier = fn(to: *stream, from: *stream) (size | error);


M net/ip/ip.ha => net/ip/ip.ha +4 -4
@@ 32,7 32,7 @@ export const ANY_V6: addr6 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// Invalid parse result.
export type invalid = void!;

// Test if two [addr]s are equal.
// Test if two [[addr]]s are equal.
export fn equal(l: addr, r: addr) bool = {
	return match (l) {
		l: addr4 => {


@@ 299,7 299,7 @@ fn fmtsubnet(s: *io::stream, subnet: subnet) (io::error | size) = {
	return ret;
};

// Formats an [addr] or [subnet] and prints it to a stream.
// Formats an [[addr]] or [[subnet]] and prints it to a stream.
export fn fmt(s: *io::stream, item: (...addr | subnet)) (io::error | size) = {
	return match (item) {
		v4: addr4 => fmtv4(s, v4)?,


@@ 308,8 308,8 @@ export fn fmt(s: *io::stream, item: (...addr | subnet)) (io::error | size) = {
	};
};

// Formats an [addr] or [subnet] as a string. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// Formats an [[addr]] or [[subnet]] as a string. The return value is statically
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// extend its lifetime.
export fn string(item: (...addr | subnet)) str = {
	// Maximum length of an IPv6 address plus its netmask in hexadecimal

M net/socket.ha => net/socket.ha +5 -5
@@ 15,12 15,12 @@ export type reuseaddr = void;
// default (10) is used.
export type backlog = u32;

// To have the system select an arbitrary unused port for [listen], set port to
// To have the system select an arbitrary unused port for [[listen]], set port to
// zero. To retrieve the assigned port, provide this as one of the options and
// the addressed u16 will be filled in with the port.
export type portassignment = *u16;

// Options for the [listen] family of functions.
// Options for the [[listen]] family of functions.
export type listen_option = (
	keepalive |
	reuseport |


@@ 28,13 28,13 @@ export type listen_option = (
	backlog |
	portassignment);

// Options for the [connect] family of functions.
// Options for the [[connect]] family of functions.
export type connect_option = keepalive;

// Indicates that a send or recv operation should include out-of-band data.
export type oob = []u8;

// Options for [send] and [recv].
// Options for [[send]] and [[recv]].
export type dgram_option = oob;

// A listener binds a socket and listens for incoming traffic for some


@@ 58,7 58,7 @@ export fn accept(l: *listener) (*io::stream | io::error) = {
	};
};

// Shuts down a [listener] and frees resources associated with it.
// Shuts down a [[listener]] and frees resources associated with it.
export fn shutdown(l: *listener) void = {
	match (l.shutdown) {
		f: *fn(l: *listener) void => f(l),

M os/+linux/dirfdfs.ha => os/+linux/dirfdfs.ha +4 -4
@@ 43,11 43,11 @@ type os_filesystem = struct {
	resolve: resolve_flags,
};

// Opens a file descriptor as an [fs::fs]. This file descriptor must be a
// Opens a file descriptor as an [[fs::fs]]. This file descriptor must be a
// directory file. The file will be closed when the fs is closed.
//
// If no other flags are provided to [fs::open] and [fs::create] when used with
// a dirfdfs, [fs::flags::NOCTTY] and [fs::flags::CLOEXEC] are used when opening
// If no other flags are provided to [[fs::open]] and [[fs::create]] when used with
// a dirfdfs, [[fs::flags::NOCTTY]] and [[fs::flags::CLOEXEC]] are used when opening
// the file. If you pass your own flags, it is recommended that you add these
// unless you know that you do not want them.
export fn dirfdopen(fd: int, resolve: resolve_flags...) *fs::fs = {


@@ 81,7 81,7 @@ fn static_dirfdopen(fd: int, filesystem: *os_filesystem) *fs::fs = {
	return &filesystem.fs;
};

// Clones a dirfd filesystem, optionally adding additional [resolve_flags]
// Clones a dirfd filesystem, optionally adding additional [[resolve_flags]]
// constraints.
export fn dirfs_clone(fs: *fs::fs, resolve: resolve_flags...) *fs::fs = {
	assert(fs.open == &fs_open);

M os/+linux/fdstream.ha => os/+linux/fdstream.ha +2 -2
@@ 46,11 46,11 @@ fn is_fdstream(s: *io::stream) bool = {
		|| s.copier == &fd_copy;
};

// Returns the file descriptor for a given [io::stream]. If there is no fd
// Returns the file descriptor for a given [[io::stream]]. If there is no fd
// associated with this stream, void is returned.
//
// If 'unwrap' is true, the stream will be unwrapped until an fdstream is found.
// Note that many stream wrappers (such as [io::tee]) provide important
// Note that many stream wrappers (such as [[io::tee]]) provide important
// functionality, and using the file descriptor directly will circumvent that
// functionality. Use with caution.
export fn streamfd(s: *io::stream, unwrap: bool) (int | void) = {

M os/+linux/fs.ha => os/+linux/fs.ha +1 -1
@@ 16,7 16,7 @@ use strings;
};

// Returns the current working directory. The return value is statically
// allocated and must be duplicated (see [strings::dup]) before calling getcwd
// allocated and must be duplicated (see [[strings::dup]]) before calling getcwd
// again.
export fn getcwd() str = strings::fromc(rt::getcwd() as *const char);


M os/exec/cmd.ha => os/exec/cmd.ha +2 -2
@@ 3,7 3,7 @@ use errors;
use os;
use strings;

// Prepares a [command] based on its name and a list of arguments. The argument
// Prepares a [[command]] based on its name and a list of arguments. The argument
// list should not start with the command name; it will be added for you. The
// argument list is borrowed from the strings you pass into this command.
//


@@ 43,7 43,7 @@ export fn setname(cmd: *command, name: str) void = {
};

// Frees state associated with a command. You only need to call this if you do
// not execute the command with [exec] or [start]; in those cases the state is
// not execute the command with [[exec]] or [[start]]; in those cases the state is
// cleaned up for you.
export fn finish(cmd: *command) void = {
	platform_finish(cmd);

M os/fs.ha => os/fs.ha +9 -9
@@ 2,20 2,20 @@ use fs;
use io;
use path;

// Provides an implementation of [fs::fs] for the host filesystem.
// Provides an implementation of [[fs::fs]] for the host filesystem.
export let root: *fs::fs = null: *fs::fs;

// Provides an implementation of [fs::fs] for the current working directory.
// Provides an implementation of [[fs::fs]] for the current working directory.
export let cwd: *fs::fs = null: *fs::fs;

// Removes a file.
export fn remove(path: str) (void | fs::error) = fs::remove(cwd, path);

// Creates an [fs::iterator] for a given directory to read its contents.
// Creates an [[fs::iterator]] for a given directory to read its contents.
export fn iterdir(path: str) (*fs::iterator | fs::error) = fs::iter(cwd, path);

// Reads all entries from a directory. The caller must free the return value
// with [fs::dirents_free].
// with [[fs::dirents_free]].
export fn readdir(path: str) ([]fs::dirent | fs::error) = fs::readdir(cwd, path);

// Returns file information for a given path.


@@ 30,7 30,7 @@ export fn mkdir(path: str) (void | fs::error) = fs::mkdir(cwd, path);
// Creates a directory, and all non-extant directories in its path.
export fn mkdirs(path: str) (void | fs::error) = fs::mkdirs(cwd, path);

// Removes a directory. The target directory must be empty; see [rmdirall] to
// Removes a directory. The target directory must be empty; see [[rmdirall]] to
// remove its contents as well.
export fn rmdir(path: str) (void | fs::error) = fs::rmdir(cwd, path);



@@ 54,8 54,8 @@ export fn resolve(path: str) str = fs::resolve(cwd, path);

// Opens a file.
//
// If no flags are provided, [fs::flags::RDONLY], [fs::flags::NOCTTY],
// [fs::flags::CLOEXEC] are used when opening the file. If you pass your own
// If no flags are provided, [[fs::flags::RDONLY]], [[fs::flags::NOCTTY]],
// [[fs::flags::CLOEXEC]] are used when opening the file. If you pass your own
// flags, it is recommended that you add the latter two unless you know that you
// do not want them.
export fn open(path: str, flags: fs::flags...) (*io::stream | fs::error) =


@@ 63,8 63,8 @@ export fn open(path: str, flags: fs::flags...) (*io::stream | fs::error) =

// Creates a new file and opens it for writing.
//
// If no flags are provided, [fs::flags::WRONLY], [fs::flags::NOCTTY],
// [fs::flags::CLOEXEC] are used when opening the file. If you pass your own
// If no flags are provided, [[fs::flags::WRONLY]], [[fs::flags::NOCTTY]],
// [[fs::flags::CLOEXEC]] are used when opening the file. If you pass your own
// flags, it is recommended that you add the latter two unless you know that you
// do not want them.
//

M path/names.ha => path/names.ha +3 -3
@@ 5,7 5,7 @@ use strings;
// Returns the directory name for a given path. For a path to a file name, this
// returns the directory in which that file resides. For a path to a directory,
// this returns the path to its parent directory. The return value is borrowed
// from the input, use [dup] to extend its lifetime.
// from the input, use [[dup]] to extend its lifetime.
export fn dirname(path: str) str = {
	let b = strings::toutf8(path);
	let i = match (bytes::rindex(b, PATHSEP)) {


@@ 28,7 28,7 @@ export fn dirname(path: str) str = {

// Returns the final component of a given path. For a path to a file name, this
// returns the file name. For a path to a directory, this returns the directory
// name. The return value is borrowed from the input, use [dup] to extend its
// name. The return value is borrowed from the input, use [[dup]] to extend its
// lifetime.
export fn basename(path: str) str = {
	let b = strings::toutf8(path);


@@ 48,7 48,7 @@ export fn basename(path: str) str = {
};

// Returns the file name and extension for a path. The return value is borrowed
// from the input, see [strings::dup] to extend its lifetime.
// from the input, see [[strings::dup]] to extend its lifetime.
//
// The extension includes the '.' character.
//

M rt/+linux/errno.ha => rt/+linux/errno.ha +2 -2
@@ 13,7 13,7 @@ fn wrap_return(r: u64) (errno | u64) = {
	return r;
};

// Obtains a human-friendly reading of an [errno] (e.g. "Operation not
// Obtains a human-friendly reading of an [[errno]] (e.g. "Operation not
// permitted").
export fn strerror(err: errno) str = {
	return switch (err: int) {


@@ 152,7 152,7 @@ export fn strerror(err: errno) str = {
	};
};

// Gets the programmer-friendly name for an [errno] (e.g. EPERM).
// Gets the programmer-friendly name for an [[errno]] (e.g. EPERM).
export fn errname(err: errno) str = {
	return switch (err: int) {
		EPERM => "EPERM",

M rt/malloc.ha => rt/malloc.ha +1 -1
@@ 105,7 105,7 @@ fn list_from_block(s: size, p: uintptr) nullable *void = {
	return (p + WASTE: uintptr + WORD: uintptr): *void;
};

// Frees a pointer previously allocated with [malloc].
// Frees a pointer previously allocated with [[malloc]].
export @symbol("rt.free") fn free_(_p: nullable *void) void = {
	if (_p != null) {
		let p = _p: *void;

M strconv/ftos.ha => strconv/ftos.ha +1 -1
@@ 453,7 453,7 @@ fn encode(buf: []u8, v: decf64) size = {
};

// Converts a f64 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn f64tos(n: f64) const str = {
	// The biggest string produced by a f64 number in base 10 would have the

M strconv/itos.ha => strconv/itos.ha +10 -10
@@ 3,7 3,7 @@ use types;
use strings;

// Converts an i64 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i64tosb(i: i64, b: base) const str = {
	static assert(types::I64_MAX == 9223372036854775807);


@@ 26,47 26,47 @@ export fn i64tosb(i: i64, b: base) const str = {
};

// Converts a i32 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i32tosb(i: i32, b: base) const str = i64tosb(i, b);

// Converts a i16 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i16tosb(i: i16, b: base) const str = i64tosb(i, b);

// Converts a i8 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i8tosb(i: i8, b: base) const str = i64tosb(i, b);

// Converts an int to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn itosb(i: int, b: base) const str = i64tosb(i, b);

// Converts a i64 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i64tos(i: i64) const str = i64tosb(i, base::DEC);

// Converts a i32 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i32tos(i: i32) const str = i64tos(i);

// Converts a i16 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i16tos(i: i16) const str = i64tos(i);

// Converts a i8 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn i8tos(i: i8) const str = i64tos(i);

// Converts a int to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn itos(i: int) const str = i64tos(i);


M strconv/numeric.ha => strconv/numeric.ha +20 -20
@@ 1,8 1,8 @@
use types;

// Converts any [types::signed] to a string in a given base. The return value is
// Converts any [[types::signed]] to a string in a given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn signedtosb(n: types::signed, b: base) const str = {
	return match (n) {
		i: int => itosb(i, b),


@@ 13,14 13,14 @@ export fn signedtosb(n: types::signed, b: base) const str = {
	};
};

// Converts any [types::signed] to a string in base 10. The return value is
// Converts any [[types::signed]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn signedtos(n: types::signed) const str = signedtosb(n, base::DEC);

// Converts any [types::unsigned] to a string in a given base. The return value
// Converts any [[types::unsigned]] to a string in a given base. The return value
// is statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn unsignedtosb(n: types::unsigned, b: base) const str = {
	return match (n) {
		u: size => ztosb(u, b),


@@ 32,14 32,14 @@ export fn unsignedtosb(n: types::unsigned, b: base) const str = {
	};
};

// Converts any [types::unsigned] to a string in base 10. The return value is
// Converts any [[types::unsigned]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn unsignedtos(n: types::unsigned) const str = unsignedtosb(n, base::DEC);

// Converts any [types::integer] to a string in a given base, which must be 2,
// Converts any [[types::integer]] to a string in a given base, which must be 2,
// 8, 10, or 16. The return value is statically allocated and will be
// overwritten on subsequent calls; see [strings::dup] to duplicate the result.
// overwritten on subsequent calls; see [[strings::dup]] to duplicate the result.
export fn integertosb(n: types::integer, b: base) const str = {
	return match (n) {
		s: types::signed   => signedtosb(s, b),


@@ 47,26 47,26 @@ export fn integertosb(n: types::integer, b: base) const str = {
	};
};

// Converts any [types::integer] to a string in base 10. The return value is
// Converts any [[types::integer]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn integertos(n: types::integer) const str = integertosb(n, base::DEC);

// Converts any [types::floating] to a string in a given base. The return value
// Converts any [[types::floating]] to a string in a given base. The return value
// is statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn floatingtosb(n: types::floating, b: base) const str = {
	abort(); // TODO
};

// Converts any [types::floating] to a string in base 10. The return value is
// Converts any [[types::floating]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn floatingtos(n: types::floating) const str = floatingtosb(n, base::DEC);

// Converts any [types::numeric] to a string in a given base. The return value
// Converts any [[types::numeric]] to a string in a given base. The return value
// is statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn numerictosb(n: types::numeric, b: base) const str = {
	return match (n) {
		i: types::integer  => integertosb(i, b),


@@ 74,9 74,9 @@ export fn numerictosb(n: types::numeric, b: base) const str = {
	};
};

// Converts any [types::numeric] to a string in base 10. The return value is
// Converts any [[types::numeric]] to a string in base 10. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn numerictos(n: types::numeric) const str = numerictosb(n, base::DEC);

@test fn numeric() void = {

M strconv/stoi.ha => strconv/stoi.ha +20 -20
@@ 3,8 3,8 @@ use strings;

// Converts a string to an i64 in the given base. If the string contains any
// non-numeric characters, except '-' at the start, or if it's empty,
// [strconv::invalid] is returned. If the number is too large to be represented
// by an i64, [strconv::overflow] is returned.
// [[strconv::invalid]] is returned. If the number is too large to be represented
// by an i64, [[strconv::overflow]] is returned.
export fn stoi64b(s: str, base: uint) (i64 | invalid | overflow) = {
	if (len(s) == 0) return 0: invalid;
	let b = strings::toutf8(s);


@@ 25,8 25,8 @@ export fn stoi64b(s: str, base: uint) (i64 | invalid | overflow) = {

// Converts a string to an i32 in the given base. If the string contains any
// non-numeric characters, except '-' at the start, or if it's empty,
// [strconv::invalid] is returned. If the number is too large to be represented
// by an i32, [strconv::overflow] is returned.
// [[strconv::invalid]] is returned. If the number is too large to be represented
// by an i32, [[strconv::overflow]] is returned.
export fn stoi32b(s: str, base: uint) (i32 | invalid | overflow) = {
	let n = stoi64b(s, base)?;
	if (n >= types::I32_MIN: i64 && n <= types::I32_MAX: i64) {


@@ 37,8 37,8 @@ export fn stoi32b(s: str, base: uint) (i32 | invalid | overflow) = {

// Converts a string to an i16 in the given base. If the string contains any
// non-numeric characters, except '-' at the start, or if it's empty,
// [strconv::invalid] is returned. If the number is too large to be represented
// by an i16, [strconv::overflow] is returned.
// [[strconv::invalid]] is returned. If the number is too large to be represented
// by an i16, [[strconv::overflow]] is returned.
export fn stoi16b(s: str, base: uint) (i16 | invalid | overflow) = {
	let n = stoi64b(s, base)?;
	if (n >= types::I16_MIN: i64 && n <= types::I16_MAX: i64) {


@@ 49,8 49,8 @@ export fn stoi16b(s: str, base: uint) (i16 | invalid | overflow) = {

// Converts a string to an i8 in the given base. If the string contains any
// non-numeric characters, except '-' at the start, or if it's empty,
// [strconv::invalid] is returned. If the number is too large to be represented
// by an i8, [strconv::overflow] is returned.
// [[strconv::invalid]] is returned. If the number is too large to be represented
// by an i8, [[strconv::overflow]] is returned.
export fn stoi8b(s: str, base: uint) (i8 | invalid | overflow) = {
	let n= stoi64b(s, base)?;
	if (n >= types::I8_MIN: i64 && n <= types::I8_MAX: i64) {


@@ 61,8 61,8 @@ export fn stoi8b(s: str, base: uint) (i8 | invalid | overflow) = {

// Converts a string to an int in the given base. If the string contains any
// non-numeric characters, except '-' at the start, or if it's empty,
// [strconv::invalid] is returned. If the number is too large to be represented
// by an int, [strconv::overflow] is returned.
// [[strconv::invalid]] is returned. If the number is too large to be represented
// by an int, [[strconv::overflow]] is returned.
export fn stoib(s: str, base: uint) (int | invalid | overflow) = {
	static assert(size(int) == size(i32) || size(int) == size(i64));
	return


@@ 71,31 71,31 @@ export fn stoib(s: str, base: uint) (int | invalid | overflow) = {
};

// Converts a string to an i64 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u64, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u64, [[strconv::overflow]] is
// returned.
export fn stoi64(s: str) (i64 | invalid | overflow) = stoi64b(s, 10);

// Converts a string to an i32 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u32, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u32, [[strconv::overflow]] is
// returned.
export fn stoi32(s: str) (i32 | invalid | overflow) = stoi32b(s, 10);

// Converts a string to an i16 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u16, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u16, [[strconv::overflow]] is
// returned.
export fn stoi16(s: str) (i16 | invalid | overflow) = stoi16b(s, 10);

// Converts a string to an i8 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u8, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u8, [[strconv::overflow]] is
// returned.
export fn stoi8(s: str) (i8 | invalid | overflow) = stoi8b(s, 10);

// Converts a string to an int in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a uint, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a uint, [[strconv::overflow]] is
// returned.
export fn stoi(s: str) (int | invalid | overflow) = stoib(s, 10);

M strconv/stou.ha => strconv/stou.ha +24 -24
@@ 13,8 13,8 @@ fn rune_to_integer(r: rune) (u64 | void) = {
};

// Converts a string to a u64 in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u64, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u64, [[strconv::overflow]] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stou64b(s: str, base: uint) (u64 | invalid | overflow) = {
	assert(base == 2 || base == 8 || base == 10 || base == 16);


@@ 51,8 51,8 @@ export fn stou64b(s: str, base: uint) (u64 | invalid | overflow) = {
};

// Converts a string to a u32 in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u32, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u32, [[strconv::overflow]] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stou32b(s: str, base: uint) (u32 | invalid | overflow) = {
	let n = stou64b(s, base)?;


@@ 63,8 63,8 @@ export fn stou32b(s: str, base: uint) (u32 | invalid | overflow) = {
};

// Converts a string to a u16 in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u16, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u16, [[strconv::overflow]] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stou16b(s: str, base: uint) (u16 | invalid | overflow) = {
	let n = stou64b(s, base)?;


@@ 75,8 75,8 @@ export fn stou16b(s: str, base: uint) (u16 | invalid | overflow) = {
};

// Converts a string to a u8 in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u8, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u8, [[strconv::overflow]] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stou8b(s: str, base: uint) (u8 | invalid | overflow) = {
	let n = stou64b(s, base)?;


@@ 87,8 87,8 @@ export fn stou8b(s: str, base: uint) (u8 | invalid | overflow) = {
};

// Converts a string to a uint in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a uint, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a uint, [[strconv::overflow]] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stoub(s: str, base: uint) (uint | invalid | overflow) = {
	static assert(size(uint) == size(u32) || size(uint) == size(u64));


@@ 98,8 98,8 @@ export fn stoub(s: str, base: uint) (uint | invalid | overflow) = {
};

// Converts a string to a size in the given base, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a size, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a size, [[strconv::overflow]] is
// returned. Supported bases are 2, 8, 10 and 16.
export fn stozb(s: str, base: uint) (size | invalid | overflow) = {
	static assert(size(size) == size(u32) || size(size) == size(u64));


@@ 113,37 113,37 @@ export fn stozb(s: str, base: uint) (size | invalid | overflow) = {
};

// Converts a string to a u64 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u64, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u64, [[strconv::overflow]] is
// returned.
export fn stou64(s: str) (u64 | invalid | overflow) = stou64b(s, 10);

// Converts a string to a u32 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u32, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u32, [[strconv::overflow]] is
// returned.
export fn stou32(s: str) (u32 | invalid | overflow) = stou32b(s, 10);

// Converts a string to a u16 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u16, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u16, [[strconv::overflow]] is
// returned.
export fn stou16(s: str) (u16 | invalid | overflow) = stou16b(s, 10);

// Converts a string to a u8 in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a u8, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a u8, [[strconv::overflow]] is
// returned.
export fn stou8(s: str) (u8 | invalid | overflow) = stou8b(s, 10);

// Converts a string to a uint in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a uint, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a uint, [[strconv::overflow]] is
// returned.
export fn stou(s: str) (uint | invalid | overflow) = stoub(s, 10);

// Converts a string to a size in base 10, If the string contains any
// non-numeric characters, or if it's empty, [strconv::invalid] is returned. If
// the number is too large to be represented by a size, [strconv::overflow] is
// non-numeric characters, or if it's empty, [[strconv::invalid]] is returned. If
// the number is too large to be represented by a size, [[strconv::overflow]] is
// returned.
export fn stoz(s: str) (size | invalid | overflow) = stozb(s, 10);

M strconv/utos.ha => strconv/utos.ha +15 -15
@@ 2,7 2,7 @@ use bytes;
use types;

// Converts a u64 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u64tosb(u: u64, b: base) const str = {
	static assert(types::U64_MAX == 18446744073709551615);


@@ 37,67 37,67 @@ export fn u64tosb(u: u64, b: base) const str = {
};

// Converts a u32 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u32tosb(u: u32, b: base) const str = u64tosb(u, b);

// Converts a u16 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u16tosb(u: u16, b: base) const str = u64tosb(u, b);

// Converts a u8 to a string in the given base. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u8tosb(u: u8, b: base) const str = u64tosb(u, b);

// Converts a uint to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn utosb(u: uint, b: base) const str = u64tosb(u, b);

// Converts a size to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn ztosb(u: size, b: base) const str = u64tosb(u, b);

// Converts a size to a string in the given base. The return value is
// statically allocated and will be overwritten on subsequent calls; see
// [strings::dup] to duplicate the result.
// [[strings::dup]] to duplicate the result.
export fn uptrtosb(uptr: uintptr, b: base) const str = u64tosb(uptr: u64, b);

// Converts a u64 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u64tos(u: u64) const str = u64tosb(u, base::DEC);

// Converts a u32 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u32tos(u: u32) const str = u64tos(u);

// Converts a u16 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u16tos(u: u16) const str = u64tos(u);

// Converts a u8 to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn u8tos(u: u8) const str = u64tos(u);

// Converts a uint to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn utos(u: uint) const str = u64tos(u);

// Converts a size to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// duplicate the result, or [strconv::itosb] to pass your own string buffer.
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result, or [[strconv::itosb]] to pass your own string buffer.
export fn ztos(z: size) const str = u64tos(z);

// Converts a uintptr to a string in base 10. The return value is statically
// allocated and will be overwritten on subsequent calls; see [strings::dup] to
// allocated and will be overwritten on subsequent calls; see [[strings::dup]] to
// duplicate the result.
export fn uptrtos(uptr: uintptr) const str = u64tos(uptr: u64);


M strings/iter.ha => strings/iter.ha +4 -4
@@ 28,7 28,7 @@ export fn riter(src: str) iterator = {
// Unicode strings. If you use these runes to construct a new string,
// reordering, editing, or omitting any of the runes without careful discretion
// may cause linguistic errors to arise. To avoid this, you may need to use
// [unicode::graphiter] instead.
// [[unicode::graphiter]] instead.
export fn next(iter: *iterator) (rune | void) = {
	match (iter.push) {
		r: rune => {


@@ 57,11 57,11 @@ export fn prev(iter: *iterator) (rune | void) = {
	};
};

// Causes the next call to [next] to return the provided rune, effectively
// un-reading it. The next call using this iterator *must* be [next]; all other
// Causes the next call to [[next]] to return the provided rune, effectively
// un-reading it. The next call using this iterator *must* be [[next]]; all other
// functions will cause the program to abort until the pushed rune is consumed.
// This does not modify the underlying string, and as such, subsequent calls to
// functions like [prev] or [iter_str] will behave as if push were never called.
// functions like [[prev]] or [[iter_str]] will behave as if push were never called.
export fn push(iter: *iterator, r: rune) void = {
	assert(iter.push is void);
	iter.push = r;

M strings/sub.ha => strings/sub.ha +2 -2
@@ 29,13 29,13 @@ fn utf8_byte_len_unbounded(iter: *iterator) size = {
};

// Returns a substring in the range [start, end - 1], where each argument is the
// index of the Nth rune. If the end argument is given as [strings::end], the
// index of the Nth rune. If the end argument is given as [[strings::end]], the
// end of the substring is the end of the original string. The lifetime of the
// substring is the same as that of the original string.
//
// Note that substringing runewise is not always the correct thing to do, and it
// may cause unexpected linguistic errors to arise. You may need to use
// [unicode::graphsub] instead.
// [[unicode::graphsub]] instead.
export fn sub(s: str, start: size, end: (size | end)) str = {
	let iter = iter(s);
	let starti = utf8_byte_len_bounded(&iter, start);

M strings/tokenize.ha => strings/tokenize.ha +2 -2
@@ 82,7 82,7 @@ export fn remaining_tokens(s: *tokenizer) str = {

// Splits a string into tokens delimited by 'delim', returning a slice of up to
// N tokens. The caller must free this slice. The strings within the slice are
// borrowed from 'in', and needn't be freed - but should be [strings::dup_all]'d
// borrowed from 'in', and needn't be freed - but should be [[strings::dup_all]]'d
// if they should outlive 'in'.
export fn splitN(in: str, delim: str, n: size) []str = {
	let toks: []str = alloc([]);


@@ 99,7 99,7 @@ export fn splitN(in: str, delim: str, n: size) []str = {

// Splits a string into tokens delimited by 'delim'.  The caller must free the
// returned slice. The strings within the slice are borrowed from 'in', and
// needn't be freed - but must be [strings::dup_all]'d if they should outlive
// needn't be freed - but must be [[strings::dup_all]]'d if they should outlive
// 'in'.
export fn split(in: str, delim: str) []str = splitN(in, delim, types::SIZE_MAX);


M strings/utf8.ha => strings/utf8.ha +1 -1
@@ 14,7 14,7 @@ export fn fromutf8_unsafe(in: []u8) str = {

// Converts a byte slice into a string. Aborts if the bytes contain invalid
// UTF-8. To handle such an error without aborting, see
// [encoding::utf8::decode] instead.
// [[encoding::utf8::decode]] instead.
export fn fromutf8(in: []u8) str = {
	let s = fromutf8_unsafe(in);
	assert(utf8::valid(s), "attempted to load invalid UTF-8 string");

M strio/dynamic.ha => strio/dynamic.ha +1 -1
@@ 10,7 10,7 @@ type dynamic_stream = struct {
// Creates a write-only string stream using an allocated buffer for storage, for
// efficiently building strings.
//
// Calling [io::close] on this stream will free the buffer. Call [strio::finish]
// Calling [[io::close]] on this stream will free the buffer. Call [[strio::finish]]
// instead to free up resources associated with the stream, but transfer
// ownership of the buffer to the caller.
export fn dynamic() *io::stream = {

M strio/ops.ha => strio/ops.ha +1 -1
@@ 2,7 2,7 @@ use encoding::utf8;
use io;
use strings;

// Appends zero or more strings to an [io::stream]. The stream needn't be a
// Appends zero or more strings to an [[io::stream]]. The stream needn't be a
// strio stream, but it's often efficient if it is. Returns the number of bytes
// written, or an error.
export fn concat(st: *io::stream, strs: str...) (size | io::error) = {

M temp/+linux.ha => temp/+linux.ha +3 -3
@@ 13,7 13,7 @@ fn get_tmpdir() str = os::tryenv("TMPDIR", "/tmp");
// any directory. If it is necessary to create a real file, it will be removed
// when the stream is closed.
//
// The I/O mode must be either [io::mode::WRITE] or [io::mode::RDWR].
// The I/O mode must be either [[io::mode::WRITE]] or [[io::mode::RDWR]].
//
// Only one variadic argument may be provided, if at all, to specify the mode of
// the new file. The default is 0o644.


@@ 38,7 38,7 @@ export fn file(

// Creates a named temporary file.
//
// The I/O mode must be either [io::mode::WRITE] or [io::mode::RDWR].
// The I/O mode must be either [[io::mode::WRITE]] or [[io::mode::RDWR]].
//
// Only one variadic argument may be provided, if at all, to specify the mode of
// the new file. The default is 0o644.


@@ 52,7 52,7 @@ export fn named(
// Creates a temporary directory. This function only guarantees that the
// directory will have a unique name and be placed in the system temp directory,
// but not that it will be removed automatically; the caller must remove it when
// they're done using it via [os::rmdir] or [os::rmdirall].
// they're done using it via [[os::rmdir]] or [[os::rmdirall]].
//
// The caller must free the return value.
export fn dir() str = {

M time/types.ha => time/types.ha +2 -2
@@ 20,10 20,10 @@ export fn compare(a: instant, b: instant) int = {
		if (a.nsec < b.nsec) -1 else if (a.nsec > b.nsec) 1 else 0;
};

// Converts the given [instant] to a Unix timestamp.
// Converts the given [[instant]] to a Unix timestamp.
export fn unix(a: instant) i64 = a.sec;

// Returns a [instant] from a Unix timestamp.
// Returns a [[instant]] from a Unix timestamp.
export fn from_unix(u: i64) instant = instant {
	sec = u,
	nsec = 0,

M unix/passwd/group.ha => unix/passwd/group.ha +3 -3
@@ 17,7 17,7 @@ export type grent = struct {
};

// Reads a Unix-like group entry from a stream. The caller must free the result
// using [grent_finish].
// using [[grent_finish]].
export fn nextgr(stream: *io::stream) (grent | io::EOF | io::error | invalid) = {
	let line = match (bufio::scanline(stream)?) {
		ln: []u8 => ln,


@@ 49,7 49,7 @@ export fn nextgr(stream: *io::stream) (grent | io::EOF | io::error | invalid) = 
	};
};

// Frees resources associated with [grent].
// Frees resources associated with [[grent]].
export fn grent_finish(ent: grent) void = {
	free(ent.name);
	free(ent.userlist);


@@ 58,7 58,7 @@ export fn grent_finish(ent: grent) void = {
// Looks up a group by name in a Unix-like group file. It expects a such file at
// /etc/group. Aborts if that file doesn't exist or is not properly formatted.
//
// See [nextgr] for low-level parsing API.
// See [[nextgr]] for low-level parsing API.
export fn getgroup(name: str) (grent | void) = {
	let file = match (os::open("/etc/group")) {
		s: *io::stream => s,

M unix/passwd/passwd.ha => unix/passwd/passwd.ha +3 -3
@@ 23,7 23,7 @@ export type pwent = struct {
};

// Reads a Unix-like password entry from a stream. The caller must free the
// result using [unix::passwd::pwent_finish].
// result using [[unix::passwd::pwent_finish]].
export fn nextpw(stream: *io::stream) (pwent | io::EOF | io::error | invalid) = {
	let line = match (bufio::scanline(stream)?) {
		io::EOF => return io::EOF,


@@ 63,7 63,7 @@ export fn nextpw(stream: *io::stream) (pwent | io::EOF | io::error | invalid) = 
	};
};

// Frees resources associated with [pwent].
// Frees resources associated with [[pwent]].
export fn pwent_finish(ent: pwent) void = {
	// pwent fields are sliced from one allocated string returned by
	// bufio::scanline. Freeing the first field frees the entire string in


@@ 75,7 75,7 @@ export fn pwent_finish(ent: pwent) void = {
// database file at /etc/passwd. Aborts if that file doesn't exist or is not
// properly formatted.
//
// See [unix::passwd::nextpw] for low-level parsing API.
// See [[unix::passwd::nextpw]] for low-level parsing API.
export fn getuser(username: str) (pwent | void) = {
	let file = match (os::open("/etc/passwd")) {
		s: *io::stream => s,

M unix/setuid.ha => unix/setuid.ha +4 -4
@@ 6,7 6,7 @@ use rt;
// If the system returns an error, this function will abort the program. Failing
// to handle errors from setuid is a grave security issue in your program, and
// therefore we require this function to succeed. If you need to handle the
// error case gracefully, call the appropriate syscall wrapper in [rt] yourself,
// error case gracefully, call the appropriate syscall wrapper in [[rt]] yourself,
// and take extreme care to handle errors correctly.
export fn setuid(uid: uint) void = rt::setresuid(uid, -1u, -1u) as void;



@@ 16,7 16,7 @@ export fn setuid(uid: uint) void = rt::setresuid(uid, -1u, -1u) as void;
// If the system returns an error, this function will abort the program. Failing
// to handle errors from seteuid is a grave security issue in your program, and
// therefore we require this function to succeed. If you need to handle the
// error case gracefully, call the appropriate syscall wrapper in [rt] yourself,
// error case gracefully, call the appropriate syscall wrapper in [[rt]] yourself,
// and take extreme care to handle errors correctly.
export fn seteuid(uid: uint) void = rt::setresuid(-1u, uid, -1u) as void;



@@ 26,7 26,7 @@ export fn seteuid(uid: uint) void = rt::setresuid(-1u, uid, -1u) as void;
// If the system returns an error, this function will abort the program. Failing
// to handle errors from setuid is a grave security issue in your program, and
// therefore we require this function to succeed. If you need to handle the
// error case gracefully, call the appropriate syscall wrapper in [rt] yourself,
// error case gracefully, call the appropriate syscall wrapper in [[rt]] yourself,
// and take extreme care to handle errors correctly.
export fn setgid(gid: uint) void = rt::setresgid(gid, -1u, -1u) as void;



@@ 36,6 36,6 @@ export fn setgid(gid: uint) void = rt::setresgid(gid, -1u, -1u) as void;
// If the system returns an error, this function will abort the program. Failing
// to handle errors from setegid is a grave security issue in your program, and
// therefore we require this function to succeed. If you need to handle the
// error case gracefully, call the appropriate syscall wrapper in [rt] yourself,
// error case gracefully, call the appropriate syscall wrapper in [[rt]] yourself,
// and take extreme care to handle errors correctly.
export fn setegid(gid: uint) void = rt::setresgid(-1u, gid, -1u) as void;

M uuid/uuid.ha => uuid/uuid.ha +2 -2
@@ 68,7 68,7 @@ export fn uri(out: *io::stream, in: uuid) (size | io::error) = {
};

// Encodes a UUID as a string. The return value is statically allocated, the
// caller must use [strings::dup] to extend its lifetime.
// caller must use [[strings::dup]] to extend its lifetime.
export fn encodestr(in: uuid) str = {
	static let buf: [UUID_STRLEN]u8 = [0...];
	let sink = strio::fixed(buf);


@@ 78,7 78,7 @@ export fn encodestr(in: uuid) str = {
};

// Encodes a UUID as a string. The return value is statically allocated, the
// caller must use [strings::dup] to extend its lifetime.
// caller must use [[strings::dup]] to extend its lifetime.
export fn encodeuri(in: uuid) str = {
	static let buf: [UUID_URILEN]u8 = [0...];
	let sink = strio::fixed(buf);