~sircmpwn/hare

213634235b39a005ce032be9627b6ca72a652178 ā€” Drew DeVault 8 months ago 11ad912
all: update names to be consistent with style guide
M bufio/buffered.ha => bufio/buffered.ha +5 -5
@@ 78,21 78,21 @@ export fn flush(s: *io::stream) (io::error | void) = {

// Sets the list of bytes which will cause the stream to flush when written. By
// default, the stream will flush when a newline (\n) is written.
export fn set_flush_bytes(s: *io::stream, b: []u8) void = {
export fn setflush(s: *io::stream, b: []u8) void = {
	assert(s.writer == &buffered_write,
		"bufio::set_flush_bytes used on non-buffered stream");
		"bufio: setflush used on non-buffered stream");
	let s = s: *bufstream;
	s.flush = b;
};

// Returns true if this is a buffered stream.
export fn is_buffered(s: *io::stream) bool = {
export fn isbuffered(s: *io::stream) bool = {
	return s.reader == &buffered_read || s.writer == &buffered_write;
};

// Returns true if this stream or any underlying streams are buffered.
export fn is_buffered_any(s: *io::stream) bool = {
	for (!is_buffered(s)) {
export fn any_isbuffered(s: *io::stream) bool = {
	for (!isbuffered(s)) {
		s = match (io::source(s)) {
			io::unsupported => return false,
			s: *io::stream  => s,

M cmd/hare/main.ha => cmd/hare/main.ha +2 -2
@@ 9,7 9,7 @@ export fn main() void = {
	let cmd = getopt::parse(os::args, help...);
	defer getopt::finish(&cmd);
	if (len(cmd.args) < 1) {
		getopt::print_usage(os::stderr, os::args[0], help...);
		getopt::printusage(os::stderr, os::args[0], help...);
		os::exit(1);
	};
	if (cmd.args[0] == "build") build(cmd.args)


@@ 19,7 19,7 @@ export fn main() void = {
	else if (cmd.args[0] == "test") test(cmd.args)
	else if (cmd.args[0] == "version") version(cmd.args)
	else {
		getopt::print_usage(os::stderr, os::args[0], help...);
		getopt::printusage(os::stderr, os::args[0], help...);
		os::exit(1);
	};
};

M cmd/hare/schedule.ha => cmd/hare/schedule.ha +3 -3
@@ 33,7 33,7 @@ fn sched_module(plan: *plan, ident: ast::ident, link: *[]*task) *task = {

	let ver = match (module::lookup(plan.context, ident)) {
		err: module::error => {
			let ident = unparse::ident_s(ident);
			let ident = unparse::identstr(ident);
			fmt::fatal("Error resolving {}: {}",
				ident, module::errstr(err));
		},


@@ 157,8 157,8 @@ fn sched_hare_object(
	let current = false;
	let output = if (len(namespace) != 0) {
		let version = hex::encode(ver.hash);
		let ns = unparse::ident_s(namespace);
		let env = module::ident_underscore(namespace);
		let ns = unparse::identstr(namespace);
		let env = module::identuscore(namespace);
		defer free(env);

		append(harec.cmd, "-N", ns);

M cmd/hare/subcmds.ha => cmd/hare/subcmds.ha +1 -1
@@ 63,7 63,7 @@ fn build(args: []str) void = {
		if (len(cmd.args) == 0) os::getcwd()
		else if (len(cmd.args) == 1) cmd.args[0]
		else {
			getopt::print_usage(os::stderr, args[0], help...);
			getopt::printusage(os::stderr, args[0], help...);
			os::exit(1);
		};


M dirs/xdg.ha => dirs/xdg.ha +3 -3
@@ 38,7 38,7 @@ 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
// path for this program to store data will be returned.
export fn config_fs(prog: str) *fs::fs = os::diropen(config(prog)) as *fs::fs;
export fn configfs(prog: str) *fs::fs = os::diropen(config(prog)) as *fs::fs;

// Returns a directory suitable for cache files. If 'prog' is given, a unique
// path for this program to store data will be returned.


@@ 46,7 46,7 @@ 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
// this program to store data will be returned.
export fn cache_fs(prog: str) *fs::fs = os::diropen(cache(prog)) as *fs::fs;
export fn cachefs(prog: str) *fs::fs = os::diropen(cache(prog)) as *fs::fs;

// Returns a directory suitable for persistent data files. If 'prog' is given, a
// unique path for this program to store data will be returned.


@@ 55,4 55,4 @@ export fn data(prog: str) str =

// 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 data_fs(prog: str) *fs::fs = os::diropen(data(prog)) as *fs::fs;
export fn datafs(prog: str) *fs::fs = os::diropen(data(prog)) as *fs::fs;

M encoding/utf8/encode.ha => encoding/utf8/encode.ha +2 -2
@@ 1,6 1,6 @@
// Encodes a rune as UTF-8 and returns the result as a slice. The result is
// statically allocated; duplicate it if you aren't using it right away.
export fn encode_rune(r: rune) []u8 = {
export fn encoderune(r: rune) []u8 = {
	let ch = r: u32, n = 0z, first = 0u8;
	if (ch < 0x80) {
		first = 0;


@@ 33,7 33,7 @@ export fn encode_rune(r: rune) []u8 = {
	];
	const inputs = ['\0', '%', '恓'];
	for (let i = 0z; i < len(inputs); i += 1) {
		const out = encode_rune(inputs[i]);
		const out = encoderune(inputs[i]);
		for (let j = 0z; j < len(expected[i]); j += 1) {
			assert(out[j] == expected[i][j]);
		};

M fmt/fmt.ha => fmt/fmt.ha +5 -5
@@ 207,7 207,7 @@ export fn fprintf(
			};

			const arg = if (r == '{') {
				n += io::write(s, utf8::encode_rune('{'))?;
				n += io::write(s, utf8::encoderune('{'))?;
				continue;
			} else if (ascii::isdigit(r)) {
				strings::push(&iter, r);


@@ 236,9 236,9 @@ export fn fprintf(
				r: rune => assert(r == '}', "Invalid format string (hanging '}')"),
			};

			n += io::write(s, utf8::encode_rune('}'))?;
			n += io::write(s, utf8::encoderune('}'))?;
		} else {
			n += io::write(s, utf8::encode_rune(r))?;
			n += io::write(s, utf8::encoderune(r))?;
		};
	};



@@ 250,7 250,7 @@ fn format(out: *io::stream, arg: formattable, mod: *modifiers) (size | io::error

	let pad: []u8 = [];
	if (z < mod.width: size) {
		pad = utf8::encode_rune(switch (mod.padding) {
		pad = utf8::encoderune(switch (mod.padding) {
			padding::ZEROES => '0',
			* => ' ',
		});


@@ 277,7 277,7 @@ fn format_raw(
	mod: *modifiers,
) (size | io::error) = match (arg) {
	s: str => io::write(out, strings::to_utf8(s)),
	r: rune => io::write(out, utf8::encode_rune(r)),
	r: rune => io::write(out, utf8::encoderune(r)),
	b: bool => io::write(out, strings::to_utf8(if (b) "true" else "false")),
	n: types::numeric => {
		let s = strconv::numerictosb(n, mod.base);

M getopt/getopts.ha => getopt/getopts.ha +8 -8
@@ 157,7 157,7 @@ export fn parse(args: []str, help: help...) command = {
				continue :arg;
			};
			if (r =='h') {
				print_help(os::stderr, args[0], help);
				printhelp(os::stderr, args[0], help);
				os::exit(0);
			};
			errmsg(args[0], "unrecognized option: ", r, help);


@@ 185,7 185,7 @@ export fn finish(cmd: *command) void = {
	free(cmd.opts);
};

fn _print_usage(s: *io::stream, name: str, indent: bool, help: []help) size = {
fn _printusage(s: *io::stream, name: str, indent: bool, help: []help) size = {
	let z = fmt::fprint(s, "Usage:", name) as size;

	let started_flags = false;


@@ 219,18 219,18 @@ fn _print_usage(s: *io::stream, name: str, indent: bool, help: []help) size = {
};

// Prints command usage to the provided stream.
export fn print_usage(s: *io::stream, name: str, help: []help) void = {
	let z = _print_usage(io::empty, name, false, help);
	_print_usage(s, name, if (z > 72) true else false, help);
export fn printusage(s: *io::stream, name: str, help: []help) void = {
	let z = _printusage(io::empty, name, false, help);
	_printusage(s, name, if (z > 72) true else false, help);
};

// Prints command help to the provided stream.
export fn print_help(s: *io::stream, name: str, help: []help) void = {
export fn printhelp(s: *io::stream, name: str, help: []help) void = {
	if (help[0] is cmd_help) {
		fmt::fprintfln(s, "{}: {}\n", name, help[0] as cmd_help: str);
	};

	print_usage(s, name, help);
	printusage(s, name, help);

	for (let i = 0z; i < len(help); i += 1) match (help[i]) {
		cmd_help => void,


@@ 257,7 257,7 @@ fn errmsg(name: str, err: str, opt: (rune | void), help: []help) void = {
		r: rune => r,
		void => "",
	});
	print_usage(os::stderr, name, help);
	printusage(os::stderr, name, help);
};

@test fn parse() void = {

M hare/lex/lex.ha => hare/lex/lex.ha +3 -3
@@ 167,7 167,7 @@ fn lex_string(
			else {
				unget(lex, r);
				r = lex_rune(lex, loc)?;
				append(chars, ...utf8::encode_rune(r));
				append(chars, ...utf8::encoderune(r));
			},
	};
	return (strings::from_utf8(chars): literal, loc);


@@ 206,7 206,7 @@ fn lex_name(
	match (next(lex)) {
		r: rune => {
			assert(is_name(r, false));
			append(chars, ...utf8::encode_rune(r));
			append(chars, ...utf8::encoderune(r));
		},
		(io::EOF | io::error) => abort(),
	};


@@ 218,7 218,7 @@ fn lex_name(
				unget(lex, r);
				break;
			};
			append(chars, ...utf8::encode_rune(r));
			append(chars, ...utf8::encoderune(r));
		},
	};


M hare/module/context.ha => hare/module/context.ha +6 -6
@@ 70,7 70,7 @@ export fn context_finish(ctx: *context) void = {

// Converts an identifier to a partial path (e.g. foo::bar becomes foo/bar). The
// return value must be freed by the caller.
export fn ident_path(name: ast::ident) str = {
export fn identpath(name: ast::ident) str = {
	let p = path::join(name[0]);
	for (let i = 1z; i < len(name); i += 1) {
		let q = path::join(p, name[i]);


@@ 80,9 80,9 @@ export fn ident_path(name: ast::ident) str = {
	return p;
};

@test fn ident_path() void = {
@test fn identpath() void = {
	let ident: ast::ident = ["foo", "bar", "baz"];
	let p = ident_path(ident);
	let p = identpath(ident);
	defer free(p);
	assert(p == "foo/bar/baz");
};


@@ 91,7 91,7 @@ export fn ident_path(name: ast::ident) str = {
// value must be freed by the caller.
//
// This is used for module names in environment variables and some file names.
export fn ident_underscore(ident: ast::ident) str = {
export fn identuscore(ident: ast::ident) str = {
	let buf = strio::dynamic();
	for (let i = 0z; i < len(ident); i += 1) {
		fmt::fprintf(buf, "{}{}", ident[i],


@@ 101,9 101,9 @@ export fn ident_underscore(ident: ast::ident) str = {
	return strio::finish(buf);
};

@test fn ident_underscore() void = {
@test fn identuscore() void = {
	let ident: ast::ident = ["foo", "bar", "baz"];
	let p = ident_underscore(ident);
	let p = identuscore(ident);
	defer free(p);
	assert(p == "foo_bar_baz");
};

M hare/module/manifest.ha => hare/module/manifest.ha +3 -3
@@ 42,7 42,7 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {
		inputs = [],
		versions = [],
	};
	let ipath = ident_path(manifest.ident);
	let ipath = identpath(manifest.ident);
	defer free(ipath);
	let cachedir = path::join(ctx.cache, ipath);
	defer free(cachedir);


@@ 209,7 209,7 @@ export fn current(manifest: *manifest, version: *version) bool = {

// Writes a module manifest to the build cache.
export fn manifest_write(ctx: *context, manifest: *manifest) (void | error) = {
	let ipath = ident_path(manifest.ident);
	let ipath = identpath(manifest.ident);
	defer free(ipath);
	let cachedir = path::join(ctx.cache, ipath);
	defer free(cachedir);


@@ 223,7 223,7 @@ export fn manifest_write(ctx: *context, manifest: *manifest) (void | error) = {
	let fd = fs::create(ctx.fs, mpath, 0o644)?;
	defer io::close(fd);

	let ident = unparse::ident_s(manifest.ident);
	let ident = unparse::identstr(manifest.ident);
	defer free(ident);
	fmt::fprintfln(fd, "# {}", ident)?;
	fmt::fprintln(fd, "# This file is an internal Hare implementation detail.")?;

M hare/module/scan.ha => hare/module/scan.ha +6 -6
@@ 74,7 74,7 @@ fn parse_name(name: str) (str, str, []tag) = {
		else if (m: size < p: size) m: size
		else p: size;
	let tags = strings::sub(base, i, strings::end);
	let tags = match (parse_tags(tags)) {
	let tags = match (parsetags(tags)) {
		void => return (base, ext, []),
		t: []tag => t,
	};


@@ 129,7 129,7 @@ fn scan_directory(
			!strings::has_prefix(name, "-"))) {
			continue;
		};
		if (!tags_compat(ctx.tags, tags)) {
		if (!tagcompat(ctx.tags, tags)) {
			continue;
		};



@@ 151,7 151,7 @@ fn scan_directory(
				break;
			};
		};
		if (!eligible || !tags_compat(ctx.tags, tags)) {
		if (!eligible || !tagcompat(ctx.tags, tags)) {
			tags_free(tags);
			continue;
		};


@@ 175,7 175,7 @@ fn scan_directory(
// 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 = ident_path(name);
	let ipath = identpath(name);
	for (let i = len(ctx.paths); i > 0; i -= 1) {
		let cand = path::join(ctx.paths[i - 1], ipath);
		defer free(cand);


@@ 236,7 236,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].
export fn parse_tags(in: str) ([]tag | void) = {
export fn parsetags(in: str) ([]tag | void) = {
	let tags: []tag = [];
	// defer! tags_free(tags);
	let iter = strings::iter(in);


@@ 278,7 278,7 @@ export fn tags_free(tags: []tag) void = {
};

// Compares two tag sets and tells you if they are compatible.
export fn tags_compat(have: []tag, want: []tag) bool = {
export fn tagcompat(have: []tag, want: []tag) bool = {
	// XXX: O(nĀ²), lame
	for (let i = 0z; i < len(want); i += 1) {
		let present = false;

M hare/unparse/ident.ha => hare/unparse/ident.ha +3 -3
@@ 15,17 15,17 @@ export fn ident(out: *io::stream, id: ast::ident) (size | io::error) = {
};

// Unparses an identifier into a string. The caller must free the return value.
export fn ident_s(id: ast::ident) str = {
export fn identstr(id: ast::ident) str = {
	let buf = strio::dynamic();
	ident(buf, id);
	return strio::finish(buf);
};

@test fn ident() void = {
	let s = ident_s(["foo", "bar", "baz"]);
	let s = identstr(["foo", "bar", "baz"]);
	assert(s == "foo::bar::baz");
	free(s);
	s = ident_s(["foo"]);
	s = identstr(["foo"]);
	assert(s == "foo");
	free(s);
};

M io/+test/limit.ha => io/+test/limit.ha +2 -2
@@ 3,7 3,7 @@
	let source_stream = test_stream_open();
	defer close(&source_stream.stream);

	let r_stream = limit_reader(&source_stream.stream, 20);
	let r_stream = limitreader(&source_stream.stream, 20);
	match (write(r_stream, buf)) {
		unsupported => void,
		* => abort(),


@@ 18,7 18,7 @@
	};
	close(r_stream);

	let w_stream = limit_writer(&source_stream.stream, 20);
	let w_stream = limitwriter(&source_stream.stream, 20);
	match (read(w_stream, buf)) {
		unsupported => void,
		* => abort(),

M io/limit.ha => io/limit.ha +2 -2
@@ 20,7 20,7 @@ fn limited_stream_new(source: *stream, limit: size) *limited_stream = {

// Create an overlay stream that only allows a limited amount of bytes to be
// read from the underlying stream.
export fn limit_reader(source: *stream, limit: size) *stream = {
export fn limitreader(source: *stream, limit: size) *stream = {
	let stream = limited_stream_new(source, limit);
	stream.stream.reader = &limited_read;
	return &stream.stream;


@@ 28,7 28,7 @@ export fn limit_reader(source: *stream, limit: size) *stream = {

// Create an overlay stream that only allows a limited amount of bytes to be
// written to the underlying stream.
export fn limit_writer(source: *stream, limit: size) *stream = {
export fn limitwriter(source: *stream, limit: size) *stream = {
	let stream = limited_stream_new(source, limit);
	stream.stream.writer = &limited_write;
	return &stream.stream;

M linux/vdso/vdso.ha => linux/vdso/vdso.ha +3 -3
@@ 124,7 124,7 @@ fn vdso_checkver(ctx: *vdso_ctx, version: str, num: u32) bool = {
				cur.vd_aux: uintptr): *elf::verdaux64;
			const name = ctx.stringtab +
				aux.vda_name: uintptr: *char;
			return version == strings::from_c(name);
			return version == strings::fromc(name);
		};
		prev = cur;
		cur += cur.vd_next: uintptr;


@@ 132,7 132,7 @@ fn vdso_checkver(ctx: *vdso_ctx, version: str, num: u32) bool = {
	return false;
};

export fn get_vdso_sym(symname: str, symver: str) nullable *void = {
export fn getsym(symname: str, symver: str) nullable *void = {
	const ctx = match (get_vdso_ctx()) {
		null => return null,
		x: *vdso_ctx => x,


@@ 155,7 155,7 @@ export fn get_vdso_sym(symname: str, symver: str) nullable *void = {
			continue;
		};
		const name = ctx.stringtab + sym.st_name: uintptr: *char;
		const s: str = strings::from_c(name);
		const s: str = strings::fromc(name);
		if (s != symname)
			continue;
		if (!vdso_checkver(ctx, symver, i))

M os/+linux/dirfdfs.ha => os/+linux/dirfdfs.ha +1 -1
@@ 378,7 378,7 @@ fn iter_next(iter: *fs::iterator) (fs::dirent | void) = {
	};
	let de = &iter.buf[iter.buf_pos]: *rt::dirent64;
	iter.buf_pos += de.d_reclen;
	let name = strings::from_c(&de.d_name: *const char);
	let name = strings::fromc(&de.d_name: *const char);

	let ftype: fs::mode = switch (de.d_type) {
		rt::DT_UNKNOWN => fs::mode::UNKNOWN,

M os/+linux/environ.ha => os/+linux/environ.ha +9 -9
@@ 15,12 15,12 @@ let args_static: [32]str = [""...];
	if (rt::argc < len(args_static)) {
		args = args_static[..rt::argc];
		for (let i = 0z; i < rt::argc; i += 1) {
			args[i] = strings::from_c(rt::argv[i]);
			args[i] = strings::fromc(rt::argv[i]);
		};
	} else {
		args = alloc([], rt::argc);
		for (let i = 0z; i < rt::argc; i += 1) {
			append(args, strings::from_c(rt::argv[i]));
			append(args, strings::fromc(rt::argv[i]));
		};
	};



@@ 42,7 42,7 @@ export fn getenv(name: const str) (str | void) = {
			i: size => i,
		};
		if (bytes::equal(name_b, item[..eq])) {
			const ln = strings::c_strlen(item: *const char);
			const ln = strings::cstrlen(item: *const char);
			return strings::from_utf8(item[eq+1..ln]);
		};
	};


@@ 63,7 63,7 @@ export fn getenvs() []str = {
		return envp;
	};
	for (let i = 0z; rt::envp[i] != null; i += 1) {
		append(envp, strings::from_c(rt::envp[i]: *const char));
		append(envp, strings::fromc(rt::envp[i]: *const char));
	};
	return envp;
};


@@ 76,7 76,7 @@ export fn sysname() const str = {
	if (!uts_valid) {
		rt::uname(&uts) as void;
	};
	return strings::from_c(&uts.sysname: *const char);
	return strings::fromc(&uts.sysname: *const char);
};

// Returns the host system hostname


@@ 84,7 84,7 @@ export fn hostname() const str = {
	if (!uts_valid) {
		rt::uname(&uts) as void;
	};
	return strings::from_c(&uts.nodename: *const char);
	return strings::fromc(&uts.nodename: *const char);
};

// Returns the host kernel version


@@ 92,7 92,7 @@ export fn release() const str = {
	if (!uts_valid) {
		rt::uname(&uts) as void;
	};
	return strings::from_c(&uts.release: *const char);
	return strings::fromc(&uts.release: *const char);
};

// Returns the host operating system version


@@ 100,7 100,7 @@ export fn version() const str = {
	if (!uts_valid) {
		rt::uname(&uts) as void;
	};
	return strings::from_c(&uts.version: *const char);
	return strings::fromc(&uts.version: *const char);
};

// Returns the host CPU architecture


@@ 108,5 108,5 @@ export fn machine() const str = {
	if (!uts_valid) {
		rt::uname(&uts) as void;
	};
	return strings::from_c(&uts.machine: *const char);
	return strings::fromc(&uts.machine: *const char);
};

M os/+linux/fs.ha => os/+linux/fs.ha +1 -1
@@ 17,7 17,7 @@ use strings;
// Returns the current working directory. The return value is statically
// allocated and must be duplicated (see [strings::dup]) before calling getcwd
// again.
export fn getcwd() str = strings::from_c(rt::getcwd() as *const char);
export fn getcwd() str = strings::fromc(rt::getcwd() as *const char);

// Change the current working directory.
export fn chdir(target: (*fs::fs | str)) (void | fs::error) = {

M strings/contains.ha => strings/contains.ha +1 -1
@@ 4,7 4,7 @@ use encoding::utf8;
// Returns true if a string contains a rune or a sub-string.
export fn contains(haystack: str, needle: (str | rune)) bool = match (needle) {
	s: str  => bytes::contains(to_utf8(haystack), to_utf8(s)),
	r: rune => bytes::contains(to_utf8(haystack), utf8::encode_rune(r)),
	r: rune => bytes::contains(to_utf8(haystack), utf8::encoderune(r)),
};

@test fn contains() void = {

M strings/cstrings.ha => strings/cstrings.ha +5 -5
@@ 10,7 10,7 @@ export let c_empty: *const char = &emptybuf: *[*]u8: *const char;

// Computes the length of a NUL-terminated C string, in octets, in O(n). The
// computed length does not include the NUL terminator.
export fn c_strlen(cstr: *const char) size = {
export fn cstrlen(cstr: *const char) size = {
	const ptr = cstr: *[*]u8;
	let ln = 0z;
	for (ptr[ln] != 0; ln += 1) void;


@@ 19,8 19,8 @@ export fn c_strlen(cstr: *const char) size = {

// Converts a C string to a Hare string in O(n), and does not check if it's
// valid UTF-8.
export fn from_c_unsafe(cstr: *const char) const str = {
	const l = c_strlen(cstr);
export fn fromc_unsafe(cstr: *const char) const str = {
	const l = cstrlen(cstr);
	const s = types::string {
		data     = cstr: *[*]u8,
		length   = l,


@@ 31,8 31,8 @@ export fn from_c_unsafe(cstr: *const char) const str = {

// Converts a C string to a Hare string in O(n). If the string is not valid
// UTF-8, abort.
export fn from_c(cstr: *const char) const str = {
	let s = from_c_unsafe(cstr);
export fn fromc(cstr: *const char) const str = {
	let s = fromc_unsafe(cstr);
	assert(utf8::valid(s));
	return s;
};

M strings/dup.ha => strings/dup.ha +1 -1
@@ 24,7 24,7 @@ export fn dup(s: const str) str = {

// Duplicates every string of a slice in place, returning the same slice with
// new strings.
export fn dup_all(s: []str) void = {
export fn dupall(s: []str) void = {
	for (let i = 0z; i < len(s); i += 1) {
		s[i] = strings::dup(s[i]);
	};

M strio/ops.ha => strio/ops.ha +1 -1
@@ 107,4 107,4 @@ export fn rjoin(st: *io::stream, delim: str, strs: str...) (size | io::error) = 

// Appends a rune to a stream.
export fn append_rune(st: *io::stream, r: rune) (size | io::error) =
	io::write(st, utf8::encode_rune(r));
	io::write(st, utf8::encoderune(r));

M time/+linux/functions.ha => time/+linux/functions.ha +1 -1
@@ 64,7 64,7 @@ fn get_cgt_vdso() nullable *fn(_: int, _: *rt::timespec) int = {
		return cgt_vdso;
	vdso_checked = true;

	cgt_vdso = vdso::get_vdso_sym(VDSO_CGT_SYM, VDSO_CGT_VER)
	cgt_vdso = vdso::getsym(VDSO_CGT_SYM, VDSO_CGT_VER)
		: nullable *fn(_: int, _: *rt::timespec) int;
	return cgt_vdso;
};