commit 029f0b187ae1ed7d2153429a117e76ef30ec91ad
parent 4880c021c1752b24fe2ae63df46e479b31394b38
Author: Mallory Adams <malloryadams@fastmail.com>
Date: Fri, 24 May 2024 16:26:11 -0400
NetBSD: port Hare to NetBSD/amd64
Note that NetBSD does not include NTP's leap seconds list. That will
need to be installed separately:
```
ftp https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list
doas mv leap-seconds.list /usr/share/zoneinfo/leap-seconds.list
```
Thanks-to: Lorenz (xha) <me@xha.li>
Signed-off-by: Mallory Adams <malloryadams@fastmail.com>
Diffstat:
68 files changed, 7354 insertions(+), 1 deletion(-)
diff --git a/.builds/netbsd.yml b/.builds/netbsd.yml
@@ -0,0 +1,47 @@
+image: netbsd/latest
+sources:
+- https://git.sr.ht/~sircmpwn/hare
+- https://git.sr.ht/~sircmpwn/harec
+- git://c9x.me/qbe.git
+packages:
+- binutils
+- scdoc
+- git
+triggers:
+- action: email
+ condition: failure
+ to: "<~sircmpwn/hare-dev@lists.sr.ht>"
+tasks:
+- environment: |
+ cd hare
+ if [ "$BUILD_SUBMITTER" = "git.sr.ht" ]
+ then
+ if [ "$GIT_REF" != "refs/heads/master" ]
+ then
+ complete-build
+ fi
+ if [ "$(git remote get-url origin)" != "https://git.sr.ht/~sircmpwn/hare" ]
+ then
+ complete-build
+ fi
+ fi
+- ntp-leapseconds: |
+ ftp https://hpiers.obspm.fr/iers/bul/bulc/ntp/leap-seconds.list
+ doas mv leap-seconds.list /var/db/ntpd.leap-seconds.list
+- qbe: |
+ cd qbe
+ make -j2 PREFIX=/usr
+ doas make install PREFIX=/usr
+- harec: |
+ cd harec
+ cp configs/netbsd.mk config.mk
+ make -j2
+ doas make install
+- hare: |
+ cd hare
+ cp configs/netbsd.mk config.mk
+ make -j2
+ doas make install
+- check: |
+ cd hare
+ make -j2 check
diff --git a/cmd/hare/build/platform.ha b/cmd/hare/build/platform.ha
@@ -19,6 +19,10 @@ const platforms: [_]platform = [
...
},
platform {
+ name = "NetBSD",
+ ...
+ },
+ platform {
name = "OpenBSD",
need_libc = true,
default_flags = [
diff --git a/configs/netbsd.mk b/configs/netbsd.mk
@@ -0,0 +1,43 @@
+# install locations
+PREFIX = /usr/local
+BINDIR = $(PREFIX)/bin
+MANDIR = $(PREFIX)/share/man
+SRCDIR = $(PREFIX)/src
+STDLIB = $(SRCDIR)/hare/stdlib
+
+# variables used during build
+PLATFORM = netbsd
+ARCH = x86_64
+HAREFLAGS =
+HARECFLAGS =
+QBEFLAGS =
+ASFLAGS =
+LDLINKFLAGS = --gc-sections -z noexecstack
+
+# commands used by the build script
+HAREC = harec
+QBE = qbe
+AS = as
+LD = ld
+SCDOC = scdoc
+
+# build locations
+HARECACHE = .cache
+BINOUT = .bin
+
+# variables that will be embedded in the binary with -D definitions
+HAREPATH = $(SRCDIR)/hare/stdlib:$(SRCDIR)/hare/third-party
+VERSION=$$(./scripts/version)
+
+# For cross-compilation, modify the variables below
+AARCH64_AS=as
+AARCH64_CC=cc
+AARCH64_LD=ld
+
+RISCV64_AS=as
+RISCV64_CC=cc
+RISCV64_LD=ld
+
+X86_64_AS=as
+X86_64_CC=cc
+X86_64_LD=ld
diff --git a/crypto/random/+netbsd.ha b/crypto/random/+netbsd.ha
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use rt;
+
+// 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 amount, use [[stream]] instead.
+export fn buffer(buf: []u8) void = {
+ let n = 0z;
+ for (n < len(buf)) {
+ match (rt::getrandom(buf[n..]: *[*]u8, len(buf), 0)) {
+ case let err: rt::errno =>
+ switch (err) {
+ case rt::EINTR => void;
+ case =>
+ abort();
+ };
+ case let z: size =>
+ n += z;
+ };
+ };
+};
+
+fn rand_reader(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
+ assert(s == stream);
+ match (rt::getrandom(buf: *[*]u8, len(buf), 0)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let n: size =>
+ return n;
+ };
+};
diff --git a/debug/+netbsd/+x86_64/ucontext.ha b/debug/+netbsd/+x86_64/ucontext.ha
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use rt;
+
+// Returns the stack pointer from a ucontext.
+fn uctx_sp(uctx: *opaque) uintptr = {
+ const uctx = uctx: *rt::ucontext;
+ return uctx.uc_mcontext.mc_rsp: uintptr;
+};
+
+// Returns the instruction pointer from a ucontext.
+fn uctx_ip(uctx: *opaque) uintptr = {
+ const uctx = uctx: *rt::ucontext;
+ return uctx.uc_mcontext.mc_rip: uintptr;
+};
+
+// Returns the current call frame from a ucontext.
+fn uctx_frame(uctx: *opaque) stackframe = {
+ const uctx = uctx: *rt::ucontext;
+ return *(uctx.uc_mcontext.mc_rbp: uintptr: *stackframe);
+};
diff --git a/debug/+netbsd/translate.ha b/debug/+netbsd/translate.ha
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use rt;
+use types::c;
+
+// Tries to translate a pointer into an ELF address of the currently running
+// binary.
+export fn translate(ptr: uintptr) (uintptr | void) = {
+ // TODO NetBSD (will break when enabling PIE code)
+ return ptr;
+};
diff --git a/debug/image/self+netbsd.ha b/debug/image/self+netbsd.ha
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use fs;
+use io;
+use os;
+use path;
+use rt;
+use types::c;
+
+// Opens the executing process's binary image.
+export fn self() (image | io::error | fs::error) = {
+ // 1: sysctl
+ let buf: [path::MAX * 2 + 1]u8 = [0...];
+ let pathsz = len(buf);
+ match (rt::sysctl([rt::CTL_KERN, rt::KERN_PROC_PATHNAME],
+ &buf[0], &pathsz, null, 0)) {
+ case rt::errno => void;
+ case void =>
+ const file = os::open(c::tostr(&buf[0]: *const c::char)!)?;
+ match (open(file)) {
+ case let img: image =>
+ return img;
+ case let err: io::error =>
+ return err;
+ case errors::invalid =>
+ abort("Running program image is not a valid ELF file");
+ };
+ };
+
+ // 2. procfs (not mounted by default, but better than step 3)
+ match (os::open("/proc/curproc/exe")) {
+ case let file: io::file =>
+ match (open(file)) {
+ case let img: image =>
+ return img;
+ case let err: io::error =>
+ return err;
+ case errors::invalid =>
+ abort("Running program image is not a valid ELF file");
+ };
+ case => void;
+ };
+
+ // 3. Fallback (os::args[0])
+ return self_argv();
+};
diff --git a/format/elf/platform+netbsd.ha b/format/elf/platform+netbsd.ha
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// The ABI of the target.
+export def TARGET_ABI: ident_abi = ident_abi::SYSV;
diff --git a/io/+netbsd/dup.ha b/io/+netbsd/dup.ha
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use rt;
+
+// Flags for [[dup]] and [[dup2]] operations.
+export type dupflag = enum {
+ NONE = 0,
+
+ // Causes [[dup]] and [[dup2]] not to set the CLOEXEC flag on the
+ // duplicated file descriptor. By default, CLOEXEC is set.
+ NOCLOEXEC = rt::FD_CLOEXEC,
+};
+
+// Duplicates a file descriptor.
+export fn dup(old: file, flags: dupflag) (file | error) = {
+ flags ^= dupflag::NOCLOEXEC; // Invert CLOEXEC
+
+ match (rt::dup2(old, -1)) {
+ case let fd: int =>
+ const fl = rt::fcntl(fd, rt::F_GETFD, 0)!;
+ rt::fcntl(fd, rt::F_SETFD, fl | rt::FD_CLOEXEC)!;
+ return fd;
+ case let e: rt::errno =>
+ return errors::errno(e);
+ };
+};
+
+// Duplicates a file descriptor and stores the new file at a specific file
+// descriptor number. If the file indicated by "new" already refers to an open
+// file, this file will be closed before the file descriptor is reused.
+export fn dup2(old: file, new: file, flags: dupflag) (file | error) = {
+ flags ^= dupflag::NOCLOEXEC; // Invert CLOEXEC
+
+ match (rt::dup2(old, new)) {
+ case let fd: int =>
+ const fl = rt::fcntl(fd, rt::F_GETFD, 0)!;
+ rt::fcntl(fd, rt::F_SETFD, fl | flags)!;
+ return fd;
+ case let e: rt::errno =>
+ return errors::errno(e);
+ };
+};
diff --git a/io/+netbsd/mmap.ha b/io/+netbsd/mmap.ha
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use rt;
+
+// Values for the [[mmap]] prot parameter. Only the EXEC, READ, WRITE, and NONE
+// values are portable.
+export type prot = enum uint {
+ NONE = rt::PROT_NONE,
+ READ = rt::PROT_READ,
+ WRITE = rt::PROT_WRITE,
+ EXEC = rt::PROT_EXEC,
+};
+
+// Values for the [[mmap]] flags parameter. Only the SHARED, PRIVATE, and FIXED
+// values are portable.
+export type mflag = enum uint {
+ SHARED = rt::MAP_SHARED,
+ PRIVATE = rt::MAP_PRIVATE,
+ FIXED = rt::MAP_FIXED,
+ HASSEMAPHORE = rt::MAP_HASSEMAPHORE ,
+ STACK = rt::MAP_STACK,
+ NOSYNC = rt::MAP_NOSYNC,
+ FILE = rt::MAP_FILE,
+ ANON = rt::MAP_ANON,
+ GUARD = rt::MAP_GUARD,
+ EXCL = rt::MAP_EXCL,
+ NOCORE = rt::MAP_NOCORE,
+ PREFAULT_READ = rt::MAP_PREFAULT_READ,
+ _32BIT = rt::MAP_32BIT,
+};
+
+// Performs the mmap syscall. Consult your system for documentation on this
+// function.
+export fn mmap(
+ addr: nullable *opaque,
+ length: size,
+ prot: prot,
+ flags: mflag,
+ fd: file,
+ offs: size
+) (*opaque | errors::error) = {
+ match (rt::mmap(addr, length, prot, flags, fd, offs)) {
+ case let ptr: *opaque =>
+ return ptr;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+// Unmaps memory previously mapped with [[mmap]].
+export fn munmap(addr: *opaque, length: size) (void | errors::error) = {
+ match (rt::munmap(addr, length)) {
+ case void =>
+ return;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
diff --git a/io/+netbsd/platform_file.ha b/io/+netbsd/platform_file.ha
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use rt;
+
+// This is an opaque type which encloses an OS-level file handle resource. It
+// can be used as a [[handle]] in most situations, but there are some APIs which
+// require a [[file]] with some OS-level handle backing it - this type is used
+// for such APIs.
+//
+// On NetBSD, [[file]] is a file descriptor.
+export type file = int;
+
+// Opens a Unix file descriptor as a file. This is a low-level interface, to
+// open files most programs will use something like [[os::open]]. This function
+// is not portable.
+export fn fdopen(fd: int) file = fd;
+
+fn fd_read(fd: file, buf: []u8) (size | EOF | error) = {
+ match (rt::read(fd, buf: *[*]u8, len(buf))) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let n: size =>
+ switch (n) {
+ case 0 =>
+ return EOF;
+ case =>
+ return n;
+ };
+ };
+};
+
+fn fd_write(fd: file, buf: const []u8) (size | error) = {
+ match (rt::write(fd, buf: *const [*]u8, len(buf))) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let n: size =>
+ return n;
+ };
+};
+
+fn fd_close(fd: file) (void | error) = {
+ match (rt::close(fd)) {
+ case void => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+fn fd_seek(
+ fd: file,
+ offs: off,
+ whence: whence,
+) (off | error) = {
+ match (rt::lseek(fd, offs: i64, whence: int)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let n: i64 =>
+ return n: off;
+ };
+};
+
+fn fd_copy(to: file, from: file) (size | error) = errors::unsupported;
+
+fn fd_lock(fd: file, flags: int) (bool | error) = {
+ match (rt::flock(fd: int, flags)) {
+ case void => return true;
+ case let e: rt::errno =>
+ if (e == rt::EWOULDBLOCK: rt::errno) {
+ return false;
+ } else {
+ return errors::errno(e);
+ };
+ };
+};
+
+fn fd_trunc(fd: file, ln: size) (void | error) = {
+ match (rt::ftruncate(fd: int, ln: rt::off_t)) {
+ case void => void;
+ case let e: rt::errno => return errors::errno(e);
+ };
+};
diff --git a/io/+netbsd/vector.ha b/io/+netbsd/vector.ha
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use rt;
+use types;
+
+export type vector = rt::iovec;
+
+// Creates a vector for use with [[writev]] and [[readv]].
+export fn mkvector(buf: []u8) vector = vector {
+ iov_base = buf: *[*]u8,
+ iov_len = len(buf),
+};
+
+// Performs a vectored read on the given file. A read is performed on each of
+// the vectors, prepared with [[mkvector]], in order, and the total number of
+// bytes read is returned.
+export fn readv(fd: file, vectors: vector...) (size | EOF | error) = {
+ if (len(vectors) > types::INT_MAX: size) {
+ return errors::invalid;
+ };
+ match (rt::readv(fd, vectors: *[*]rt::iovec, len(vectors): int)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let n: size =>
+ switch (n) {
+ case 0 =>
+ return EOF;
+ case =>
+ return n;
+ };
+ };
+};
+
+// Performs a vectored write on the given file. Each of the vectors, prepared
+// with [[mkvector]], are written to the file in order, and the total number of
+// bytes written is returned.
+export fn writev(fd: file, vectors: vector...) (size | error) = {
+ if (len(vectors) > types::INT_MAX: size) {
+ return errors::invalid;
+ };
+ match (rt::writev(fd, vectors: *[*]rt::iovec, len(vectors): int)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let n: size =>
+ return n;
+ };
+};
diff --git a/makefiles/netbsd.aarch64.mk b/makefiles/netbsd.aarch64.mk
@@ -0,0 +1,256 @@
+# generated by cmd/genbootstrap
+# DO NOT EDIT BY HAND. run 'make bootstrap' to update
+TDENV = env HARE_TD_rt=$(HARECACHE)/rt.td HARE_TD_encoding::utf8=$(HARECACHE)/encoding_utf8.td HARE_TD_sort::cmp=$(HARECACHE)/sort_cmp.td HARE_TD_types=$(HARECACHE)/types.td HARE_TD_bytes=$(HARECACHE)/bytes.td HARE_TD_strings=$(HARECACHE)/strings.td HARE_TD_ascii=$(HARECACHE)/ascii.td HARE_TD_errors=$(HARECACHE)/errors.td HARE_TD_io=$(HARECACHE)/io.td HARE_TD_bufio=$(HARECACHE)/bufio.td HARE_TD_crypto::math=$(HARECACHE)/crypto_math.td HARE_TD_endian=$(HARECACHE)/endian.td HARE_TD_hash=$(HARECACHE)/hash.td HARE_TD_crypto::sha256=$(HARECACHE)/crypto_sha256.td HARE_TD_math=$(HARECACHE)/math.td HARE_TD_memio=$(HARECACHE)/memio.td HARE_TD_path=$(HARECACHE)/path.td HARE_TD_time=$(HARECACHE)/time.td HARE_TD_fs=$(HARECACHE)/fs.td HARE_TD_types::c=$(HARECACHE)/types_c.td HARE_TD_os=$(HARECACHE)/os.td HARE_TD_strconv=$(HARECACHE)/strconv.td HARE_TD_fmt=$(HARECACHE)/fmt.td HARE_TD_encoding::hex=$(HARECACHE)/encoding_hex.td HARE_TD_sort=$(HARECACHE)/sort.td HARE_TD_hare::lex=$(HARECACHE)/hare_lex.td HARE_TD_hare::ast=$(HARECACHE)/hare_ast.td HARE_TD_hare::parse=$(HARECACHE)/hare_parse.td HARE_TD_hare::unparse=$(HARECACHE)/hare_unparse.td HARE_TD_time::chrono=$(HARECACHE)/time_chrono.td HARE_TD_time::date=$(HARECACHE)/time_date.td HARE_TD_hare::module=$(HARECACHE)/hare_module.td HARE_TD_unix=$(HARECACHE)/unix.td HARE_TD_unix::signal=$(HARECACHE)/unix_signal.td HARE_TD_os::exec=$(HARECACHE)/os_exec.td HARE_TD_shlex=$(HARECACHE)/shlex.td HARE_TD_unix::tty=$(HARECACHE)/unix_tty.td HARE_TD_cmd::hare::build=$(HARECACHE)/cmd_hare_build.td HARE_TD_dirs=$(HARECACHE)/dirs.td HARE_TD_getopt=$(HARECACHE)/getopt.td HARE_TD_cmd::hare=$(HARECACHE)/cmd_hare.td
+RTSCRIPT = rt/+netbsd/hare.sc
+OBJS = $(HARECACHE)/rt.o $(HARECACHE)/encoding_utf8.o $(HARECACHE)/sort_cmp.o $(HARECACHE)/types.o $(HARECACHE)/bytes.o $(HARECACHE)/strings.o $(HARECACHE)/ascii.o $(HARECACHE)/errors.o $(HARECACHE)/io.o $(HARECACHE)/bufio.o $(HARECACHE)/crypto_math.o $(HARECACHE)/endian.o $(HARECACHE)/hash.o $(HARECACHE)/crypto_sha256.o $(HARECACHE)/math.o $(HARECACHE)/memio.o $(HARECACHE)/path.o $(HARECACHE)/time.o $(HARECACHE)/fs.o $(HARECACHE)/types_c.o $(HARECACHE)/os.o $(HARECACHE)/strconv.o $(HARECACHE)/fmt.o $(HARECACHE)/encoding_hex.o $(HARECACHE)/sort.o $(HARECACHE)/hare_lex.o $(HARECACHE)/hare_ast.o $(HARECACHE)/hare_parse.o $(HARECACHE)/hare_unparse.o $(HARECACHE)/time_chrono.o $(HARECACHE)/time_date.o $(HARECACHE)/hare_module.o $(HARECACHE)/unix.o $(HARECACHE)/unix_signal.o $(HARECACHE)/os_exec.o $(HARECACHE)/shlex.o $(HARECACHE)/unix_tty.o $(HARECACHE)/cmd_hare_build.o $(HARECACHE)/dirs.o $(HARECACHE)/getopt.o $(HARECACHE)/cmd_hare.o
+
+rt_ha = rt/+aarch64/arch_jmp.ha rt/+aarch64/cpuid.ha rt/+netbsd/env.ha rt/+netbsd/errno.ha rt/+netbsd/initfini.ha rt/+netbsd/platform_abort.ha rt/+netbsd/platformstart-libc.ha rt/+netbsd/segmalloc.ha rt/+netbsd/signal.ha rt/+netbsd/socket.ha rt/+netbsd/start.ha rt/+netbsd/syscallno.ha rt/+netbsd/syscalls.ha rt/+netbsd/sysctl.ha rt/+netbsd/types.ha rt/abort.ha rt/ensure.ha rt/fenv_defs.ha rt/jmp.ha rt/malloc.ha rt/memcpy.ha rt/memfunc_ptr.ha rt/memmove.ha rt/memset.ha rt/strcmp.ha rt/u64tos.ha rt/unknown_errno.ha
+$(HARECACHE)/rt.ssa: $(rt_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/rt.td.tmp -N rt $(rt_ha)
+
+rt_s = $(HARECACHE)/rt.s rt/+aarch64/cpuid.s rt/+aarch64/fenv.s rt/+aarch64/longjmp.s rt/+aarch64/setjmp.s
+$(HARECACHE)/rt.o: $(rt_s)
+ @printf 'AS\t%s\n' "$@"
+ @$(AS) $(ASFLAGS) -o $@ $(rt_s)
+
+encoding_utf8_ha = encoding/utf8/decode.ha encoding/utf8/decodetable.ha encoding/utf8/encode.ha encoding/utf8/rune.ha encoding/utf8/types.ha
+$(HARECACHE)/encoding_utf8.ssa: $(encoding_utf8_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/encoding_utf8.td.tmp -N encoding::utf8 $(encoding_utf8_ha)
+
+sort_cmp_ha = sort/cmp/cmp.ha
+$(HARECACHE)/sort_cmp.ssa: $(sort_cmp_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/sort_cmp.td.tmp -N sort::cmp $(sort_cmp_ha)
+
+types_ha = types/arch+aarch64.ha types/classes.ha types/limits.ha
+$(HARECACHE)/types.ssa: $(types_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/types.td.tmp -N types $(types_ha)
+
+bytes_ha = bytes/contains.ha bytes/equal.ha bytes/index.ha bytes/reverse.ha bytes/tokenize.ha bytes/trim.ha bytes/two_way.ha bytes/zero.ha
+$(HARECACHE)/bytes.ssa: $(bytes_ha) $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/bytes.td.tmp -N bytes $(bytes_ha)
+
+strings_ha = strings/compare.ha strings/concat.ha strings/contains.ha strings/dup.ha strings/index.ha strings/iter.ha strings/pad.ha strings/replace.ha strings/runes.ha strings/sub.ha strings/suffix.ha strings/tokenize.ha strings/trim.ha strings/utf8.ha
+$(HARECACHE)/strings.ssa: $(strings_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/strings.td.tmp -N strings $(strings_ha)
+
+ascii_ha = ascii/ctype.ha ascii/string.ha ascii/valid.ha
+$(HARECACHE)/ascii.ssa: $(ascii_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/ascii.td.tmp -N ascii $(ascii_ha)
+
+errors_ha = errors/common.ha errors/opaque.ha errors/rt.ha errors/string.ha
+$(HARECACHE)/errors.ssa: $(errors_ha) $(HARECACHE)/rt.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/errors.td.tmp -N errors $(errors_ha)
+
+io_ha = io/+netbsd/dup.ha io/+netbsd/mmap.ha io/+netbsd/platform_file.ha io/+netbsd/vector.ha io/arch+aarch64.ha io/copy.ha io/drain.ha io/empty.ha io/file.ha io/handle.ha io/limit.ha io/stream.ha io/tee.ha io/types.ha io/util.ha io/zero.ha
+$(HARECACHE)/io.ssa: $(io_ha) $(HARECACHE)/bytes.td $(HARECACHE)/errors.td $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/io.td.tmp -N io $(io_ha)
+
+bufio_ha = bufio/scanner.ha bufio/stream.ha
+$(HARECACHE)/bufio.ssa: $(bufio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/bufio.td.tmp -N bufio $(bufio_ha)
+
+crypto_math_ha = crypto/math/arithm.ha crypto/math/bits.ha
+$(HARECACHE)/crypto_math.ssa: $(crypto_math_ha) $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/crypto_math.td.tmp -N crypto::math $(crypto_math_ha)
+
+endian_ha = endian/big.ha endian/endian.ha endian/host+aarch64.ha endian/little.ha endian/network.ha
+$(HARECACHE)/endian.ssa: $(endian_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/endian.td.tmp -N endian $(endian_ha)
+
+hash_ha = hash/hash.ha
+$(HARECACHE)/hash.ssa: $(hash_ha) $(HARECACHE)/io.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hash.td.tmp -N hash $(hash_ha)
+
+crypto_sha256_ha = crypto/sha256/sha256.ha
+$(HARECACHE)/crypto_sha256.ssa: $(crypto_sha256_ha) $(HARECACHE)/bytes.td $(HARECACHE)/crypto_math.td $(HARECACHE)/endian.td $(HARECACHE)/hash.td $(HARECACHE)/io.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/crypto_sha256.td.tmp -N crypto::sha256 $(crypto_sha256_ha)
+
+math_ha = math/fenv+aarch64.ha math/fenv_func.ha math/floats.ha math/ints.ha math/math.ha math/trig.ha math/uints.ha
+$(HARECACHE)/math.ssa: $(math_ha) $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/math.td.tmp -N math $(math_ha)
+
+memio_ha = memio/ops.ha memio/stream.ha
+$(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
+
+path_ha = path/+netbsd.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/path.td.tmp -N path $(path_ha)
+
+time_ha = time/+netbsd/functions.ha time/arithm.ha time/conv.ha time/types.ha
+$(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
+
+fs_ha = fs/fs.ha fs/types.ha fs/util.ha
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
+
+types_c_ha = types/c/arch+aarch64.ha types/c/strings.ha types/c/types.ha
+$(HARECACHE)/types_c.ssa: $(types_c_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/types_c.td.tmp -N types::c $(types_c_ha)
+
+os_ha = os/+netbsd/dirfdfs.ha os/+netbsd/exit.ha os/+netbsd/fs.ha os/+netbsd/platform_environ.ha os/+netbsd/status.ha os/+netbsd/stdfd.ha os/environ.ha os/os.ha
+$(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/types_c.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha)
+
+strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha
+$(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/strconv.td.tmp -N strconv $(strconv_ha)
+
+fmt_ha = fmt/iter.ha fmt/print.ha fmt/wrappers.ha
+$(HARECACHE)/fmt.ssa: $(fmt_ha) $(HARECACHE)/ascii.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/fmt.td.tmp -N fmt $(fmt_ha)
+
+encoding_hex_ha = encoding/hex/hex.ha
+$(HARECACHE)/encoding_hex.ssa: $(encoding_hex_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/encoding_hex.td.tmp -N encoding::hex $(encoding_hex_ha)
+
+sort_ha = sort/bisect.ha sort/search.ha sort/sort.ha sort/types.ha
+$(HARECACHE)/sort.ssa: $(sort_ha) $(HARECACHE)/math.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/sort.td.tmp -N sort $(sort_ha)
+
+hare_lex_ha = hare/lex/lex.ha hare/lex/token.ha
+$(HARECACHE)/hare_lex.ssa: $(hare_lex_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_lex.td.tmp -N hare::lex $(hare_lex_ha)
+
+hare_ast_ha = hare/ast/decl.ha hare/ast/expr.ha hare/ast/ident.ha hare/ast/import.ha hare/ast/type.ha hare/ast/unit.ha
+$(HARECACHE)/hare_ast.ssa: $(hare_ast_ha) $(HARECACHE)/hare_lex.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_ast.td.tmp -N hare::ast $(hare_ast_ha)
+
+hare_parse_ha = hare/parse/decl.ha hare/parse/expr.ha hare/parse/ident.ha hare/parse/import.ha hare/parse/parse.ha hare/parse/type.ha hare/parse/unit.ha
+$(HARECACHE)/hare_parse.ssa: $(hare_parse_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/fmt.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_parse.td.tmp -N hare::parse $(hare_parse_ha)
+
+hare_unparse_ha = hare/unparse/decl.ha hare/unparse/expr.ha hare/unparse/ident.ha hare/unparse/import.ha hare/unparse/syn.ha hare/unparse/type.ha hare/unparse/unit.ha hare/unparse/util.ha
+$(HARECACHE)/hare_unparse.ssa: $(hare_unparse_ha) $(HARECACHE)/fmt.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_unparse.td.tmp -N hare::unparse $(hare_unparse_ha)
+
+time_chrono_ha = time/chrono/+netbsd.ha time/chrono/arithmetic.ha time/chrono/chronology.ha time/chrono/error.ha time/chrono/leapsec.ha time/chrono/timescale.ha time/chrono/timezone.ha time/chrono/tzdb.ha
+$(HARECACHE)/time_chrono.ssa: $(time_chrono_ha) $(HARECACHE)/bufio.td $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/endian.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/time_chrono.td.tmp -N time::chrono $(time_chrono_ha)
+
+time_date_ha = time/date/constants.ha time/date/date.ha time/date/daydate.ha time/date/daytime.ha time/date/error.ha time/date/format.ha time/date/locality.ha time/date/observe.ha time/date/parithm.ha time/date/parse.ha time/date/period.ha time/date/reckon.ha time/date/tarithm.ha time/date/virtual.ha
+$(HARECACHE)/time_date.ssa: $(time_date_ha) $(HARECACHE)/ascii.td $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/sort.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/time_chrono.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/time_date.td.tmp -N time::date $(time_date_ha)
+
+hare_module_ha = hare/module/cache.ha hare/module/deps.ha hare/module/format.ha hare/module/srcs.ha hare/module/types.ha hare/module/util.ha
+$(HARECACHE)/hare_module.ssa: $(hare_module_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/hare_parse.td $(HARECACHE)/hare_unparse.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/time_chrono.td $(HARECACHE)/time_date.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_module.td.tmp -N hare::module $(hare_module_ha)
+
+unix_ha = unix/+netbsd/creds.ha unix/+netbsd/nice.ha unix/+netbsd/pipe.ha unix/+netbsd/umask.ha
+$(HARECACHE)/unix.ssa: $(unix_ha) $(HARECACHE)/errors.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/rt.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/unix.td.tmp -N unix $(unix_ha)
+
+unix_signal_ha = unix/signal/+netbsd.ha unix/signal/types.ha
+$(HARECACHE)/unix_signal.ssa: $(unix_signal_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/rt.td $(HARECACHE)/time.td $(HARECACHE)/unix.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/unix_signal.td.tmp -N unix::signal $(unix_signal_ha)
+
+os_exec_ha = os/exec/+netbsd/exec.ha os/exec/+netbsd/platform_cmd.ha os/exec/+netbsd/process.ha os/exec/cmd.ha os/exec/types.ha
+$(HARECACHE)/os_exec.ssa: $(os_exec_ha) $(HARECACHE)/ascii.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/types_c.td $(HARECACHE)/unix.td $(HARECACHE)/unix_signal.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os_exec.td.tmp -N os::exec $(os_exec_ha)
+
+shlex_ha = shlex/escape.ha shlex/split.ha
+$(HARECACHE)/shlex.ssa: $(shlex_ha) $(HARECACHE)/ascii.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/shlex.td.tmp -N shlex $(shlex_ha)
+
+unix_tty_ha = unix/tty/+netbsd/isatty.ha unix/tty/+netbsd/open.ha unix/tty/+netbsd/pgid.ha unix/tty/+netbsd/pty.ha unix/tty/+netbsd/termios.ha unix/tty/+netbsd/winsize.ha unix/tty/pty_test.ha unix/tty/types.ha
+$(HARECACHE)/unix_tty.ssa: $(unix_tty_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/os_exec.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td $(HARECACHE)/types_c.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/unix_tty.td.tmp -N unix::tty $(unix_tty_ha)
+
+cmd_hare_build_ha = cmd/hare/build/gather.ha cmd/hare/build/platform.ha cmd/hare/build/queue.ha cmd/hare/build/types.ha cmd/hare/build/util.ha
+$(HARECACHE)/cmd_hare_build.ssa: $(cmd_hare_build_ha) $(HARECACHE)/crypto_sha256.td $(HARECACHE)/encoding_hex.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_module.td $(HARECACHE)/hare_unparse.td $(HARECACHE)/hash.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/os_exec.td $(HARECACHE)/path.td $(HARECACHE)/shlex.td $(HARECACHE)/sort.td $(HARECACHE)/strings.td $(HARECACHE)/unix_tty.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/cmd_hare_build.td.tmp -N cmd::hare::build $(cmd_hare_build_ha)
+
+dirs_ha = dirs/xdg.ha
+$(HARECACHE)/dirs.ssa: $(dirs_ha) $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/unix.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/dirs.td.tmp -N dirs $(dirs_ha)
+
+getopt_ha = getopt/getopts.ha
+$(HARECACHE)/getopt.ssa: $(getopt_ha) $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/getopt.td.tmp -N getopt $(getopt_ha)
+
+cmd_hare_ha = cmd/hare/arch.ha cmd/hare/build.ha cmd/hare/cache.ha cmd/hare/deps.ha cmd/hare/error.ha cmd/hare/main.ha cmd/hare/util.ha cmd/hare/version.ha
+$(HARECACHE)/cmd_hare.ssa: $(cmd_hare_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/cmd_hare_build.td $(HARECACHE)/dirs.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/getopt.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/hare_module.td $(HARECACHE)/hare_parse.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/os_exec.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/unix_tty.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/cmd_hare.td.tmp $(HARE_DEFINES) $(cmd_hare_ha)
diff --git a/makefiles/netbsd.riscv64.mk b/makefiles/netbsd.riscv64.mk
@@ -0,0 +1,256 @@
+# generated by cmd/genbootstrap
+# DO NOT EDIT BY HAND. run 'make bootstrap' to update
+TDENV = env HARE_TD_rt=$(HARECACHE)/rt.td HARE_TD_encoding::utf8=$(HARECACHE)/encoding_utf8.td HARE_TD_sort::cmp=$(HARECACHE)/sort_cmp.td HARE_TD_types=$(HARECACHE)/types.td HARE_TD_bytes=$(HARECACHE)/bytes.td HARE_TD_strings=$(HARECACHE)/strings.td HARE_TD_ascii=$(HARECACHE)/ascii.td HARE_TD_errors=$(HARECACHE)/errors.td HARE_TD_io=$(HARECACHE)/io.td HARE_TD_bufio=$(HARECACHE)/bufio.td HARE_TD_crypto::math=$(HARECACHE)/crypto_math.td HARE_TD_endian=$(HARECACHE)/endian.td HARE_TD_hash=$(HARECACHE)/hash.td HARE_TD_crypto::sha256=$(HARECACHE)/crypto_sha256.td HARE_TD_math=$(HARECACHE)/math.td HARE_TD_memio=$(HARECACHE)/memio.td HARE_TD_path=$(HARECACHE)/path.td HARE_TD_time=$(HARECACHE)/time.td HARE_TD_fs=$(HARECACHE)/fs.td HARE_TD_types::c=$(HARECACHE)/types_c.td HARE_TD_os=$(HARECACHE)/os.td HARE_TD_strconv=$(HARECACHE)/strconv.td HARE_TD_fmt=$(HARECACHE)/fmt.td HARE_TD_encoding::hex=$(HARECACHE)/encoding_hex.td HARE_TD_sort=$(HARECACHE)/sort.td HARE_TD_hare::lex=$(HARECACHE)/hare_lex.td HARE_TD_hare::ast=$(HARECACHE)/hare_ast.td HARE_TD_hare::parse=$(HARECACHE)/hare_parse.td HARE_TD_hare::unparse=$(HARECACHE)/hare_unparse.td HARE_TD_time::chrono=$(HARECACHE)/time_chrono.td HARE_TD_time::date=$(HARECACHE)/time_date.td HARE_TD_hare::module=$(HARECACHE)/hare_module.td HARE_TD_unix=$(HARECACHE)/unix.td HARE_TD_unix::signal=$(HARECACHE)/unix_signal.td HARE_TD_os::exec=$(HARECACHE)/os_exec.td HARE_TD_shlex=$(HARECACHE)/shlex.td HARE_TD_unix::tty=$(HARECACHE)/unix_tty.td HARE_TD_cmd::hare::build=$(HARECACHE)/cmd_hare_build.td HARE_TD_dirs=$(HARECACHE)/dirs.td HARE_TD_getopt=$(HARECACHE)/getopt.td HARE_TD_cmd::hare=$(HARECACHE)/cmd_hare.td
+RTSCRIPT = rt/+netbsd/hare.sc
+OBJS = $(HARECACHE)/rt.o $(HARECACHE)/encoding_utf8.o $(HARECACHE)/sort_cmp.o $(HARECACHE)/types.o $(HARECACHE)/bytes.o $(HARECACHE)/strings.o $(HARECACHE)/ascii.o $(HARECACHE)/errors.o $(HARECACHE)/io.o $(HARECACHE)/bufio.o $(HARECACHE)/crypto_math.o $(HARECACHE)/endian.o $(HARECACHE)/hash.o $(HARECACHE)/crypto_sha256.o $(HARECACHE)/math.o $(HARECACHE)/memio.o $(HARECACHE)/path.o $(HARECACHE)/time.o $(HARECACHE)/fs.o $(HARECACHE)/types_c.o $(HARECACHE)/os.o $(HARECACHE)/strconv.o $(HARECACHE)/fmt.o $(HARECACHE)/encoding_hex.o $(HARECACHE)/sort.o $(HARECACHE)/hare_lex.o $(HARECACHE)/hare_ast.o $(HARECACHE)/hare_parse.o $(HARECACHE)/hare_unparse.o $(HARECACHE)/time_chrono.o $(HARECACHE)/time_date.o $(HARECACHE)/hare_module.o $(HARECACHE)/unix.o $(HARECACHE)/unix_signal.o $(HARECACHE)/os_exec.o $(HARECACHE)/shlex.o $(HARECACHE)/unix_tty.o $(HARECACHE)/cmd_hare_build.o $(HARECACHE)/dirs.o $(HARECACHE)/getopt.o $(HARECACHE)/cmd_hare.o
+
+rt_ha = rt/+netbsd/env.ha rt/+netbsd/errno.ha rt/+netbsd/initfini.ha rt/+netbsd/platform_abort.ha rt/+netbsd/platformstart-libc.ha rt/+netbsd/segmalloc.ha rt/+netbsd/signal.ha rt/+netbsd/socket.ha rt/+netbsd/start.ha rt/+netbsd/syscallno.ha rt/+netbsd/syscalls.ha rt/+netbsd/sysctl.ha rt/+netbsd/types.ha rt/+riscv64/arch_jmp.ha rt/+riscv64/cpuid.ha rt/abort.ha rt/ensure.ha rt/fenv_defs.ha rt/jmp.ha rt/malloc.ha rt/memcpy.ha rt/memfunc_ptr.ha rt/memmove.ha rt/memset.ha rt/strcmp.ha rt/u64tos.ha rt/unknown_errno.ha
+$(HARECACHE)/rt.ssa: $(rt_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/rt.td.tmp -N rt $(rt_ha)
+
+rt_s = $(HARECACHE)/rt.s rt/+riscv64/cpuid.s rt/+riscv64/fenv.s rt/+riscv64/longjmp.s rt/+riscv64/setjmp.s
+$(HARECACHE)/rt.o: $(rt_s)
+ @printf 'AS\t%s\n' "$@"
+ @$(AS) $(ASFLAGS) -o $@ $(rt_s)
+
+encoding_utf8_ha = encoding/utf8/decode.ha encoding/utf8/decodetable.ha encoding/utf8/encode.ha encoding/utf8/rune.ha encoding/utf8/types.ha
+$(HARECACHE)/encoding_utf8.ssa: $(encoding_utf8_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/encoding_utf8.td.tmp -N encoding::utf8 $(encoding_utf8_ha)
+
+sort_cmp_ha = sort/cmp/cmp.ha
+$(HARECACHE)/sort_cmp.ssa: $(sort_cmp_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/sort_cmp.td.tmp -N sort::cmp $(sort_cmp_ha)
+
+types_ha = types/arch+riscv64.ha types/classes.ha types/limits.ha
+$(HARECACHE)/types.ssa: $(types_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/types.td.tmp -N types $(types_ha)
+
+bytes_ha = bytes/contains.ha bytes/equal.ha bytes/index.ha bytes/reverse.ha bytes/tokenize.ha bytes/trim.ha bytes/two_way.ha bytes/zero.ha
+$(HARECACHE)/bytes.ssa: $(bytes_ha) $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/bytes.td.tmp -N bytes $(bytes_ha)
+
+strings_ha = strings/compare.ha strings/concat.ha strings/contains.ha strings/dup.ha strings/index.ha strings/iter.ha strings/pad.ha strings/replace.ha strings/runes.ha strings/sub.ha strings/suffix.ha strings/tokenize.ha strings/trim.ha strings/utf8.ha
+$(HARECACHE)/strings.ssa: $(strings_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/strings.td.tmp -N strings $(strings_ha)
+
+ascii_ha = ascii/ctype.ha ascii/string.ha ascii/valid.ha
+$(HARECACHE)/ascii.ssa: $(ascii_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/ascii.td.tmp -N ascii $(ascii_ha)
+
+errors_ha = errors/common.ha errors/opaque.ha errors/rt.ha errors/string.ha
+$(HARECACHE)/errors.ssa: $(errors_ha) $(HARECACHE)/rt.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/errors.td.tmp -N errors $(errors_ha)
+
+io_ha = io/+netbsd/dup.ha io/+netbsd/mmap.ha io/+netbsd/platform_file.ha io/+netbsd/vector.ha io/arch+riscv64.ha io/copy.ha io/drain.ha io/empty.ha io/file.ha io/handle.ha io/limit.ha io/stream.ha io/tee.ha io/types.ha io/util.ha io/zero.ha
+$(HARECACHE)/io.ssa: $(io_ha) $(HARECACHE)/bytes.td $(HARECACHE)/errors.td $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/io.td.tmp -N io $(io_ha)
+
+bufio_ha = bufio/scanner.ha bufio/stream.ha
+$(HARECACHE)/bufio.ssa: $(bufio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/bufio.td.tmp -N bufio $(bufio_ha)
+
+crypto_math_ha = crypto/math/arithm.ha crypto/math/bits.ha
+$(HARECACHE)/crypto_math.ssa: $(crypto_math_ha) $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/crypto_math.td.tmp -N crypto::math $(crypto_math_ha)
+
+endian_ha = endian/big.ha endian/endian.ha endian/host+riscv64.ha endian/little.ha endian/network.ha
+$(HARECACHE)/endian.ssa: $(endian_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/endian.td.tmp -N endian $(endian_ha)
+
+hash_ha = hash/hash.ha
+$(HARECACHE)/hash.ssa: $(hash_ha) $(HARECACHE)/io.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hash.td.tmp -N hash $(hash_ha)
+
+crypto_sha256_ha = crypto/sha256/sha256.ha
+$(HARECACHE)/crypto_sha256.ssa: $(crypto_sha256_ha) $(HARECACHE)/bytes.td $(HARECACHE)/crypto_math.td $(HARECACHE)/endian.td $(HARECACHE)/hash.td $(HARECACHE)/io.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/crypto_sha256.td.tmp -N crypto::sha256 $(crypto_sha256_ha)
+
+math_ha = math/fenv+riscv64.ha math/fenv_func.ha math/floats.ha math/ints.ha math/math.ha math/trig.ha math/uints.ha
+$(HARECACHE)/math.ssa: $(math_ha) $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/math.td.tmp -N math $(math_ha)
+
+memio_ha = memio/ops.ha memio/stream.ha
+$(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
+
+path_ha = path/+netbsd.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/path.td.tmp -N path $(path_ha)
+
+time_ha = time/+netbsd/functions.ha time/arithm.ha time/conv.ha time/types.ha
+$(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
+
+fs_ha = fs/fs.ha fs/types.ha fs/util.ha
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
+
+types_c_ha = types/c/arch+riscv64.ha types/c/strings.ha types/c/types.ha
+$(HARECACHE)/types_c.ssa: $(types_c_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/types_c.td.tmp -N types::c $(types_c_ha)
+
+os_ha = os/+netbsd/dirfdfs.ha os/+netbsd/exit.ha os/+netbsd/fs.ha os/+netbsd/platform_environ.ha os/+netbsd/status.ha os/+netbsd/stdfd.ha os/environ.ha os/os.ha
+$(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/types_c.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha)
+
+strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha
+$(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/strconv.td.tmp -N strconv $(strconv_ha)
+
+fmt_ha = fmt/iter.ha fmt/print.ha fmt/wrappers.ha
+$(HARECACHE)/fmt.ssa: $(fmt_ha) $(HARECACHE)/ascii.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/fmt.td.tmp -N fmt $(fmt_ha)
+
+encoding_hex_ha = encoding/hex/hex.ha
+$(HARECACHE)/encoding_hex.ssa: $(encoding_hex_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/encoding_hex.td.tmp -N encoding::hex $(encoding_hex_ha)
+
+sort_ha = sort/bisect.ha sort/search.ha sort/sort.ha sort/types.ha
+$(HARECACHE)/sort.ssa: $(sort_ha) $(HARECACHE)/math.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/sort.td.tmp -N sort $(sort_ha)
+
+hare_lex_ha = hare/lex/lex.ha hare/lex/token.ha
+$(HARECACHE)/hare_lex.ssa: $(hare_lex_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_lex.td.tmp -N hare::lex $(hare_lex_ha)
+
+hare_ast_ha = hare/ast/decl.ha hare/ast/expr.ha hare/ast/ident.ha hare/ast/import.ha hare/ast/type.ha hare/ast/unit.ha
+$(HARECACHE)/hare_ast.ssa: $(hare_ast_ha) $(HARECACHE)/hare_lex.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_ast.td.tmp -N hare::ast $(hare_ast_ha)
+
+hare_parse_ha = hare/parse/decl.ha hare/parse/expr.ha hare/parse/ident.ha hare/parse/import.ha hare/parse/parse.ha hare/parse/type.ha hare/parse/unit.ha
+$(HARECACHE)/hare_parse.ssa: $(hare_parse_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/fmt.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_parse.td.tmp -N hare::parse $(hare_parse_ha)
+
+hare_unparse_ha = hare/unparse/decl.ha hare/unparse/expr.ha hare/unparse/ident.ha hare/unparse/import.ha hare/unparse/syn.ha hare/unparse/type.ha hare/unparse/unit.ha hare/unparse/util.ha
+$(HARECACHE)/hare_unparse.ssa: $(hare_unparse_ha) $(HARECACHE)/fmt.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_unparse.td.tmp -N hare::unparse $(hare_unparse_ha)
+
+time_chrono_ha = time/chrono/+netbsd.ha time/chrono/arithmetic.ha time/chrono/chronology.ha time/chrono/error.ha time/chrono/leapsec.ha time/chrono/timescale.ha time/chrono/timezone.ha time/chrono/tzdb.ha
+$(HARECACHE)/time_chrono.ssa: $(time_chrono_ha) $(HARECACHE)/bufio.td $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/endian.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/time_chrono.td.tmp -N time::chrono $(time_chrono_ha)
+
+time_date_ha = time/date/constants.ha time/date/date.ha time/date/daydate.ha time/date/daytime.ha time/date/error.ha time/date/format.ha time/date/locality.ha time/date/observe.ha time/date/parithm.ha time/date/parse.ha time/date/period.ha time/date/reckon.ha time/date/tarithm.ha time/date/virtual.ha
+$(HARECACHE)/time_date.ssa: $(time_date_ha) $(HARECACHE)/ascii.td $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/sort.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/time_chrono.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/time_date.td.tmp -N time::date $(time_date_ha)
+
+hare_module_ha = hare/module/cache.ha hare/module/deps.ha hare/module/format.ha hare/module/srcs.ha hare/module/types.ha hare/module/util.ha
+$(HARECACHE)/hare_module.ssa: $(hare_module_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/hare_parse.td $(HARECACHE)/hare_unparse.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/time_chrono.td $(HARECACHE)/time_date.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_module.td.tmp -N hare::module $(hare_module_ha)
+
+unix_ha = unix/+netbsd/creds.ha unix/+netbsd/nice.ha unix/+netbsd/pipe.ha unix/+netbsd/umask.ha
+$(HARECACHE)/unix.ssa: $(unix_ha) $(HARECACHE)/errors.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/rt.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/unix.td.tmp -N unix $(unix_ha)
+
+unix_signal_ha = unix/signal/+netbsd.ha unix/signal/types.ha
+$(HARECACHE)/unix_signal.ssa: $(unix_signal_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/rt.td $(HARECACHE)/time.td $(HARECACHE)/unix.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/unix_signal.td.tmp -N unix::signal $(unix_signal_ha)
+
+os_exec_ha = os/exec/+netbsd/exec.ha os/exec/+netbsd/platform_cmd.ha os/exec/+netbsd/process.ha os/exec/cmd.ha os/exec/types.ha
+$(HARECACHE)/os_exec.ssa: $(os_exec_ha) $(HARECACHE)/ascii.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/types_c.td $(HARECACHE)/unix.td $(HARECACHE)/unix_signal.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os_exec.td.tmp -N os::exec $(os_exec_ha)
+
+shlex_ha = shlex/escape.ha shlex/split.ha
+$(HARECACHE)/shlex.ssa: $(shlex_ha) $(HARECACHE)/ascii.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/shlex.td.tmp -N shlex $(shlex_ha)
+
+unix_tty_ha = unix/tty/+netbsd/isatty.ha unix/tty/+netbsd/open.ha unix/tty/+netbsd/pgid.ha unix/tty/+netbsd/pty.ha unix/tty/+netbsd/termios.ha unix/tty/+netbsd/winsize.ha unix/tty/pty_test.ha unix/tty/types.ha
+$(HARECACHE)/unix_tty.ssa: $(unix_tty_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/os_exec.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td $(HARECACHE)/types_c.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/unix_tty.td.tmp -N unix::tty $(unix_tty_ha)
+
+cmd_hare_build_ha = cmd/hare/build/gather.ha cmd/hare/build/platform.ha cmd/hare/build/queue.ha cmd/hare/build/types.ha cmd/hare/build/util.ha
+$(HARECACHE)/cmd_hare_build.ssa: $(cmd_hare_build_ha) $(HARECACHE)/crypto_sha256.td $(HARECACHE)/encoding_hex.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_module.td $(HARECACHE)/hare_unparse.td $(HARECACHE)/hash.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/os_exec.td $(HARECACHE)/path.td $(HARECACHE)/shlex.td $(HARECACHE)/sort.td $(HARECACHE)/strings.td $(HARECACHE)/unix_tty.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/cmd_hare_build.td.tmp -N cmd::hare::build $(cmd_hare_build_ha)
+
+dirs_ha = dirs/xdg.ha
+$(HARECACHE)/dirs.ssa: $(dirs_ha) $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/unix.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/dirs.td.tmp -N dirs $(dirs_ha)
+
+getopt_ha = getopt/getopts.ha
+$(HARECACHE)/getopt.ssa: $(getopt_ha) $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/getopt.td.tmp -N getopt $(getopt_ha)
+
+cmd_hare_ha = cmd/hare/arch.ha cmd/hare/build.ha cmd/hare/cache.ha cmd/hare/deps.ha cmd/hare/error.ha cmd/hare/main.ha cmd/hare/util.ha cmd/hare/version.ha
+$(HARECACHE)/cmd_hare.ssa: $(cmd_hare_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/cmd_hare_build.td $(HARECACHE)/dirs.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/getopt.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/hare_module.td $(HARECACHE)/hare_parse.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/os_exec.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/unix_tty.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/cmd_hare.td.tmp $(HARE_DEFINES) $(cmd_hare_ha)
diff --git a/makefiles/netbsd.x86_64.mk b/makefiles/netbsd.x86_64.mk
@@ -0,0 +1,256 @@
+# generated by cmd/genbootstrap
+# DO NOT EDIT BY HAND. run 'make bootstrap' to update
+TDENV = env HARE_TD_rt=$(HARECACHE)/rt.td HARE_TD_encoding::utf8=$(HARECACHE)/encoding_utf8.td HARE_TD_sort::cmp=$(HARECACHE)/sort_cmp.td HARE_TD_types=$(HARECACHE)/types.td HARE_TD_bytes=$(HARECACHE)/bytes.td HARE_TD_strings=$(HARECACHE)/strings.td HARE_TD_ascii=$(HARECACHE)/ascii.td HARE_TD_errors=$(HARECACHE)/errors.td HARE_TD_io=$(HARECACHE)/io.td HARE_TD_bufio=$(HARECACHE)/bufio.td HARE_TD_crypto::math=$(HARECACHE)/crypto_math.td HARE_TD_endian=$(HARECACHE)/endian.td HARE_TD_hash=$(HARECACHE)/hash.td HARE_TD_crypto::sha256=$(HARECACHE)/crypto_sha256.td HARE_TD_math=$(HARECACHE)/math.td HARE_TD_memio=$(HARECACHE)/memio.td HARE_TD_path=$(HARECACHE)/path.td HARE_TD_time=$(HARECACHE)/time.td HARE_TD_fs=$(HARECACHE)/fs.td HARE_TD_types::c=$(HARECACHE)/types_c.td HARE_TD_os=$(HARECACHE)/os.td HARE_TD_strconv=$(HARECACHE)/strconv.td HARE_TD_fmt=$(HARECACHE)/fmt.td HARE_TD_encoding::hex=$(HARECACHE)/encoding_hex.td HARE_TD_sort=$(HARECACHE)/sort.td HARE_TD_hare::lex=$(HARECACHE)/hare_lex.td HARE_TD_hare::ast=$(HARECACHE)/hare_ast.td HARE_TD_hare::parse=$(HARECACHE)/hare_parse.td HARE_TD_hare::unparse=$(HARECACHE)/hare_unparse.td HARE_TD_time::chrono=$(HARECACHE)/time_chrono.td HARE_TD_time::date=$(HARECACHE)/time_date.td HARE_TD_hare::module=$(HARECACHE)/hare_module.td HARE_TD_unix=$(HARECACHE)/unix.td HARE_TD_unix::signal=$(HARECACHE)/unix_signal.td HARE_TD_os::exec=$(HARECACHE)/os_exec.td HARE_TD_shlex=$(HARECACHE)/shlex.td HARE_TD_unix::tty=$(HARECACHE)/unix_tty.td HARE_TD_cmd::hare::build=$(HARECACHE)/cmd_hare_build.td HARE_TD_dirs=$(HARECACHE)/dirs.td HARE_TD_getopt=$(HARECACHE)/getopt.td HARE_TD_cmd::hare=$(HARECACHE)/cmd_hare.td
+RTSCRIPT = rt/+netbsd/hare.sc
+OBJS = $(HARECACHE)/rt.o $(HARECACHE)/encoding_utf8.o $(HARECACHE)/sort_cmp.o $(HARECACHE)/types.o $(HARECACHE)/bytes.o $(HARECACHE)/strings.o $(HARECACHE)/ascii.o $(HARECACHE)/errors.o $(HARECACHE)/io.o $(HARECACHE)/bufio.o $(HARECACHE)/crypto_math.o $(HARECACHE)/endian.o $(HARECACHE)/hash.o $(HARECACHE)/crypto_sha256.o $(HARECACHE)/math.o $(HARECACHE)/memio.o $(HARECACHE)/path.o $(HARECACHE)/time.o $(HARECACHE)/fs.o $(HARECACHE)/types_c.o $(HARECACHE)/os.o $(HARECACHE)/strconv.o $(HARECACHE)/fmt.o $(HARECACHE)/encoding_hex.o $(HARECACHE)/sort.o $(HARECACHE)/hare_lex.o $(HARECACHE)/hare_ast.o $(HARECACHE)/hare_parse.o $(HARECACHE)/hare_unparse.o $(HARECACHE)/time_chrono.o $(HARECACHE)/time_date.o $(HARECACHE)/hare_module.o $(HARECACHE)/unix.o $(HARECACHE)/unix_signal.o $(HARECACHE)/os_exec.o $(HARECACHE)/shlex.o $(HARECACHE)/unix_tty.o $(HARECACHE)/cmd_hare_build.o $(HARECACHE)/dirs.o $(HARECACHE)/getopt.o $(HARECACHE)/cmd_hare.o
+
+rt_ha = rt/+netbsd/+x86_64.ha rt/+netbsd/env.ha rt/+netbsd/errno.ha rt/+netbsd/initfini.ha rt/+netbsd/platform_abort.ha rt/+netbsd/platformstart-libc.ha rt/+netbsd/segmalloc.ha rt/+netbsd/signal.ha rt/+netbsd/socket.ha rt/+netbsd/start.ha rt/+netbsd/syscallno.ha rt/+netbsd/syscalls.ha rt/+netbsd/sysctl.ha rt/+netbsd/types.ha rt/+x86_64/arch_jmp.ha rt/+x86_64/cpuid.ha rt/abort.ha rt/ensure.ha rt/fenv_defs.ha rt/jmp.ha rt/malloc.ha rt/memcpy.ha rt/memfunc_ptr.ha rt/memmove.ha rt/memset.ha rt/strcmp.ha rt/u64tos.ha rt/unknown_errno.ha
+$(HARECACHE)/rt.ssa: $(rt_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/rt.td.tmp -N rt $(rt_ha)
+
+rt_s = $(HARECACHE)/rt.s rt/+netbsd/start+x86_64-libc.s rt/+netbsd/syscall+x86_64.s rt/+x86_64/cpuid.s rt/+x86_64/fenv.s rt/+x86_64/longjmp.s rt/+x86_64/setjmp.s
+$(HARECACHE)/rt.o: $(rt_s)
+ @printf 'AS\t%s\n' "$@"
+ @$(AS) $(ASFLAGS) -o $@ $(rt_s)
+
+encoding_utf8_ha = encoding/utf8/decode.ha encoding/utf8/decodetable.ha encoding/utf8/encode.ha encoding/utf8/rune.ha encoding/utf8/types.ha
+$(HARECACHE)/encoding_utf8.ssa: $(encoding_utf8_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/encoding_utf8.td.tmp -N encoding::utf8 $(encoding_utf8_ha)
+
+sort_cmp_ha = sort/cmp/cmp.ha
+$(HARECACHE)/sort_cmp.ssa: $(sort_cmp_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/sort_cmp.td.tmp -N sort::cmp $(sort_cmp_ha)
+
+types_ha = types/arch+x86_64.ha types/classes.ha types/limits.ha
+$(HARECACHE)/types.ssa: $(types_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/types.td.tmp -N types $(types_ha)
+
+bytes_ha = bytes/contains.ha bytes/equal.ha bytes/index.ha bytes/reverse.ha bytes/tokenize.ha bytes/trim.ha bytes/two_way.ha bytes/zero.ha
+$(HARECACHE)/bytes.ssa: $(bytes_ha) $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/bytes.td.tmp -N bytes $(bytes_ha)
+
+strings_ha = strings/compare.ha strings/concat.ha strings/contains.ha strings/dup.ha strings/index.ha strings/iter.ha strings/pad.ha strings/replace.ha strings/runes.ha strings/sub.ha strings/suffix.ha strings/tokenize.ha strings/trim.ha strings/utf8.ha
+$(HARECACHE)/strings.ssa: $(strings_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/strings.td.tmp -N strings $(strings_ha)
+
+ascii_ha = ascii/ctype.ha ascii/string.ha ascii/valid.ha
+$(HARECACHE)/ascii.ssa: $(ascii_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/ascii.td.tmp -N ascii $(ascii_ha)
+
+errors_ha = errors/common.ha errors/opaque.ha errors/rt.ha errors/string.ha
+$(HARECACHE)/errors.ssa: $(errors_ha) $(HARECACHE)/rt.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/errors.td.tmp -N errors $(errors_ha)
+
+io_ha = io/+netbsd/dup.ha io/+netbsd/mmap.ha io/+netbsd/platform_file.ha io/+netbsd/vector.ha io/arch+x86_64.ha io/copy.ha io/drain.ha io/empty.ha io/file.ha io/handle.ha io/limit.ha io/stream.ha io/tee.ha io/types.ha io/util.ha io/zero.ha
+$(HARECACHE)/io.ssa: $(io_ha) $(HARECACHE)/bytes.td $(HARECACHE)/errors.td $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/io.td.tmp -N io $(io_ha)
+
+bufio_ha = bufio/scanner.ha bufio/stream.ha
+$(HARECACHE)/bufio.ssa: $(bufio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/bufio.td.tmp -N bufio $(bufio_ha)
+
+crypto_math_ha = crypto/math/arithm.ha crypto/math/bits.ha
+$(HARECACHE)/crypto_math.ssa: $(crypto_math_ha) $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/crypto_math.td.tmp -N crypto::math $(crypto_math_ha)
+
+endian_ha = endian/big.ha endian/endian.ha endian/host+x86_64.ha endian/little.ha endian/network.ha
+$(HARECACHE)/endian.ssa: $(endian_ha)
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/endian.td.tmp -N endian $(endian_ha)
+
+hash_ha = hash/hash.ha
+$(HARECACHE)/hash.ssa: $(hash_ha) $(HARECACHE)/io.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hash.td.tmp -N hash $(hash_ha)
+
+crypto_sha256_ha = crypto/sha256/sha256.ha
+$(HARECACHE)/crypto_sha256.ssa: $(crypto_sha256_ha) $(HARECACHE)/bytes.td $(HARECACHE)/crypto_math.td $(HARECACHE)/endian.td $(HARECACHE)/hash.td $(HARECACHE)/io.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/crypto_sha256.td.tmp -N crypto::sha256 $(crypto_sha256_ha)
+
+math_ha = math/fenv+x86_64.ha math/fenv_func.ha math/floats.ha math/ints.ha math/math.ha math/trig.ha math/uints.ha
+$(HARECACHE)/math.ssa: $(math_ha) $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/math.td.tmp -N math $(math_ha)
+
+memio_ha = memio/ops.ha memio/stream.ha
+$(HARECACHE)/memio.ssa: $(memio_ha) $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/memio.td.tmp -N memio $(memio_ha)
+
+path_ha = path/+netbsd.ha path/buffer.ha path/error.ha path/ext_stack.ha path/iter.ha path/posix.ha path/prefix.ha path/stack.ha
+$(HARECACHE)/path.ssa: $(path_ha) $(HARECACHE)/bytes.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/path.td.tmp -N path $(path_ha)
+
+time_ha = time/+netbsd/functions.ha time/arithm.ha time/conv.ha time/types.ha
+$(HARECACHE)/time.ssa: $(time_ha) $(HARECACHE)/math.td $(HARECACHE)/rt.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/time.td.tmp -N time $(time_ha)
+
+fs_ha = fs/fs.ha fs/types.ha fs/util.ha
+$(HARECACHE)/fs.ssa: $(fs_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/fs.td.tmp -N fs $(fs_ha)
+
+types_c_ha = types/c/arch+x86_64.ha types/c/strings.ha types/c/types.ha
+$(HARECACHE)/types_c.ssa: $(types_c_ha) $(HARECACHE)/encoding_utf8.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/types_c.td.tmp -N types::c $(types_c_ha)
+
+os_ha = os/+netbsd/dirfdfs.ha os/+netbsd/exit.ha os/+netbsd/fs.ha os/+netbsd/platform_environ.ha os/+netbsd/status.ha os/+netbsd/stdfd.ha os/environ.ha os/os.ha
+$(HARECACHE)/os.ssa: $(os_ha) $(HARECACHE)/bufio.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/errors.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/path.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/types_c.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os.td.tmp -N os $(os_ha)
+
+strconv_ha = strconv/ftos.ha strconv/ftos_multiprecision.ha strconv/ftos_ryu.ha strconv/itos.ha strconv/numeric.ha strconv/stof.ha strconv/stof_data.ha strconv/stoi.ha strconv/stou.ha strconv/types.ha strconv/utos.ha
+$(HARECACHE)/strconv.ssa: $(strconv_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/strconv.td.tmp -N strconv $(strconv_ha)
+
+fmt_ha = fmt/iter.ha fmt/print.ha fmt/wrappers.ha
+$(HARECACHE)/fmt.ssa: $(fmt_ha) $(HARECACHE)/ascii.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/fmt.td.tmp -N fmt $(fmt_ha)
+
+encoding_hex_ha = encoding/hex/hex.ha
+$(HARECACHE)/encoding_hex.ssa: $(encoding_hex_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bytes.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/encoding_hex.td.tmp -N encoding::hex $(encoding_hex_ha)
+
+sort_ha = sort/bisect.ha sort/search.ha sort/sort.ha sort/types.ha
+$(HARECACHE)/sort.ssa: $(sort_ha) $(HARECACHE)/math.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/sort.td.tmp -N sort $(sort_ha)
+
+hare_lex_ha = hare/lex/lex.ha hare/lex/token.ha
+$(HARECACHE)/hare_lex.ssa: $(hare_lex_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_lex.td.tmp -N hare::lex $(hare_lex_ha)
+
+hare_ast_ha = hare/ast/decl.ha hare/ast/expr.ha hare/ast/ident.ha hare/ast/import.ha hare/ast/type.ha hare/ast/unit.ha
+$(HARECACHE)/hare_ast.ssa: $(hare_ast_ha) $(HARECACHE)/hare_lex.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_ast.td.tmp -N hare::ast $(hare_ast_ha)
+
+hare_parse_ha = hare/parse/decl.ha hare/parse/expr.ha hare/parse/ident.ha hare/parse/import.ha hare/parse/parse.ha hare/parse/type.ha hare/parse/unit.ha
+$(HARECACHE)/hare_parse.ssa: $(hare_parse_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/fmt.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td $(HARECACHE)/types.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_parse.td.tmp -N hare::parse $(hare_parse_ha)
+
+hare_unparse_ha = hare/unparse/decl.ha hare/unparse/expr.ha hare/unparse/ident.ha hare/unparse/import.ha hare/unparse/syn.ha hare/unparse/type.ha hare/unparse/unit.ha hare/unparse/util.ha
+$(HARECACHE)/hare_unparse.ssa: $(hare_unparse_ha) $(HARECACHE)/fmt.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_unparse.td.tmp -N hare::unparse $(hare_unparse_ha)
+
+time_chrono_ha = time/chrono/+netbsd.ha time/chrono/arithmetic.ha time/chrono/chronology.ha time/chrono/error.ha time/chrono/leapsec.ha time/chrono/timescale.ha time/chrono/timezone.ha time/chrono/tzdb.ha
+$(HARECACHE)/time_chrono.ssa: $(time_chrono_ha) $(HARECACHE)/bufio.td $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/endian.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/time.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/time_chrono.td.tmp -N time::chrono $(time_chrono_ha)
+
+time_date_ha = time/date/constants.ha time/date/date.ha time/date/daydate.ha time/date/daytime.ha time/date/error.ha time/date/format.ha time/date/locality.ha time/date/observe.ha time/date/parithm.ha time/date/parse.ha time/date/period.ha time/date/reckon.ha time/date/tarithm.ha time/date/virtual.ha
+$(HARECACHE)/time_date.ssa: $(time_date_ha) $(HARECACHE)/ascii.td $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/math.td $(HARECACHE)/memio.td $(HARECACHE)/sort.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/time_chrono.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/time_date.td.tmp -N time::date $(time_date_ha)
+
+hare_module_ha = hare/module/cache.ha hare/module/deps.ha hare/module/format.ha hare/module/srcs.ha hare/module/types.ha hare/module/util.ha
+$(HARECACHE)/hare_module.ssa: $(hare_module_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/bytes.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/hare_parse.td $(HARECACHE)/hare_unparse.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/time_chrono.td $(HARECACHE)/time_date.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/hare_module.td.tmp -N hare::module $(hare_module_ha)
+
+unix_ha = unix/+netbsd/creds.ha unix/+netbsd/nice.ha unix/+netbsd/pipe.ha unix/+netbsd/umask.ha
+$(HARECACHE)/unix.ssa: $(unix_ha) $(HARECACHE)/errors.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/rt.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/unix.td.tmp -N unix $(unix_ha)
+
+unix_signal_ha = unix/signal/+netbsd.ha unix/signal/types.ha
+$(HARECACHE)/unix_signal.ssa: $(unix_signal_ha) $(HARECACHE)/errors.td $(HARECACHE)/io.td $(HARECACHE)/rt.td $(HARECACHE)/time.td $(HARECACHE)/unix.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/unix_signal.td.tmp -N unix::signal $(unix_signal_ha)
+
+os_exec_ha = os/exec/+netbsd/exec.ha os/exec/+netbsd/platform_cmd.ha os/exec/+netbsd/process.ha os/exec/cmd.ha os/exec/types.ha
+$(HARECACHE)/os_exec.ssa: $(os_exec_ha) $(HARECACHE)/ascii.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td $(HARECACHE)/time.td $(HARECACHE)/types_c.td $(HARECACHE)/unix.td $(HARECACHE)/unix_signal.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/os_exec.td.tmp -N os::exec $(os_exec_ha)
+
+shlex_ha = shlex/escape.ha shlex/split.ha
+$(HARECACHE)/shlex.ssa: $(shlex_ha) $(HARECACHE)/ascii.td $(HARECACHE)/encoding_utf8.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/shlex.td.tmp -N shlex $(shlex_ha)
+
+unix_tty_ha = unix/tty/+netbsd/isatty.ha unix/tty/+netbsd/open.ha unix/tty/+netbsd/pgid.ha unix/tty/+netbsd/pty.ha unix/tty/+netbsd/termios.ha unix/tty/+netbsd/winsize.ha unix/tty/pty_test.ha unix/tty/types.ha
+$(HARECACHE)/unix_tty.ssa: $(unix_tty_ha) $(HARECACHE)/bufio.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/os_exec.td $(HARECACHE)/rt.td $(HARECACHE)/strings.td $(HARECACHE)/types_c.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/unix_tty.td.tmp -N unix::tty $(unix_tty_ha)
+
+cmd_hare_build_ha = cmd/hare/build/gather.ha cmd/hare/build/platform.ha cmd/hare/build/queue.ha cmd/hare/build/types.ha cmd/hare/build/util.ha
+$(HARECACHE)/cmd_hare_build.ssa: $(cmd_hare_build_ha) $(HARECACHE)/crypto_sha256.td $(HARECACHE)/encoding_hex.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_module.td $(HARECACHE)/hare_unparse.td $(HARECACHE)/hash.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/os_exec.td $(HARECACHE)/path.td $(HARECACHE)/shlex.td $(HARECACHE)/sort.td $(HARECACHE)/strings.td $(HARECACHE)/unix_tty.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/cmd_hare_build.td.tmp -N cmd::hare::build $(cmd_hare_build_ha)
+
+dirs_ha = dirs/xdg.ha
+$(HARECACHE)/dirs.ssa: $(dirs_ha) $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/os.td $(HARECACHE)/path.td $(HARECACHE)/unix.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/dirs.td.tmp -N dirs $(dirs_ha)
+
+getopt_ha = getopt/getopts.ha
+$(HARECACHE)/getopt.ssa: $(getopt_ha) $(HARECACHE)/fmt.td $(HARECACHE)/io.td $(HARECACHE)/os.td $(HARECACHE)/strings.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/getopt.td.tmp -N getopt $(getopt_ha)
+
+cmd_hare_ha = cmd/hare/arch.ha cmd/hare/build.ha cmd/hare/cache.ha cmd/hare/deps.ha cmd/hare/error.ha cmd/hare/main.ha cmd/hare/util.ha cmd/hare/version.ha
+$(HARECACHE)/cmd_hare.ssa: $(cmd_hare_ha) $(HARECACHE)/ascii.td $(HARECACHE)/bufio.td $(HARECACHE)/cmd_hare_build.td $(HARECACHE)/dirs.td $(HARECACHE)/errors.td $(HARECACHE)/fmt.td $(HARECACHE)/fs.td $(HARECACHE)/getopt.td $(HARECACHE)/hare_ast.td $(HARECACHE)/hare_lex.td $(HARECACHE)/hare_module.td $(HARECACHE)/hare_parse.td $(HARECACHE)/io.td $(HARECACHE)/memio.td $(HARECACHE)/os.td $(HARECACHE)/os_exec.td $(HARECACHE)/path.td $(HARECACHE)/sort.td $(HARECACHE)/sort_cmp.td $(HARECACHE)/strconv.td $(HARECACHE)/strings.td $(HARECACHE)/unix_tty.td
+ @mkdir -p -- "$(HARECACHE)"
+ @printf 'HAREC\t%s\n' "$@"
+ @$(TDENV) $(HAREC) $(HARECFLAGS) -o $@ -t $(HARECACHE)/cmd_hare.td.tmp $(HARE_DEFINES) $(cmd_hare_ha)
diff --git a/mime/+netbsd.ha b/mime/+netbsd.ha
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// Path to the system MIME database.
+export def SYSTEM_DB: str = "/etc/mime.types";
diff --git a/net/+netbsd.ha b/net/+netbsd.ha
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use rt;
+
+// A network socket.
+export type socket = io::file;
+
+// Optional flags to [[accept]] to be set on the returned [[socket]].
+// See the O_CLOEXEC and O_NONBLOCK sections of open(2) for details.
+// Note that CLOEXEC is on by default, and NOCLOEXEC flag disables it.
+export type sockflag = enum int {
+ NOCLOEXEC = rt::SOCK_CLOEXEC,
+ NONBLOCK = rt::SOCK_NONBLOCK
+};
+
+// Accepts the next connection from a socket. Blocks until a new connection is
+// available. Optionally accepts NOCLOEXEC and NONBLOCK flags. If flags are
+// supplied, the [[io::file]] returned will have the supplied flags set.
+export fn accept(sock: socket, flags: sockflag = 0) (socket | error) = {
+ flags ^= rt::SOCK_CLOEXEC: sockflag; // invert CLOEXEC
+ const fd = match (rt::accept4(sock, null, null, flags)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let fd: int =>
+ yield fd;
+ };
+ return io::fdopen(fd);
+};
+
+fn msg_to_native(msg: *msghdr) *rt::msghdr = {
+ let native = &msg.native;
+ if (len(msg.vectors) != 0) {
+ native.msg_iov = msg.vectors: *[*]rt::iovec;
+ native.msg_iovlen = len(msg.vectors): int;
+ };
+ if (len(msg.control) != 0) {
+ native.msg_control = msg.control: *[*]u8;
+ native.msg_controllen = len(msg.control): rt::socklen_t;
+ };
+ return native;
+};
+
+// Sends a message to a socket. See [[newmsg]] for details.
+export fn sendmsg(sock: socket, msg: *msghdr) (size | error) = {
+ // TODO: Flags
+ match (rt::sendmsg(sock, msg_to_native(msg), 0)) {
+ case let n: int =>
+ return n: size;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+// Receives a message from a socket. See [[newmsg]] for details.
+export fn recvmsg(sock: socket, msg: *msghdr) (size | error) = {
+ // TODO: Flags
+ match (rt::recvmsg(sock, msg_to_native(msg), 0)) {
+ case let n: int =>
+ return n: size;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+// Closes a [[socket]]. No further operations against this socket are permitted
+// after calling this function. Closing a socket can fail only under certain
+// conditions (for example, closing a socket twice, or an interrupted syscall).
+// However, the user should not attempt to close the file again on failure - at
+// best the user should print a diagnostic message and move on. See close(2) for
+// details.
+//
+// On NetBSD, this function is an alias for [[io::close]].
+export fn close(sock: socket) (void | error) = match (io::close(sock)) {
+case void => void;
+case io::underread => abort();
+case let err: errors::error =>
+ yield err;
+};
+
+// Shuts down part of a full-duplex connection.
+export fn shutdown(sock: socket, how: shut) (void | error) = {
+ match (rt::shutdown(sock, how)) {
+ case void => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
diff --git a/net/ip/+netbsd.ha b/net/ip/+netbsd.ha
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use endian;
+use rt;
+
+export fn to_native(a: addr, port: u16) rt::sockaddr = {
+ match (a) {
+ case let v4: addr4 =>
+ return rt::sockaddr {
+ in = rt::sockaddr_in {
+ sin_len = size(rt::in_addr): u8,
+ sin_family = rt::AF_INET,
+ sin_port = endian::htonu16(port),
+ sin_addr = rt::in_addr { s_addr = *(&v4[0]: *opaque: *u32) },
+ ...
+ },
+ ...
+ };
+ case let v6: addr6 =>
+ return rt::sockaddr {
+ in6 = rt::sockaddr_in6 {
+ sin6_len = size(rt::in6_addr): u8,
+ sin6_family = rt::AF_INET6,
+ sin6_port = endian::htonu16(port),
+ sin6_addr = rt::in6_addr { s6_addr = v6 },
+ ...
+ },
+ ...
+ };
+ };
+};
+
+export fn from_native(a: rt::sockaddr) (addr, u16) = {
+ let family = a.in.sin_family;
+ switch (family) {
+ case rt::AF_INET =>
+ let addr = a.in.sin_addr.s_addr;
+ return (
+ [addr: u8, (addr >> 8): u8, (addr >> 16): u8,
+ (addr >> 24): u8]: addr4,
+ endian::ntohu16(a.in.sin_port)
+ );
+ case rt::AF_INET6 =>
+ return (
+ a.in6.sin6_addr.s6_addr: addr6,
+ endian::ntohu16(a.in6.sin6_port)
+ );
+ case =>
+ abort("Wrong address family!");
+ };
+};
+
+export fn native_addrlen(a: addr) u32 = {
+ match (a) {
+ case addr4 =>
+ return size(rt::sockaddr_in): u32;
+ case addr6 =>
+ return size(rt::sockaddr_in6): u32;
+ };
+};
diff --git a/net/tcp/+netbsd.ha b/net/tcp/+netbsd.ha
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use net;
+use net::ip;
+use os;
+use rt;
+
+// Opens a TCP connection to the given host and port. Blocks until the
+// connection is established.
+export fn connect(
+ addr: ip::addr,
+ port: u16,
+ options: connect_option...
+) (net::socket | net::error) = {
+ const sockaddr = ip::to_native(addr, port);
+ const family = match (addr) {
+ case ip::addr4 =>
+ yield rt::AF_INET: int;
+ case ip::addr6 =>
+ yield rt::AF_INET6: int;
+ };
+ let f = 0i;
+ for (let i = 0z; i < len(options); i += 1) {
+ match (options[i]) {
+ case let fl: net::sockflag =>
+ f |= fl;
+ case => void;
+ };
+ };
+ f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
+ const sockfd = match (rt::socket(family, rt::SOCK_STREAM | f, 0)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let fd: int =>
+ yield fd;
+ };
+
+ for (let i = 0z; i < len(options); i += 1) {
+ match (options[i]) {
+ case keepalive =>
+ setsockopt(sockfd, rt::SO_KEEPALIVE, true)?;
+ case => void;
+ };
+ };
+ const sz = ip::native_addrlen(addr);
+ match (rt::connect(sockfd, &sockaddr, sz)) {
+ case let err: rt::errno =>
+ if (err != rt::EINPROGRESS) {
+ return errors::errno(err);
+ };
+ assert(f & rt::SOCK_NONBLOCK == rt::SOCK_NONBLOCK);
+ case int => void;
+ };
+ return io::fdopen(sockfd);
+};
+
+// Binds a TCP socket to the given address.
+export fn listen(
+ addr: ip::addr,
+ port: u16,
+ options: listen_option...
+) (net::socket | net::error) = {
+ const sockaddr = ip::to_native(addr, port);
+ const family = match (addr) {
+ case ip::addr4 =>
+ yield rt::AF_INET: int;
+ case ip::addr6 =>
+ yield rt::AF_INET6: int;
+ };
+ let f = 0i;
+ for (let i = 0z; i < len(options); i += 1) {
+ match (options[i]) {
+ case let fl: net::sockflag =>
+ f |= fl;
+ case => void;
+ };
+ };
+ f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
+ const sockfd = match (rt::socket(family, rt::SOCK_STREAM | f, 0)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let fd: int =>
+ yield fd;
+ };
+
+ let bk: u32 = 10;
+ for (let i = 0z; i < len(options); i += 1) {
+ match (options[i]) {
+ case reuseaddr =>
+ setsockopt(sockfd, rt::SO_REUSEADDR, true)?;
+ case reuseport =>
+ setsockopt(sockfd, rt::SO_REUSEPORT, true)?;
+ case keepalive =>
+ setsockopt(sockfd, rt::SO_KEEPALIVE, true)?;
+ case let b: backlog =>
+ bk = b;
+ case => void;
+ };
+ };
+
+ const sz = ip::native_addrlen(addr);
+ match (rt::bind(sockfd, &sockaddr, sz)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case int => void;
+ };
+ match (rt::listen(sockfd, bk)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case int => void;
+ };
+
+ for (let i = 0z; i < len(options); i += 1) {
+ let portout = match (options[i]) {
+ case let p: portassignment =>
+ yield p;
+ case =>
+ continue;
+ };
+ let sn = rt::sockaddr {...};
+ let al = size(rt::sockaddr): u32;
+ match (rt::getsockname(sockfd, &sn, &al)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case int => void;
+ };
+ const addr = ip::from_native(sn);
+ *portout = addr.1;
+ };
+
+ return sockfd;
+};
+
+// Returns the remote address for a given connection, or void if none is
+// available.
+export fn peeraddr(peer: net::socket) ((ip::addr, u16) | void) = {
+ let sn = rt::sockaddr {...};
+ let sz = size(rt::sockaddr): u32;
+ if (rt::getpeername(peer, &sn, &sz) is rt::errno) {
+ return;
+ };
+ return ip::from_native(sn);
+};
+
+fn setsockopt(
+ sockfd: int,
+ option: int,
+ value: bool,
+) (void | net::error) = {
+ let val: int = if (value) 1 else 0;
+ match (rt::setsockopt(sockfd, rt::SOL_SOCKET, option,
+ &val: *opaque, size(int): u32)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case int => void;
+ };
+};
diff --git a/net/udp/+netbsd.ha b/net/udp/+netbsd.ha
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use net;
+use net::ip;
+use os;
+use rt;
+
+// Creates a UDP socket and sets the default destination to the given address.
+export fn connect(
+ dest: ip::addr,
+ port: u16,
+ options: connect_option...
+) (net::socket | net::error) = {
+ const family = match (dest) {
+ case ip::addr4 =>
+ yield rt::AF_INET: int;
+ case ip::addr6 =>
+ yield rt::AF_INET6: int;
+ };
+ let f = 0i;
+ for (let i = 0z; i < len(options); i += 1) {
+ // Only sockflag for now
+ f |= options[i];
+ };
+ f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
+ const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | f, 0)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let fd: int =>
+ yield fd;
+ };
+
+ const sockaddr = ip::to_native(dest, port);
+ const sz = ip::native_addrlen(dest);
+ match (rt::connect(sockfd, &sockaddr, sz)) {
+ case int =>
+ return io::fdopen(sockfd);
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+// Creates a UDP socket bound to an interface.
+export fn listen(
+ addr: ip::addr,
+ port: u16,
+ options: listen_option...
+) (net::socket | net::error) = {
+ const family = match (addr) {
+ case ip::addr4 =>
+ yield rt::AF_INET: int;
+ case ip::addr6 =>
+ yield rt::AF_INET6: int;
+ };
+ let f = 0i;
+ for (let i = 0z; i < len(options); i += 1) {
+ match (options[i]) {
+ case let fl: net::sockflag =>
+ f |= fl;
+ case => void;
+ };
+ };
+ f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
+ const sockfd = match (rt::socket(family, rt::SOCK_DGRAM | f, 0)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let fd: int =>
+ yield fd;
+ };
+
+ for (let i = 0z; i < len(options); i += 1) {
+ match (options[i]) {
+ case reuseaddr =>
+ setsockopt(sockfd, rt::SO_REUSEADDR, true)?;
+ case reuseport =>
+ setsockopt(sockfd, rt::SO_REUSEPORT, true)?;
+ case => void;
+ };
+ };
+
+ const sockaddr = ip::to_native(addr, port);
+ const sz = ip::native_addrlen(addr);
+ match (rt::bind(sockfd, &sockaddr, sz)) {
+ case int => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+
+ for (let i = 0z; i < len(options); i += 1) {
+ let portout = match (options[i]) {
+ case let p: portassignment =>
+ yield p;
+ case =>
+ continue;
+ };
+ let sn = rt::sockaddr {...};
+ let al = size(rt::sockaddr): u32;
+ match (rt::getsockname(sockfd, &sn, &al)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case int => void;
+ };
+ const addr = ip::from_native(sn);
+ *portout = addr.1;
+ };
+
+ return io::fdopen(sockfd);
+};
+
+// Sends a UDP packet to a [[connect]]ed UDP socket.
+export fn send(sock: net::socket, buf: []u8) (size | net::error) = {
+ match (rt::send(sock, buf: *[*]u8, len(buf), 0)) {
+ case let sz: size =>
+ return sz;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+// Sends a UDP packet using this socket.
+export fn sendto(
+ sock: net::socket,
+ buf: []u8,
+ dest: ip::addr,
+ port: u16,
+) (size | net::error) = {
+ const sockaddr = ip::to_native(dest, port);
+ const sz = ip::native_addrlen(dest);
+ match (rt::sendto(sock, buf: *[*]u8, len(buf), 0, &sockaddr, sz)) {
+ case let sz: size =>
+ return sz;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+// Receives a UDP packet from a [[connect]]ed UDP socket.
+export fn recv(
+ sock: net::socket,
+ buf: []u8,
+) (size | net::error) = {
+ match (rt::recv(sock, buf: *[*]u8, len(buf), 0)) {
+ case let sz: size =>
+ return sz;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+// Receives a UDP packet from a bound socket.
+export fn recvfrom(
+ sock: net::socket,
+ buf: []u8,
+ src: nullable *ip::addr,
+ port: nullable *u16,
+) (size | net::error) = {
+ let addrsz = size(rt::sockaddr): u32;
+ const sockaddr = rt::sockaddr { ... };
+ const sz = match (rt::recvfrom(sock, buf: *[*]u8, len(buf), 0,
+ &sockaddr, &addrsz)) {
+ case let sz: size =>
+ yield sz;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+
+ assert(addrsz <= size(rt::sockaddr));
+ const peer = ip::from_native(sockaddr);
+ match (src) {
+ case null => void;
+ case let src: *ip::addr =>
+ *src = peer.0;
+ };
+ match (port) {
+ case null => void;
+ case let port: *u16 =>
+ *port = peer.1;
+ };
+
+ return sz;
+};
+
+fn setsockopt(
+ sockfd: int,
+ option: int,
+ value: bool,
+) (void | net::error) = {
+ let val: int = if (value) 1 else 0;
+ match (rt::setsockopt(sockfd, rt::SOL_SOCKET, option,
+ &val: *opaque, size(int): u32)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case int => void;
+ };
+};
diff --git a/net/unix/+netbsd.ha b/net/unix/+netbsd.ha
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use fmt;
+use io;
+use net;
+use os;
+use rt;
+use strings;
+use types;
+
+// Opens a UNIX socket connection to the path. Blocks until the connection is
+// established.
+export fn connect(
+ addr: addr,
+ options: connect_option...
+) (net::socket | net::error) = {
+ let sockaddr = match (to_native(addr)) {
+ case let a: rt::sockaddr =>
+ yield a;
+ case invalid =>
+ return errors::unsupported; // path too long
+ };
+ let f = 0i;
+ for (let i = 0z; i < len(options); i += 1) {
+ // Only sockflag for now
+ f |= options[i];
+ };
+ f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
+ const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | f, 0)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let fd: int =>
+ yield fd;
+ };
+
+ const sz = size(rt::sockaddr_un): u32;
+ match (rt::connect(sockfd, &sockaddr, sz)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case int => void;
+ };
+ return io::fdopen(sockfd);
+};
+
+// Binds a UNIX socket to the given path.
+export fn listen(
+ addr: addr,
+ options: listen_option...
+) (net::socket | net::error) = {
+ let sockaddr = match (to_native(addr)) {
+ case let a: rt::sockaddr =>
+ yield a;
+ case invalid =>
+ return errors::unsupported; // path too long
+ };
+ let f = 0i;
+ for (let i = 0z; i < len(options); i += 1) {
+ match (options[i]) {
+ case let fl: net::sockflag =>
+ f |= fl;
+ case => void;
+ };
+ };
+ f ^= rt::SOCK_CLOEXEC; // invert CLOEXEC
+ const sockfd = match (rt::socket(rt::AF_UNIX: int, rt::SOCK_STREAM | f, 0)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let fd: int =>
+ yield fd;
+ };
+
+ let bk: u32 = 10;
+ for (let i = 0z; i < len(options); i += 1) {
+ match (options[i]) {
+ case let b: backlog =>
+ bk = b;
+ case => void;
+ };
+ };
+
+ match (rt::bind(sockfd, &sockaddr, size(rt::sockaddr_un): u32)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case int => void;
+ };
+ match (rt::listen(sockfd, bk)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case int => void;
+ };
+
+ return sockfd;
+};
+
+// Converts a UNIX socket address to a native sockaddr.
+fn to_native(addr: addr) (rt::sockaddr | invalid) = {
+ // sun_path should be NUL-terminated and fit into rt::UNIX_PATH_MAX
+ if (len(addr) > rt::UNIX_PATH_MAX - 1) {
+ return invalid;
+ };
+ let ret = rt::sockaddr {
+ un = rt::sockaddr_un {
+ sun_len = size(rt::sockaddr_un): u8,
+ sun_family = rt::AF_UNIX,
+ ...
+ }
+ };
+ match ((&addr: *types::string).data) {
+ case null => void;
+ case let data: *[*]u8 =>
+ ret.un.sun_path[..len(addr)] = data[..len(addr)];
+ };
+ ret.un.sun_path[len(addr)] = 0;
+ return ret;
+};
diff --git a/os/+netbsd/dirfdfs.ha b/os/+netbsd/dirfdfs.ha
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use encoding::utf8;
+use errors;
+use fs;
+use io;
+use path;
+use rt;
+use strings;
+use time;
+use types::c;
+
+type os_filesystem = struct {
+ fs: fs::fs,
+ dirfd: int,
+ getdents_bufsz: size,
+};
+
+// 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.
+export fn dirfdopen(fd: io::file) *fs::fs = {
+ let ofs = alloc(os_filesystem { ... });
+ let fs = static_dirfdopen(fd, ofs);
+ fs.close = &fs_close;
+ return fs;
+};
+
+fn static_dirfdopen(fd: io::file, filesystem: *os_filesystem) *fs::fs = {
+ *filesystem = os_filesystem {
+ fs = fs::fs {
+ open = &fs_open,
+ openfile = &fs_open_file,
+ create = &fs_create,
+ createfile = &fs_create_file,
+ remove = &fs_remove,
+ rename = &fs_rename,
+ iter = &fs_iter,
+ stat = &fs_stat,
+ readlink = &fs_readlink,
+ mkdir = &fs_mkdir,
+ rmdir = &fs_rmdir,
+ chmod = &fs_chmod,
+ chown = &fs_chown,
+ chtimes = &fs_chtimes,
+ fchtimes = &fs_fchtimes,
+ resolve = &fs_resolve,
+ ...
+ },
+ dirfd = fd,
+ getdents_bufsz = 32768, // 32 KiB
+ };
+ return &filesystem.fs;
+};
+
+// Sets the buffer size to use with the getdents(2) system call, for use with
+// [[fs::iter]]. A larger buffer requires a larger runtime allocation, but can
+// scan large directories faster. The default buffer size is 32 KiB.
+//
+// This function is not portable.
+export fn dirfdfs_set_getdents_bufsz(fs: *fs::fs, sz: size) void = {
+ assert(fs.open == &fs_open);
+ let fs = fs: *os_filesystem;
+ fs.getdents_bufsz = sz;
+};
+
+// Returns an [[io::file]] for this filesystem. This function is not portable.
+export fn dirfile(fs: *fs::fs) io::file = {
+ assert(fs.open == &fs_open);
+ let fs = fs: *os_filesystem;
+ return fs.dirfd;
+};
+
+fn errno_to_fs(err: rt::errno) fs::error = {
+ switch (err) {
+ case rt::ENOENT =>
+ return errors::noentry;
+ case rt::EEXIST =>
+ return errors::exists;
+ case rt::EACCES =>
+ return errors::noaccess;
+ case rt::EBUSY =>
+ return errors::busy;
+ case rt::ENOTDIR =>
+ return fs::wrongtype;
+ case rt::EOPNOTSUPP, rt::ENOSYS =>
+ return errors::unsupported;
+ case rt::EXDEV =>
+ return fs::cannotrename;
+ case =>
+ return errors::errno(err);
+ };
+};
+
+fn _fs_open(
+ fs: *fs::fs,
+ path: str,
+ flags: int,
+ mode: uint,
+) (io::file | fs::error) = {
+ let fs = fs: *os_filesystem;
+
+ let fd = match (rt::openat(fs.dirfd, path, flags, mode)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case let fd: int =>
+ yield fd;
+ };
+
+ return io::fdopen(fd);
+};
+
+fn fsflags_to_bsd(flags: fs::flag) (int | errors::unsupported) = {
+ let out = rt::O_NOCTTY | rt::O_CLOEXEC;
+ if (flags & fs::flag::WRONLY == fs::flag::WRONLY) {
+ out |= rt::O_WRONLY;
+ };
+ if (flags & fs::flag::RDWR == fs::flag::RDWR) {
+ out |= rt::O_RDWR;
+ };
+ if (flags & fs::flag::CREATE == fs::flag::CREATE) {
+ out |= rt::O_CREAT;
+ };
+ if (flags & fs::flag::EXCL == fs::flag::EXCL) {
+ out |= rt::O_EXCL;
+ };
+ if (flags & fs::flag::CTTY == fs::flag::CTTY) {
+ out &= ~rt::O_NOCTTY;
+ };
+ if (flags & fs::flag::TRUNC == fs::flag::TRUNC) {
+ out |= rt::O_TRUNC;
+ };
+ if (flags & fs::flag::APPEND == fs::flag::APPEND) {
+ out |= rt::O_APPEND;
+ };
+ if (flags & fs::flag::NONBLOCK == fs::flag::NONBLOCK) {
+ out |= rt::O_NONBLOCK;
+ };
+ if (flags & fs::flag::DSYNC == fs::flag::DSYNC) {
+ out |= rt::O_DSYNC;
+ };
+ if (flags & fs::flag::SYNC == fs::flag::SYNC) {
+ out |= rt::O_SYNC;
+ };
+ if (flags & fs::flag::RSYNC == fs::flag::RSYNC) {
+ out |= rt::O_SYNC;
+ };
+ if (flags & fs::flag::DIRECTORY == fs::flag::DIRECTORY) {
+ out |= rt::O_DIRECTORY;
+ };
+ if (flags & fs::flag::NOFOLLOW == fs::flag::NOFOLLOW) {
+ out |= rt::O_NOFOLLOW;
+ };
+ if (flags & fs::flag::NOCLOEXEC == fs::flag::NOCLOEXEC) {
+ out &= ~rt::O_CLOEXEC;
+ };
+ if (flags & fs::flag::PATH == fs::flag::PATH
+ || flags & fs::flag::NOATIME == fs::flag::NOATIME
+ || flags & fs::flag::TMPFILE == fs::flag::TMPFILE) {
+ return errors::unsupported;
+ };
+ return out;
+};
+
+fn fs_open_file(
+ fs: *fs::fs,
+ path: str,
+ flags: fs::flag,
+) (io::file | fs::error) = {
+ return _fs_open(fs, path, fsflags_to_bsd(flags)?, 0);
+};
+
+fn fs_open(
+ fs: *fs::fs,
+ path: str,
+ flags: fs::flag,
+) (io::handle | fs::error) = fs_open_file(fs, path, flags)?;
+
+fn fs_create_file(
+ fs: *fs::fs,
+ path: str,
+ mode: fs::mode,
+ flags: fs::flag,
+) (io::file | fs::error) = {
+ flags |= fs::flag::CREATE;
+ return _fs_open(fs, path, fsflags_to_bsd(flags)?, mode)?;
+};
+
+fn fs_create(
+ fs: *fs::fs,
+ path: str,
+ mode: fs::mode,
+ flags: fs::flag,
+) (io::handle | fs::error) = {
+ return fs_create_file(fs, path, mode, flags)?;
+};
+
+fn fs_remove(fs: *fs::fs, path: str) (void | fs::error) = {
+ let fs = fs: *os_filesystem;
+ match (rt::unlinkat(fs.dirfd, path, 0)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+fn fs_rename(fs: *fs::fs, oldpath: str, newpath: str) (void | fs::error) = {
+ let fs = fs: *os_filesystem;
+ match (rt::renameat(fs.dirfd, oldpath, fs.dirfd, newpath)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+fn fs_stat(fs: *fs::fs, path: str) (fs::filestat | fs::error) = {
+ let fs = fs: *os_filesystem;
+ let st = rt::st { ... };
+ match (rt::fstatat(fs.dirfd, path, &st, rt::AT_SYMLINK_NOFOLLOW)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+ return fs::filestat {
+ mask = fs::stat_mask::UID
+ | fs::stat_mask::GID
+ | fs::stat_mask::SIZE
+ | fs::stat_mask::INODE
+ | fs::stat_mask::ATIME
+ | fs::stat_mask::MTIME
+ | fs::stat_mask::CTIME,
+ mode = st.mode: fs::mode,
+ uid = st.uid,
+ gid = st.gid,
+ sz = st.sz: size,
+ inode = st.ino,
+ atime = time::instant {
+ sec = st.atime.tv_sec,
+ nsec = st.atime.tv_nsec,
+ },
+ mtime = time::instant {
+ sec = st.mtime.tv_sec,
+ nsec = st.mtime.tv_nsec,
+ },
+ ctime = time::instant {
+ sec = st.ctime.tv_sec,
+ nsec = st.ctime.tv_nsec,
+ },
+ };
+};
+
+fn fs_readlink(fs: *fs::fs, path: str) (str | fs::error) = {
+ let fs = fs: *os_filesystem;
+ static let buf: [rt::PATH_MAX]u8 = [0...];
+ let z = match (rt::readlinkat(fs.dirfd, path, buf[..])) {
+ case let err: rt::errno =>
+ switch (err) {
+ case rt::EINVAL =>
+ return fs::wrongtype;
+ case =>
+ return errno_to_fs(err);
+ };
+ case let z: size =>
+ yield z;
+ };
+ return strings::fromutf8(buf[..z])!;
+};
+
+fn fs_rmdir(fs: *fs::fs, path: str) (void | fs::error) = {
+ let fs = fs: *os_filesystem;
+ match (rt::unlinkat(fs.dirfd, path, rt::AT_REMOVEDIR)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+fn fs_mkdir(fs: *fs::fs, path: str, mode: fs::mode) (void | fs::error) = {
+ let fs = fs: *os_filesystem;
+ match (rt::mkdirat(fs.dirfd, path, mode: uint)) {
+ case let err: rt::errno =>
+ switch (err) {
+ case rt::EISDIR =>
+ return errors::exists;
+ case =>
+ return errno_to_fs(err);
+ };
+ case void => void;
+ };
+};
+
+fn fs_chmod(fs: *fs::fs, path: str, mode: fs::mode) (void | fs::error) = {
+ let fs = fs: *os_filesystem;
+ match (rt::fchmodat(fs.dirfd, path, mode: uint, 0)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+fn fs_fchmod(fd: io::file, mode: fs::mode) (void | fs::error) = {
+ match (rt::fchmod(fd, mode: uint)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+fn fs_chown(fs: *fs::fs, path: str, uid: uint, gid: uint) (void | fs::error) = {
+ let fs = fs: *os_filesystem;
+ match (rt::fchownat(fs.dirfd, path, uid, gid, 0)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+fn fs_fchown(fd: io::file, uid: uint, gid: uint) (void | fs::error) = {
+ match (rt::fchown(fd, uid, gid)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+fn instant_to_timespec(time: (time::instant | void)) rt::timespec = {
+ match (time) {
+ case let t: time::instant =>
+ return time::instant_to_timespec(t);
+ case void =>
+ return rt::timespec{
+ tv_sec = rt::UTIME_OMIT,
+ tv_nsec = rt::UTIME_OMIT
+ };
+ };
+};
+
+fn fs_chtimes(fs: *fs::fs, path: str, atime: (time::instant | void),
+ mtime: (time::instant | void)) (void | fs::error) = {
+ let utimes: [2]rt::timespec = [
+ instant_to_timespec(atime),
+ instant_to_timespec(mtime),
+ ];
+ let fs = fs: *os_filesystem;
+ match (rt::utimensat(fs.dirfd, path, &utimes, 0)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+fn fs_fchtimes(fd: io::file, atime: (time::instant | void),
+ mtime: (time::instant | void)) (void | fs::error) = {
+ let utimes: [2]rt::timespec = [
+ instant_to_timespec(atime),
+ instant_to_timespec(mtime),
+ ];
+ match (rt::futimens(fd, &utimes)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+// TODO: cannot handle errors, i.e. path too long or cannot resolve.
+fn fs_resolve(fs: *fs::fs, path: str) str = {
+ let fs = fs: *os_filesystem;
+ static let buf = path::buffer { ... };
+
+ if (path::abs(path)) {
+ return path;
+ };
+
+ if (fs.dirfd == rt::AT_FDCWD) {
+ path::set(&buf, getcwd(), path)!;
+ } else {
+ // XXX: this is the best we can for now. we should probably
+ // return an error
+ path::set(&buf, "<unknown>", path)!;
+ };
+
+ return path::string(&buf);
+};
+
+fn fs_close(fs: *fs::fs) void = {
+ let fs = fs: *os_filesystem;
+ rt::close(fs.dirfd)!;
+};
+
+// Based on musl's readdir
+type os_iterator = struct {
+ iter: fs::iterator,
+ fd: int,
+ buf_pos: size,
+ buf_end: size,
+ buf: []u8,
+};
+
+fn fs_iter(fs: *fs::fs, path: str) (*fs::iterator | fs::error) = {
+ let fs = fs: *os_filesystem;
+ let flags = rt::O_RDONLY | rt::O_CLOEXEC | rt::O_DIRECTORY;
+ let fd: int = match (rt::openat(fs.dirfd, path, flags, 0)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case let fd: int =>
+ yield fd;
+ };
+
+ let buf = match (rt::malloc(fs.getdents_bufsz)) {
+ case let v: *opaque =>
+ yield v: *[*]u8;
+ case null =>
+ return errors::nomem;
+ };
+ let iter = alloc(os_iterator {
+ iter = fs::iterator {
+ next = &iter_next,
+ finish = &iter_finish,
+ },
+ fd = fd,
+ buf = buf[..fs.getdents_bufsz],
+ ...
+ });
+ return &iter.iter;
+};
+
+fn iter_next(iter: *fs::iterator) (fs::dirent | done | fs::error) = {
+ let iter = iter: *os_iterator;
+ if (iter.buf_pos >= iter.buf_end) {
+ let n = match (rt::getdents(iter.fd,
+ iter.buf: *[*]u8, len(iter.buf))) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case let n: size =>
+ yield n;
+ };
+ if (n == 0) {
+ return done;
+ };
+ iter.buf_end = n;
+ iter.buf_pos = 0;
+ };
+ let de = &iter.buf[iter.buf_pos]: *rt::dirent;
+ iter.buf_pos += de.d_reclen;
+ let name = c::tostr(&de.d_name: *const c::char)?;
+ if (name == "." || name == "..") {
+ return iter_next(iter);
+ };
+
+ let ftype: fs::mode = switch (de.d_type) {
+ case rt::DT_UNKNOWN =>
+ yield fs::mode::UNKNOWN;
+ case rt::DT_FIFO =>
+ yield fs::mode::FIFO;
+ case rt::DT_CHR =>
+ yield fs::mode::CHR;
+ case rt::DT_DIR =>
+ yield fs::mode::DIR;
+ case rt::DT_BLK =>
+ yield fs::mode::BLK;
+ case rt::DT_REG =>
+ yield fs::mode::REG;
+ case rt::DT_LNK =>
+ yield fs::mode::LINK;
+ case rt::DT_SOCK =>
+ yield fs::mode::SOCK;
+ case =>
+ yield fs::mode::UNKNOWN;
+ };
+ return fs::dirent {
+ name = name,
+ ftype = ftype,
+ };
+};
+
+fn iter_finish(iter: *fs::iterator) void = {
+ let iter = iter: *os_iterator;
+ rt::close(iter.fd)!;
+ free(iter.buf);
+ free(iter);
+};
diff --git a/os/+netbsd/exit+test.ha b/os/+netbsd/exit+test.ha
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// Exit the program with the provided status code.
+export fn exit(status: int) never = {
+ abort("os::exit disabled in +test");
+};
diff --git a/os/+netbsd/exit.ha b/os/+netbsd/exit.ha
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use rt;
+
+// Exit the program with the provided status code.
+export fn exit(status: int) never = {
+ rt::fini();
+ rt::exit(status);
+};
diff --git a/os/+netbsd/fs.ha b/os/+netbsd/fs.ha
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use fs;
+use path;
+use rt;
+use types::c;
+
+@init fn init_cwd() void = {
+ static let cwd_fs = os_filesystem { ... };
+ cwd = static_dirfdopen(rt::AT_FDCWD, &cwd_fs);
+};
+
+// 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 = c::tostr(rt::getcwd() as *const u8: *const c::char)!;
+
+// Change the current working directory.
+export fn chdir(target: (*fs::fs | str)) (void | fs::error) = {
+ const path: str = match (target) {
+ case let fs: *fs::fs =>
+ assert(fs.open == &fs_open);
+ let fs = fs: *os_filesystem;
+ match (rt::fchdir(fs.dirfd)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void =>
+ return;
+ };
+ case let s: str =>
+ yield s;
+ };
+ match (rt::chdir(path)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+// Changes the root directory of the process. Generally requires the caller to
+// have root or otherwise elevated permissions.
+//
+// This function is not appropriate for sandboxing.
+export fn chroot(target: str) (void | fs::error) = {
+ match (rt::chroot(target)) {
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ case void => void;
+ };
+};
+
+// Access modes for [[access]].
+export type amode = enum int {
+ F_OK = rt::F_OK,
+ R_OK = rt::R_OK,
+ W_OK = rt::W_OK,
+ X_OK = rt::X_OK,
+};
+
+// Returns true if the given mode of access is permissible. The use of this
+// function is discouraged as it can allow for a race condition to occur betwen
+// testing for the desired access mode and actually using the file should the
+// permissions of the file change between these operations. It is recommended
+// instead to attempt to use the file directly and to handle any errors that
+// should occur at that time.
+export fn access(path: str, mode: amode) (bool | fs::error) = {
+ match (rt::access(path, mode)) {
+ case let b: bool =>
+ return b;
+ case let err: rt::errno =>
+ return errno_to_fs(err);
+ };
+};
+
+// TODO: NetBSD
+// export fn mkfifo(path: str, mode: fs::mode) (void | fs::error) = {
+// export fn mkblk(
+// export fn mkchr(
+// export fn mkfile(
diff --git a/os/+netbsd/platform_environ.ha b/os/+netbsd/platform_environ.ha
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use rt;
+use strings;
+use types::c;
+
+// The command line arguments provided to the program. By convention, the first
+// member is usually the name of the program.
+export let args: []str = [];
+
+// Statically allocate arg strings if there are few enough arguments, saves a
+// syscall if we don't need it.
+let args_static: [32]str = [""...];
+
+@init fn args() void = {
+ if (rt::argc < len(args_static)) {
+ args = args_static[..rt::argc];
+ for (let i = 0z; i < rt::argc; i += 1) {
+ args[i] = c::tostr(rt::argv[i]: *const c::char)!;
+ };
+ } else {
+ args = alloc([], rt::argc);
+ for (let i = 0z; i < rt::argc; i += 1) {
+ append(args, c::tostr(rt::argv[i]: *const c::char)!);
+ };
+ };
+
+};
+
+@fini fn args() void = {
+ if (rt::argc >= len(args_static)) {
+ free(args);
+ };
+};
+
+// Returns a slice of the environment strings in the form KEY=VALUE.
+export fn getenvs() []str = {
+ if (len(envp) != 0) {
+ return envp;
+ };
+ for (let i = 0z; rt::envp[i] != null; i += 1) {
+ let s = c::tostr(rt::envp[i]: *const c::char)!;
+ append(envp, strings::dup(s));
+ };
+ return envp;
+};
+
+// Returns the host kernel name
+export fn sysname() const str = {
+ static let buf: [512]u8 = [0...];
+ let sz: size = len(buf);
+ rt::sysctl([rt::CTL_KERN, rt::KERN_OSTYPE], &buf, &sz, null, 0)!;
+ return strings::fromutf8(buf[..(sz - 1)])!;
+};
+
+// Returns the host system hostname
+export fn hostname() const str = {
+ static let buf: [512]u8 = [0...];
+ let sz: size = len(buf);
+ rt::sysctl([rt::CTL_KERN, rt::KERN_HOSTNAME], &buf, &sz, null, 0)!;
+ return strings::fromutf8(buf[..(sz - 1)])!;
+};
+
+// Returns the host kernel version
+export fn release() const str = {
+ static let buf: [512]u8 = [0...];
+ let sz: size = len(buf);
+ rt::sysctl([rt::CTL_KERN, rt::KERN_OSRELEASE], &buf, &sz, null, 0)!;
+ return strings::fromutf8(buf[..(sz - 1)])!;
+};
+
+// Returns the host operating system version
+export fn version() const str = {
+ static let buf: [512]u8 = [0...];
+ let sz: size = len(buf);
+ rt::sysctl([rt::CTL_KERN, rt::KERN_VERSION], &buf, &sz, null, 0)!;
+ return strings::fromutf8(buf[..(sz - 1)])!;
+};
+
+// Returns the host CPU architecture, in a platform-specific format. See
+// [[architecture]] for a more portable wrapper.
+export fn machine() const str = {
+ static let buf: [32]u8 = [0...];
+ let sz: size = len(buf);
+ rt::sysctl([rt::CTL_HW, rt::HW_MACHINE], &buf, &sz, null, 0)!;
+ return strings::fromutf8(buf[..sz - 1])!;
+};
+
+// Returns the host CPU architecture.
+export fn architecture() arch = {
+ switch (machine()) {
+ case "aarch64" =>
+ return arch::AARCH64;
+ case "riscv64" =>
+ return arch::RISCV64;
+ case "amd64" =>
+ return arch::X86_64;
+ case => abort(); // unreachable
+ };
+};
+
+// Returns the number of usable CPUs.
+export fn cpucount() (size | errors::error) = {
+ let count = 0;
+ let length = size(int);
+ match (rt::sysctl([rt::CTL_HW, rt::HW_NCPU], &count, &length, null, 0)) {
+ case void => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+ return count: size;
+};
diff --git a/os/+netbsd/status.ha b/os/+netbsd/status.ha
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// Values that may be passed to [[exit]] to indicate successful or unsuccessful
+// termination, respectively.
+export type status = enum {
+ SUCCESS = 0,
+ FAILURE = 1,
+};
diff --git a/os/+netbsd/stdfd.ha b/os/+netbsd/stdfd.ha
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use bufio;
+use io;
+use rt;
+
+let stdin_bufio: bufio::stream = bufio::stream {
+ // Will be overwritten, but must be initialized
+ stream = null: io::stream,
+ source = 0,
+ ...
+};
+
+let stdout_bufio: bufio::stream = bufio::stream {
+ // Will be overwritten, but must be initialized
+ stream = null: io::stream,
+ source = 1,
+ ...
+};
+
+// The standard input. This handle is buffered.
+export let stdin: io::handle = rt::STDIN_FILENO; // initialized by init_stdfd
+
+// The standard input, as an [[io::file]]. This handle is unbuffered.
+export let stdin_file: io::file = rt::STDIN_FILENO;
+
+// The standard output. This handle is buffered.
+export let stdout: io::handle = rt::STDOUT_FILENO; // initialized by init_stdfd
+
+// The standard output, as an [[io::file]]. This handle is unbuffered.
+export let stdout_file: io::file = rt::STDOUT_FILENO;
+
+// The standard error. This handle is unbuffered.
+export let stderr: io::handle = rt::STDERR_FILENO;
+
+// The standard error, as an [[io::file]]. This handle is unbuffered.
+export let stderr_file: io::file = rt::STDERR_FILENO;
+
+// The recommended buffer size for reading from disk.
+export def BUFSZ: size = 4096; // 4 KiB
+
+@init fn init_stdfd() void = {
+ static let stdinbuf: [BUFSZ]u8 = [0...];
+ stdin_bufio = bufio::init(stdin_file, stdinbuf, []);
+ stdin = &stdin_bufio;
+
+ static let stdoutbuf: [BUFSZ]u8 = [0...];
+ stdout_bufio = bufio::init(stdout_file, [], stdoutbuf);
+ stdout = &stdout_bufio;
+};
+
+@fini fn fini_stdfd() void = {
+ // Flush any pending writes
+ io::close(stdout): void;
+};
diff --git a/os/exec/+netbsd/exec.ha b/os/exec/+netbsd/exec.ha
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use os;
+use rt;
+use types::c;
+use unix;
+
+// Forks the current process, returning the [[process]] of the child (to the
+// parent) and void (to the child), or an error.
+export fn fork() (process | void | error) = {
+ match (rt::fork()) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let i: rt::pid_t =>
+ return i: process;
+ case void =>
+ return void;
+ };
+};
+
+// Creates an anonymous pipe for use with [[addfile]]. Any data written to the
+// second file may be read from the first file. The caller should close one or
+// both of the file descriptors after they have transferred them to another
+// process, and after they have finished using them themselves, if applicable.
+//
+// This function will abort the process if the system is unable to allocate the
+// resources for a pipe. If you need to handle this error gracefully, you may
+// call [[unix::pipe]] yourself, but this may reduce the portability of your
+// software.
+//
+// To capture the standard output of a process:
+//
+// let pipe = exec::pipe();
+// exec::addfile(&cmd, pipe.1, os::stdout_file);
+// let proc = exec::start(&cmd)!;
+// io::close(pipe.1)!;
+//
+// let data = io::drain(pipe.0)!;
+// io::close(pipe.0)!;
+// exec::wait(&proc)!;
+//
+// To write to the standard input of a process:
+//
+// let pipe = exec::pipe();
+// exec::addfile(&cmd, os::stdin_file, pipe.0);
+// let proc = exec::start(&cmd)!;
+//
+// io::writeall(data)!;
+// io::close(pipe.1)!;
+// io::close(pipe.0)!;
+// exec::wait(&proc)!;
+export fn pipe() (io::file, io::file) = {
+ return unix::pipe()!;
+};
+
+fn open(path: str) (platform_cmd | error) = {
+ let fd = match (rt::open(path, rt::O_RDONLY, 0u)) {
+ case let fd: int =>
+ yield fd;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+ let success = false;
+ defer if (!success) rt::close(fd)!;
+ match (rt::access(path, rt::X_OK)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let b: bool =>
+ if (!b) {
+ return errors::noaccess;
+ };
+ };
+ // Make sure we are not trying to execute anything weird. fstat()
+ // already dereferences symlinks, so if this is anything other than a
+ // regular file it cannot be executed.
+ let s = rt::st { ... };
+ match (rt::fstat(fd, &s)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case void =>
+ if (s.mode & rt::S_IFREG == 0) {
+ return errors::noaccess;
+ };
+ };
+ success = true;
+ return fd;
+
+};
+
+fn platform_finish(cmd: *command) void = rt::close(cmd.platform)!;
+
+fn platform_exec(cmd: *command) error = {
+ // We don't worry about freeing the return values from c::fromstr
+ // because once we exec(2) our heap is fried anyway
+ let argv: []nullable *const c::char = alloc([], len(cmd.argv) + 1z);
+ for (let arg .. cmd.argv) {
+ append(argv, c::fromstr(arg));
+ };
+ append(argv, null);
+
+ let envp: nullable *[*]nullable *const c::char = null;
+ if (len(cmd.env) != 0) {
+ let env: []nullable *const c::char = alloc([], len(cmd.env) + 1);
+ for (let e .. cmd.env) {
+ append(env, c::fromstr(e));
+ };
+ append(env, null);
+ envp = env: *[*]nullable *const c::char;
+ };
+
+ let need_devnull = false;
+ for (let file &.. cmd.files) {
+ const from = match (file.0) {
+ case let file: io::file =>
+ yield file;
+ case nullfd =>
+ need_devnull = true;
+ continue;
+ case closefd =>
+ continue;
+ };
+
+ file.0 = match (rt::fcntl(from, rt::F_DUPFD_CLOEXEC, 0)) {
+ case let fd: int =>
+ yield fd;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+ };
+
+ const devnull: io::file = if (need_devnull) {
+ yield os::open("/dev/null")!;
+ } else -1;
+
+ for (let file .. cmd.files) {
+ const from = match (file.0) {
+ case let file: io::file =>
+ yield file;
+ case nullfd =>
+ yield devnull;
+ case closefd =>
+ io::close(file.1)?;
+ continue;
+ };
+
+ if (file.1 == from) {
+ let flags = match (rt::fcntl(from, rt::F_GETFD, 0)) {
+ case let flags: int =>
+ yield flags;
+ case let e: rt::errno =>
+ return errors::errno(e);
+ };
+ rt::fcntl(from, rt::F_SETFD, flags & ~rt::FD_CLOEXEC)!;
+ } else {
+ match (rt::dup2(from, file.1)) {
+ case int => void;
+ case let e: rt::errno =>
+ return errors::errno(e);
+ };
+ };
+ };
+
+ if (cmd.dir != "") {
+ os::chdir(cmd.dir)?;
+ };
+
+ return errors::errno(rt::fexecve(cmd.platform,
+ argv: *[*]nullable *const u8,
+ envp: *[*]nullable *const u8));
+};
+
+fn platform_start(cmd: *command) (process | errors::error) = {
+ // TODO: Let the user configure clone more to their taste (e.g. SIGCHLD)
+ let pipe: [2]int = [0...];
+ match (rt::pipe2(&pipe, rt::O_CLOEXEC)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case void => void;
+ };
+
+ match (rt::fork()) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let pid: rt::pid_t =>
+ rt::close(pipe[1])!;
+ defer rt::close(pipe[0])!;
+ let errno: int = 0;
+ match (rt::read(pipe[0], &errno, size(int))) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let n: size =>
+ switch (n) {
+ case size(int) =>
+ return errors::errno(errno);
+ case 0 =>
+ return pid;
+ case =>
+ abort("Unexpected rt::read result");
+ };
+ };
+ case void =>
+ rt::close(pipe[0])!;
+ let err = platform_exec(cmd);
+ if (!(err is errors::opaque_)) {
+ rt::exit(1);
+ };
+ let err = err as errors::opaque_;
+ let err = &err.data: *rt::errno;
+ rt::write(pipe[1], err, size(int))!;
+ rt::exit(1);
+ };
+};
diff --git a/os/exec/+netbsd/platform_cmd.ha b/os/exec/+netbsd/platform_cmd.ha
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use io;
+use os;
+use strings;
+
+export type platform_cmd = io::file;
+
+// Same as [[cmd]] except that executable file is determined by [[io::file]].
+// This function is not portable.
+export fn cmdfile(file: io::file, name: str, args: str...) command = {
+ let cmd = command {
+ platform = file,
+ argv = alloc([], len(args) + 1),
+ env = strings::dupall(os::getenvs()),
+ files = [],
+ dir = "",
+ };
+ append(cmd.argv, name);
+ append(cmd.argv, args...);
+ return cmd;
+};
diff --git a/os/exec/+netbsd/process.ha b/os/exec/+netbsd/process.ha
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use fmt;
+use rt;
+use time;
+use unix;
+use unix::signal;
+
+// Stores information about a child process.
+export type process = unix::pid;
+
+// Returns the currently running [[process]].
+export fn self() process = {
+ return unix::getpid();
+};
+
+// Stores information about an exited process.
+export type status = struct {
+ status: int,
+ // Not all of these members are supported on all operating systems.
+ // Only utime and stime are guaranteed to be available.
+ rusage: struct {
+ utime: time::instant,
+ stime: time::instant,
+ maxrss: i64,
+ ixrss: i64,
+ idrss: i64,
+ isrss: i64,
+ minflt: i64,
+ majflt: i64,
+ nswap: i64,
+ inblock: i64,
+ oublock: i64,
+ msgsnd: i64,
+ msgrcv: i64,
+ nsignals: i64,
+ nvcsw: i64,
+ nivcsw: i64,
+ },
+};
+
+fn rusage(st: *status, ru: *rt::rusage) void = {
+ st.rusage.utime = time::instant {
+ sec = ru.ru_utime.tv_sec,
+ nsec = ru.ru_utime.tv_usec * time::MICROSECOND: i64,
+ };
+ st.rusage.stime = time::instant {
+ sec = ru.ru_stime.tv_sec,
+ nsec = ru.ru_stime.tv_usec * time::MICROSECOND: i64,
+ };
+ st.rusage.maxrss = ru.ru_maxrss;
+ st.rusage.ixrss = ru.ru_ixrss;
+ st.rusage.idrss = ru.ru_idrss;
+ st.rusage.isrss = ru.ru_isrss;
+ st.rusage.minflt = ru.ru_minflt;
+ st.rusage.majflt = ru.ru_majflt;
+ st.rusage.nswap = ru.ru_nswap;
+ st.rusage.inblock = ru.ru_inblock;
+ st.rusage.oublock = ru.ru_oublock;
+ st.rusage.msgsnd = ru.ru_msgsnd;
+ st.rusage.msgrcv = ru.ru_msgrcv;
+ st.rusage.nsignals = ru.ru_nsignals;
+ st.rusage.nvcsw = ru.ru_nvcsw;
+ st.rusage.nivcsw = ru.ru_nivcsw;
+};
+
+// Waits for a process to complete, then returns its status information.
+export fn wait(proc: *process) (status | error) = {
+ let ru: rt::rusage = rt::rusage { ... };
+ let st: status = status { ... };
+ match (rt::wait4(*proc, &st.status, 0, &ru)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let pid: int =>
+ assert(pid == *proc: int);
+ };
+ rusage(&st, &ru);
+ return st;
+};
+
+// Waits for the first child process to complete, then returns its process info
+// and status
+export fn waitany() ((process, status) | error) = {
+ let ru: rt::rusage = rt::rusage { ... };
+ let st: status = status { ... };
+ match (rt::wait4(-1, &st.status, 0, &ru)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let pid: int =>
+ rusage(&st, &ru);
+ return (pid: process, st);
+ };
+};
+
+// Waits for all children to terminate succesfully. If a child process exits
+// with a nonzero status, returns its process info and exit status immediately,
+// not waiting for the remaining children.
+export fn waitall() (uint | error | !(process, exit_status)) = {
+ let st: status = status { ... };
+ let ru: rt::rusage = rt::rusage { ... };
+ for (let i = 0u; true; i += 1) match (rt::wait4(-1, &st.status, 0, &ru)) {
+ case let err: rt::errno =>
+ if (err == rt::ECHILD) {
+ return i;
+ } else {
+ return errors::errno(err);
+ };
+ case let pid: int =>
+ match (check(&st)) {
+ case void => void;
+ case let es: !exit_status =>
+ return (pid: process, es);
+ };
+ };
+};
+
+// Checks for process completion, returning its status information on
+// completion, or void if it is still running.
+export fn peek(proc: *process) (status | void | error) = {
+ let ru: rt::rusage = rt::rusage { ... };
+ let st: status = status { ... };
+ match (rt::wait4(*proc, &st.status, rt::WNOHANG, &ru)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let pid: int =>
+ switch (pid) {
+ case 0 =>
+ return;
+ case =>
+ assert(pid == *proc: int);
+ };
+ };
+ rusage(&st, &ru);
+ return st;
+};
+
+// Checks if any child process has completed, returning its process info and
+// status if so.
+export fn peekany() ((process, status) | void | error) = {
+ let ru: rt::rusage = rt::rusage { ... };
+ let st: status = status { ... };
+ match (rt::wait4(-1, &st.status, rt::WNOHANG, &ru)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let pid: int =>
+ switch (pid) {
+ case 0 =>
+ return;
+ case =>
+ return (pid: process, st);
+ };
+ };
+};
+
+// The exit status code of a process.
+export type exited = int;
+
+// The signal number which caused a process to terminate.
+export type signaled = signal::sig;
+
+// The exit status of a process.
+export type exit_status = (exited | signaled);
+
+// Returns a human friendly string describing the exit status. The string is
+// statically allocated; use [[strings::dup]] to extend its lifetime.
+export fn exitstr(status: exit_status) const str = {
+ static let buf: [1024]u8 = [0...];
+ match (status) {
+ case let i: exited =>
+ switch (i) {
+ case 0 =>
+ return "exited normally";
+ case =>
+ return fmt::bsprintf(buf, "exited with status {}",
+ i: int);
+ };
+ case let s: signaled =>
+ return fmt::bsprintf(buf, "exited with signal {}",
+ signal::signame(s));
+ };
+};
+
+// Returns the exit status of a completed process.
+export fn exit(stat: *status) exit_status = {
+ if (rt::wifexited(stat.status)) {
+ return rt::wexitstatus(stat.status): exited;
+ };
+ if (rt::wifsignaled(stat.status)) {
+ return rt::wtermsig(stat.status): signaled;
+ };
+ abort("Unexpected exit status");
+};
+
+// Checks the exit status of a completed process, returning void if successful,
+// or its status code as an error type if not.
+export fn check(stat: *status) (void | !exit_status) = {
+ if (rt::wifexited(stat.status)) {
+ switch (rt::wexitstatus(stat.status)) {
+ case 0 =>
+ return void;
+ case =>
+ return exit(stat);
+ };
+ };
+ return exit(stat);
+};
+
+// Terminates a process. On NetBSD, this sends [[unix::signal::sig::TERM]] to
+// the process.
+export fn kill(proc: process) (void | errors::error) = {
+ return sig(proc, signal::sig::TERM);
+};
+
+// Sends a signal to a child process. This function is only supported on
+// Unix-like systems.
+export fn sig(proc: process, sig: signal::sig) (void | errors::error) = {
+ match (rt::kill(proc, sig)) {
+ case let errno: rt::errno =>
+ return errors::errno(errno);
+ case void =>
+ return;
+ };
+};
diff --git a/path/+netbsd.ha b/path/+netbsd.ha
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use rt;
+
+// Platform-specific path separator byte.
+export def SEP: u8 = '/';
+
+const sepstr: str = "/";
+
+// Maximum length of a file path for this platform.
+export def MAX = rt::PATH_MAX - 1;
diff --git a/rt/+netbsd/+x86_64.ha b/rt/+netbsd/+x86_64.ha
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+export type siginfo = struct {
+ si_signo: int,
+ si_errno: int,
+ si_code: int,
+ si_pid: pid_t,
+ si_uid: uid_t,
+ si_status: int,
+ si_addr: *opaque,
+ si_value: sigval,
+ _reason: union {
+ _fault: struct {
+ _trapno: int,
+ },
+ _timer: struct {
+ _timerid: int,
+ _overrun: int,
+ },
+ _mesgq: struct {
+ _mqd: int,
+ },
+ _poll: struct {
+ _band: i64,
+ },
+ __spare__: struct {
+ __spare1__: i64,
+ __spare2__: [7]int,
+ },
+ },
+};
+
+export type ucontext = struct {
+ uc_sigmask: sigset,
+ uc_mcontext: mcontext,
+ uc_link: *ucontext,
+ uc_stack: stack_t,
+ uc_flags: int,
+ __spare__: [4]int,
+};
+
+export type mcontext = struct {
+ mc_onstack: u64,
+ mc_rdi: u64,
+ mc_rsi: u64,
+ mc_rdx: u64,
+ mc_rcx: u64,
+ mc_r8: u64,
+ mc_r9: u64,
+ mc_rax: u64,
+ mc_rbx: u64,
+ mc_rbp: u64,
+ mc_r10: u64,
+ mc_r11: u64,
+ mc_r12: u64,
+ mc_r13: u64,
+ mc_r14: u64,
+ mc_r15: u64,
+ mc_trapno: u32,
+ mc_fs: u16,
+ mc_gs: u16,
+ mc_addr: u64,
+ mc_flags: u32,
+ mc_es: u16,
+ mc_ds: u16,
+ mc_err: u64,
+ mc_rip: u64,
+ mc_cs: u64,
+ mc_rflags: u64,
+ mc_rsp: u64,
+ mc_ss: u64,
+};
diff --git a/rt/+netbsd/env.ha b/rt/+netbsd/env.ha
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+export let argc: size = 0;
+export let argv: *[*]*u8 = null: *[*]*u8;
+export let envp: *[*]nullable *u8 = null: *[*]nullable *u8;
diff --git a/rt/+netbsd/errno.ha b/rt/+netbsd/errno.ha
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// Represents an error returned from the NetBSD kernel.
+export type errno = !int;
+
+// Checks the return value from a NetBSD syscall and, if found to be in error,
+// returns the appropriate error. Otherwise, returns the original value.
+fn wrap_return(r: u64) (errno | u64) = {
+ if (r > -4096: u64) {
+ return (-(r: i64)): errno;
+ };
+ return r;
+};
+
+// Obtains a human-friendly reading of an [[errno]] (e.g. "Operation not
+// permitted"). The return value may be statically allocated.
+export fn strerror(err: errno) str = {
+ switch (err) {
+ case EPERM =>
+ return "Operation not permitted";
+ case ENOENT =>
+ return "No such file or directory";
+ case ESRCH =>
+ return "No such process";
+ case EINTR =>
+ return "Interrupted system call";
+ case EIO =>
+ return "Input/output error";
+ case ENXIO =>
+ return "No such device or address";
+ case E2BIG =>
+ return "Argument list too long";
+ case ENOEXEC =>
+ return "Exec format error";
+ case EBADF =>
+ return "Bad file descriptor";
+ case ECHILD =>
+ return "No child processes";
+ case EAGAIN =>
+ return "Resource temporarily unavailable";
+ case ENOMEM =>
+ return "Cannot allocate memory";
+ case EACCES =>
+ return "Permission denied";
+ case EFAULT =>
+ return "Bad address";
+ case ENOTBLK =>
+ return "Block device required";
+ case EBUSY =>
+ return "Device or resource busy";
+ case EEXIST =>
+ return "File exists";
+ case EXDEV =>
+ return "Invalid cross-device link";
+ case ENODEV =>
+ return "No such device";
+ case ENOTDIR =>
+ return "Not a directory";
+ case EISDIR =>
+ return "Is a directory";
+ case EINVAL =>
+ return "Invalid argument";
+ case ENFILE =>
+ return "Too many open files in system";
+ case EMFILE =>
+ return "Too many open files";
+ case ENOTTY =>
+ return "Inappropriate ioctl for device";
+ case ETXTBSY =>
+ return "Text file busy";
+ case EFBIG =>
+ return "File too large";
+ case ENOSPC =>
+ return "No space left on device";
+ case ESPIPE =>
+ return "Illegal seek";
+ case EROFS =>
+ return "Read-only file system";
+ case EMLINK =>
+ return "Too many links";
+ case EPIPE =>
+ return "Broken pipe";
+ case EDOM =>
+ return "Numerical argument out of domain";
+ case ERANGE =>
+ return "Numerical result out of range";
+ case EDEADLK =>
+ return "Resource deadlock avoided";
+ case ENAMETOOLONG =>
+ return "File name too long";
+ case ENOLCK =>
+ return "No locks available";
+ case ENOSYS =>
+ return "Function not implemented";
+ case ENOTEMPTY =>
+ return "Directory not empty";
+ case ELOOP =>
+ return "Too many levels of symbolic links";
+ case ENOMSG =>
+ return "No message of desired type";
+ case EIDRM =>
+ return "Identifier removed";
+ case EREMOTE =>
+ return "Object is remote";
+ case ENOLINK =>
+ return "Link has been severed";
+ case EPROTO =>
+ return "Protocol error";
+ case EMULTIHOP =>
+ return "Multihop attempted";
+ case EBADMSG =>
+ return "Bad message";
+ case EOVERFLOW =>
+ return "Value too large for defined data type";
+ case EILSEQ =>
+ return "Invalid or incomplete multibyte or wide character";
+ case EUSERS =>
+ return "Too many users";
+ case ENOTSOCK =>
+ return "Socket operation on non-socket";
+ case EDESTADDRREQ =>
+ return "Destination address required";
+ case EMSGSIZE =>
+ return "Message too long";
+ case EPROTOTYPE =>
+ return "Protocol wrong type for socket";
+ case ENOPROTOOPT =>
+ return "Protocol not available";
+ case EPROTONOSUPPORT =>
+ return "Protocol not supported";
+ case ESOCKTNOSUPPORT =>
+ return "Socket type not supported";
+ case EOPNOTSUPP =>
+ return "Operation not supported";
+ case EPFNOSUPPORT =>
+ return "Protocol family not supported";
+ case EAFNOSUPPORT =>
+ return "Address family not supported by protocol";
+ case EADDRINUSE =>
+ return "Address already in use";
+ case EADDRNOTAVAIL =>
+ return "Cannot assign requested address";
+ case ENETDOWN =>
+ return "Network is down";
+ case ENETUNREACH =>
+ return "Network is unreachable";
+ case ENETRESET =>
+ return "Network dropped connection on reset";
+ case ECONNABORTED =>
+ return "Software caused connection abort";
+ case ECONNRESET =>
+ return "Connection reset by peer";
+ case ENOBUFS =>
+ return "No buffer space available";
+ case EISCONN =>
+ return "Transport endpoint is already connected";
+ case ENOTCONN =>
+ return "Transport endpoint is not connected";
+ case ESHUTDOWN =>
+ return "Cannot send after transport endpoint shutdown";
+ case ETOOMANYREFS =>
+ return "Too many references: cannot splice";
+ case ETIMEDOUT =>
+ return "Connection timed out";
+ case ECONNREFUSED =>
+ return "Connection refused";
+ case EHOSTDOWN =>
+ return "Host is down";
+ case EHOSTUNREACH =>
+ return "No route to host";
+ case EALREADY =>
+ return "Operation already in progress";
+ case EINPROGRESS =>
+ return "Operation now in progress";
+ case ESTALE =>
+ return "Stale file handle";
+ case EDQUOT =>
+ return "Disk quota exceeded";
+ case ECANCELED =>
+ return "Operation canceled";
+ case EOWNERDEAD =>
+ return "Owner died";
+ case ENOTRECOVERABLE =>
+ return "State not recoverable";
+ case EAUTH =>
+ return "Authentication error";
+ case EBADRPC =>
+ return "RPC struct is bad";
+ case ECAPMODE =>
+ return "Not permitted in capability mode";
+ case EDOOFUS =>
+ return "Programming error";
+ case EINTEGRITY =>
+ return "Integrity check failed";
+ case ENEEDAUTH =>
+ return "Need authenticator";
+ case ENOATTR =>
+ return "Attribute not found";
+ case ENOTCAPABLE =>
+ return "Capabilities insufficient";
+ case EPROCLIM =>
+ return "Too many processes";
+ case EPROCUNAVAIL =>
+ return "Bad procedure for program";
+ case EPROGMISMATCH =>
+ return "Program version wrong";
+ case EPROGUNAVAIL =>
+ return "RPC program not available";
+ case ERPCMISMATCH =>
+ return "RPC version wrong";
+ case =>
+ return unknown_errno(err);
+ };
+};
+
+// Gets the programmer-friendly name for an [[errno]] (e.g. EPERM). The return
+// value may be statically allocated.
+export fn errname(err: errno) str = {
+ switch (err) {
+ case EPERM =>
+ return "EPERM";
+ case ENOENT =>
+ return "ENOENT";
+ case ESRCH =>
+ return "ESRCH";
+ case EINTR =>
+ return "EINTR";
+ case EIO =>
+ return "EIO";
+ case ENXIO =>
+ return "ENXIO";
+ case E2BIG =>
+ return "E2BIG";
+ case ENOEXEC =>
+ return "ENOEXEC";
+ case EBADF =>
+ return "EBADF";
+ case ECHILD =>
+ return "ECHILD";
+ case EAGAIN =>
+ return "EAGAIN";
+ case ENOMEM =>
+ return "ENOMEM";
+ case EACCES =>
+ return "EACCES";
+ case EFAULT =>
+ return "EFAULT";
+ case ENOTBLK =>
+ return "ENOTBLK";
+ case EBUSY =>
+ return "EBUSY";
+ case EEXIST =>
+ return "EEXIST";
+ case EXDEV =>
+ return "EXDEV";
+ case ENODEV =>
+ return "ENODEV";
+ case ENOTDIR =>
+ return "ENOTDIR";
+ case EISDIR =>
+ return "EISDIR";
+ case EINVAL =>
+ return "EINVAL";
+ case ENFILE =>
+ return "ENFILE";
+ case EMFILE =>
+ return "EMFILE";
+ case ENOTTY =>
+ return "ENOTTY";
+ case ETXTBSY =>
+ return "ETXTBSY";
+ case EFBIG =>
+ return "EFBIG";
+ case ENOSPC =>
+ return "ENOSPC";
+ case ESPIPE =>
+ return "ESPIPE";
+ case EROFS =>
+ return "EROFS";
+ case EMLINK =>
+ return "EMLINK";
+ case EPIPE =>
+ return "EPIPE";
+ case EDOM =>
+ return "EDOM";
+ case ERANGE =>
+ return "ERANGE";
+ case EDEADLK =>
+ return "EDEADLK";
+ case ENAMETOOLONG =>
+ return "ENAMETOOLONG";
+ case ENOLCK =>
+ return "ENOLCK";
+ case ENOSYS =>
+ return "ENOSYS";
+ case ENOTEMPTY =>
+ return "ENOTEMPTY";
+ case ELOOP =>
+ return "ELOOP";
+ case ENOMSG =>
+ return "ENOMSG";
+ case EIDRM =>
+ return "EIDRM";
+ case EREMOTE =>
+ return "EREMOTE";
+ case ENOLINK =>
+ return "ENOLINK";
+ case EPROTO =>
+ return "EPROTO";
+ case EMULTIHOP =>
+ return "EMULTIHOP";
+ case EBADMSG =>
+ return "EBADMSG";
+ case EOVERFLOW =>
+ return "EOVERFLOW";
+ case EILSEQ =>
+ return "EILSEQ";
+ case EUSERS =>
+ return "EUSERS";
+ case ENOTSOCK =>
+ return "ENOTSOCK";
+ case EDESTADDRREQ =>
+ return "EDESTADDRREQ";
+ case EMSGSIZE =>
+ return "EMSGSIZE";
+ case EPROTOTYPE =>
+ return "EPROTOTYPE";
+ case ENOPROTOOPT =>
+ return "ENOPROTOOPT";
+ case EPROTONOSUPPORT =>
+ return "EPROTONOSUPPORT";
+ case ESOCKTNOSUPPORT =>
+ return "ESOCKTNOSUPPORT";
+ case EOPNOTSUPP =>
+ return "EOPNOTSUPP";
+ case EPFNOSUPPORT =>
+ return "EPFNOSUPPORT";
+ case EAFNOSUPPORT =>
+ return "EAFNOSUPPORT";
+ case EADDRINUSE =>
+ return "EADDRINUSE";
+ case EADDRNOTAVAIL =>
+ return "EADDRNOTAVAIL";
+ case ENETDOWN =>
+ return "ENETDOWN";
+ case ENETUNREACH =>
+ return "ENETUNREACH";
+ case ENETRESET =>
+ return "ENETRESET";
+ case ECONNABORTED =>
+ return "ECONNABORTED";
+ case ECONNRESET =>
+ return "ECONNRESET";
+ case ENOBUFS =>
+ return "ENOBUFS";
+ case EISCONN =>
+ return "EISCONN";
+ case ENOTCONN =>
+ return "ENOTCONN";
+ case ESHUTDOWN =>
+ return "ESHUTDOWN";
+ case ETOOMANYREFS =>
+ return "ETOOMANYREFS";
+ case ETIMEDOUT =>
+ return "ETIMEDOUT";
+ case ECONNREFUSED =>
+ return "ECONNREFUSED";
+ case EHOSTDOWN =>
+ return "EHOSTDOWN";
+ case EHOSTUNREACH =>
+ return "EHOSTUNREACH";
+ case EALREADY =>
+ return "EALREADY";
+ case EINPROGRESS =>
+ return "EINPROGRESS";
+ case ESTALE =>
+ return "ESTALE";
+ case EDQUOT =>
+ return "EDQUOT";
+ case ECANCELED =>
+ return "ECANCELED";
+ case EOWNERDEAD =>
+ return "EOWNERDEAD";
+ case ENOTRECOVERABLE =>
+ return "ENOTRECOVERABLE";
+ case EAUTH =>
+ return "EAUTH";
+ case EBADRPC =>
+ return "EBADRPC";
+ case ECAPMODE =>
+ return "ECAPMODE";
+ case EDOOFUS =>
+ return "EDOOFUS";
+ case EINTEGRITY =>
+ return "EINTEGRITY";
+ case ENEEDAUTH =>
+ return "ENEEDAUTH";
+ case ENOATTR =>
+ return "ENOATTR";
+ case ENOTCAPABLE =>
+ return "ENOTCAPABLE";
+ case EPROCLIM =>
+ return "EPROCLIM";
+ case EPROCUNAVAIL =>
+ return "EPROCUNAVAIL";
+ case EPROGMISMATCH =>
+ return "EPROGMISMATCH";
+ case EPROGUNAVAIL =>
+ return "EPROGUNAVAIL";
+ case ERPCMISMATCH =>
+ return "ERPCMISMATCH";
+ case =>
+ return unknown_errno(err);
+ };
+};
+
+export def EPERM: errno = 1;
+export def ENOENT: errno = 2;
+export def ESRCH: errno = 3;
+export def EINTR: errno = 4;
+export def EIO: errno = 5;
+export def ENXIO: errno = 6;
+export def E2BIG: errno = 7;
+export def ENOEXEC: errno = 8;
+export def EBADF: errno = 9;
+export def ECHILD: errno = 10;
+export def EDEADLK: errno = 11;
+export def ENOMEM: errno = 12;
+export def EACCES: errno = 13;
+export def EFAULT: errno = 14;
+export def ENOTBLK: errno = 15;
+export def EBUSY: errno = 16;
+export def EEXIST: errno = 17;
+export def EXDEV: errno = 18;
+export def ENODEV: errno = 19;
+export def ENOTDIR: errno = 20;
+export def EISDIR: errno = 21;
+export def EINVAL: errno = 22;
+export def ENFILE: errno = 23;
+export def EMFILE: errno = 24;
+export def ENOTTY: errno = 25;
+export def ETXTBSY: errno = 26;
+export def EFBIG: errno = 27;
+export def ENOSPC: errno = 28;
+export def ESPIPE: errno = 29;
+export def EROFS: errno = 30;
+export def EMLINK: errno = 31;
+export def EPIPE: errno = 32;
+export def EDOM: errno = 33;
+export def ERANGE: errno = 34;
+export def EAGAIN: errno = 35;
+export def EWOULDBLOCK: errno = EAGAIN;
+export def EINPROGRESS: errno = 36;
+export def EALREADY: errno = 37;
+export def ENOTSOCK: errno = 38;
+export def EDESTADDRREQ: errno = 39;
+export def EMSGSIZE: errno = 40;
+export def EPROTOTYPE: errno = 41;
+export def ENOPROTOOPT: errno = 42;
+export def EPROTONOSUPPORT: errno = 43;
+export def ESOCKTNOSUPPORT: errno = 44;
+export def EOPNOTSUPP: errno = 45;
+export def ENOTSUP: errno = EOPNOTSUPP;
+export def EPFNOSUPPORT: errno = 46;
+export def EAFNOSUPPORT: errno = 47;
+export def EADDRINUSE: errno = 48;
+export def EADDRNOTAVAIL: errno = 49;
+export def ENETDOWN: errno = 50;
+export def ENETUNREACH: errno = 51;
+export def ENETRESET: errno = 52;
+export def ECONNABORTED: errno = 53;
+export def ECONNRESET: errno = 54;
+export def ENOBUFS: errno = 55;
+export def EISCONN: errno = 56;
+export def ENOTCONN: errno = 57;
+export def ESHUTDOWN: errno = 58;
+export def ETOOMANYREFS: errno = 59;
+export def ETIMEDOUT: errno = 60;
+export def ECONNREFUSED: errno = 61;
+export def ELOOP: errno = 62;
+export def ENAMETOOLONG: errno = 63;
+export def EHOSTDOWN: errno = 64;
+export def EHOSTUNREACH: errno = 65;
+export def ENOTEMPTY: errno = 66;
+export def EPROCLIM: errno = 67;
+export def EUSERS: errno = 68;
+export def EDQUOT: errno = 69;
+export def ESTALE: errno = 70;
+export def EREMOTE: errno = 71;
+export def EBADRPC: errno = 72;
+export def ERPCMISMATCH: errno = 73;
+export def EPROGUNAVAIL: errno = 74;
+export def EPROGMISMATCH: errno = 75;
+export def EPROCUNAVAIL: errno = 76;
+export def ENOLCK: errno = 77;
+export def ENOSYS: errno = 78;
+export def EFTYPE: errno = 79;
+export def EAUTH: errno = 80;
+export def ENEEDAUTH: errno = 81;
+export def EIDRM: errno = 82;
+export def ENOMSG: errno = 83;
+export def EOVERFLOW: errno = 84;
+export def ECANCELED: errno = 85;
+export def EILSEQ: errno = 86;
+export def ENOATTR: errno = 87;
+export def EDOOFUS: errno = 88;
+export def EBADMSG: errno = 89;
+export def EMULTIHOP: errno = 90;
+export def ENOLINK: errno = 91;
+export def EPROTO: errno = 92;
+export def ENOTCAPABLE: errno = 93;
+export def ECAPMODE: errno = 94;
+export def ENOTRECOVERABLE: errno = 95;
+export def EOWNERDEAD: errno = 96;
+export def EINTEGRITY: errno = 97;
diff --git a/rt/+netbsd/hare.sc b/rt/+netbsd/hare.sc
@@ -0,0 +1,47 @@
+PHDRS {
+ headers PT_PHDR PHDRS;
+ text PT_LOAD FILEHDR PHDRS;
+ data PT_LOAD;
+ note PT_NOTE;
+}
+ENTRY(_start);
+SECTIONS {
+ . = 0x8000000;
+ .text : {
+ KEEP (*(.text))
+ *(.text.*)
+ } :text
+ . = 0x80000000;
+ .data : {
+ KEEP (*(.data))
+ *(.data.*)
+ } :data
+
+ .init_array : {
+ PROVIDE_HIDDEN (__init_array_start = .);
+ KEEP (*(.init_array))
+ PROVIDE_HIDDEN (__init_array_end = .);
+ } :data
+
+ .fini_array : {
+ PROVIDE_HIDDEN (__fini_array_start = .);
+ KEEP (*(.fini_array))
+ PROVIDE_HIDDEN (__fini_array_end = .);
+ } :data
+
+ .test_array : {
+ PROVIDE_HIDDEN (__test_array_start = .);
+ KEEP (*(.test_array))
+ PROVIDE_HIDDEN (__test_array_end = .);
+ } :data
+
+ .note.netbsd.ident : {
+ KEEP (*(.note.netbsd.ident))
+ *(.note.netbsd.*)
+ } :data :note
+
+ .bss : {
+ KEEP (*(.bss))
+ *(.bss.*)
+ } :data
+}
diff --git a/rt/+netbsd/initfini.ha b/rt/+netbsd/initfini.ha
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// Run all global initialization functions.
+export fn init() void = {
+ const ninit = (&init_end: uintptr - &init_start: uintptr): size
+ / size(*fn() void);
+ for (let i = 0z; i < ninit; i += 1) {
+ init_start[i]();
+ };
+};
+
+// Run all global finalization functions.
+export fn fini() void = {
+ const nfini = (&fini_end: uintptr - &fini_start: uintptr): size
+ / size(*fn() void);
+ for (let i = nfini; i > 0; i -= 1) {
+ fini_start[i - 1]();
+ };
+};
diff --git a/rt/+netbsd/platform_abort.ha b/rt/+netbsd/platform_abort.ha
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+fn platform_abort(path: *str, line: u64, col: u64, msg: str) never = {
+ const prefix = "Abort: ";
+ const sep = ":";
+ const sepspace = ": ";
+ const linefeed = "\n";
+ write(STDERR_FILENO, *(&prefix: **opaque): *const u8, len(prefix)): void;
+ write(STDERR_FILENO, *(path: **opaque): *const u8, len(path)): void;
+ write(STDERR_FILENO, *(&sep: **opaque): *const u8, len(sep)): void;
+ let (line, z) = u64tos(line);
+ write(STDERR_FILENO, line, z): void;
+ write(STDERR_FILENO, *(&sep: **opaque): *const u8, len(sep)): void;
+ let (col, z) = u64tos(col);
+ write(STDERR_FILENO, col, z): void;
+ write(STDERR_FILENO, *(&sepspace: **opaque): *const u8,
+ len(sepspace)): void;
+ write(STDERR_FILENO, *(&msg: **opaque): *const u8, len(msg)): void;
+ write(STDERR_FILENO, *(&linefeed: **opaque): *const u8, 1): void;
+ kill(getpid(), SIGABRT): void;
+ for (true) void;
+};
diff --git a/rt/+netbsd/platformstart-libc.ha b/rt/+netbsd/platformstart-libc.ha
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+export fn start_netbsd(iv: *[*]uintptr) never = {
+ // TODO: Find & parse auxv
+ argc = iv[0]: size;
+ argv = &iv[1]: *[*]*u8;
+ envp = &argv[argc + 1]: *[*]nullable *u8;
+ start_ha();
+};
diff --git a/rt/+netbsd/segmalloc.ha b/rt/+netbsd/segmalloc.ha
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// Allocates a segment.
+fn segmalloc(n: size) nullable *opaque = {
+ return match (mmap(null, n, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0)) {
+ case let err: errno =>
+ assert(err == ENOMEM: errno);
+ yield null;
+ case let p: *opaque =>
+ yield p;
+ };
+};
+
+// Frees a segment allocated with segmalloc.
+fn segfree(p: *opaque, s: size) void = {
+ match (munmap(p, s)) {
+ case let err: errno =>
+ abort("munmap failed");
+ case void => void;
+ };
+};
diff --git a/rt/+netbsd/signal.ha b/rt/+netbsd/signal.ha
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// TODO: work when _NSIG != 32
+
+export fn alarm(sec: uint) uint = {
+ let nval = itimerval { ... };
+ let oval = itimerval { ... };
+ nval.it_value.tv_sec = sec: time_t;
+ setitimer(ITIMER_REAL, &nval, &oval)!;
+ if (oval.it_value.tv_usec != 0) {
+ oval.it_value.tv_sec += 1;
+ };
+ return oval.it_value.tv_sec: uint;
+};
+
+export def ITIMER_REAL: int = 0;
+export def ITIMER_VIRTUAL: int = 1;
+export def ITIMER_PROF: int = 2;
+
+export type itimerval = struct {
+ it_interval: timeval,
+ it_value: timeval,
+};
+
+export fn getitimer(
+ which: int,
+ cur: *itimerval,
+) (void | errno) = {
+ wrap_return(syscall2(SYS_compat_50_getitimer, which: u64, cur: uintptr: u64))?;
+};
+
+export fn setitimer(
+ which: int,
+ newval: *itimerval,
+ oldval: nullable *itimerval,
+) (void | errno) = {
+ wrap_return(syscall3(SYS_compat_50_setitimer,
+ which: u64,
+ newval: uintptr: u64,
+ oldval: uintptr: u64))?;
+};
+
+export fn sigtimedwait(
+ set: *sigset,
+ info: nullable *siginfo,
+ timeout: nullable *timespec,
+) (int | errno) = {
+ return wrap_return(syscall3(SYS_compat_50___sigtimedwait,
+ set: uintptr: u64,
+ info: uintptr: u64,
+ timeout: uintptr: u64,
+ ))?: int;
+};
+
+export fn sigwait(set: *sigset, sig: *int) (void | errno) = {
+ *sig = sigtimedwait(set, null, null)?;
+};
+
+export fn sigwaitinfo(set: *sigset, info: nullable *siginfo) (void | errno) = {
+ sigtimedwait(set, info, null)?;
+};
+
+export fn sigemptyset(set: *sigset) void = {
+ for (let i = 0z; i < len(set.__bits); i += 1) {
+ set.__bits[i] = 0;
+ };
+};
+
+export fn sigaddset(set: *sigset, signum: int) (void | errno) = {
+ if (signum < 1 || signum > NSIG) {
+ return EINVAL;
+ };
+ signum -= 1;
+ set.__bits[signum >> 5] |= (1 << signum): u32;
+};
+
+export fn sigdelset(set: *sigset, signum: int) (void | errno) = {
+ if (signum < 1 || signum > NSIG) {
+ return EINVAL;
+ };
+ signum -= 1;
+ set.__bits[signum >> 5] &= ~(1 << signum: u32);
+};
+
+export fn sigismember(set: *sigset, signum: int) (bool | errno) = {
+ if (signum < 1 || signum > NSIG) {
+ return EINVAL;
+ };
+ signum -= 1;
+ return (set.__bits[signum >> 5] & (1 << signum: u32)) != 0;
+};
+
+export fn sigfillset(set: *sigset) (void | errno) = {
+ for (let i = 0z; i < len(set.__bits); i += 1) {
+ set.__bits[i] = ~0u32;
+ };
+};
+
+// Test sigset operations do not fail for valid signal numbers.
+@test fn sigset_valid_signum() void = {
+ let set: sigset = sigset { ... };
+ sigemptyset(&set);
+
+ assert(!(sigismember(&set, 1) is errno), "Unexpected error");
+ assert(!(sigismember(&set, 15) is errno), "Unexpected error");
+ assert(!(sigismember(&set, NSIG) is errno), "Unexpected error");
+
+ assert(!(sigaddset(&set, 1) is errno), "Unexpected error");
+ assert(!(sigaddset(&set, 15) is errno), "Unexpected error");
+ assert(!(sigaddset(&set, NSIG) is errno), "Unexpected error");
+
+ // It's ok to add a signal that is already present in the set.
+ assert(!(sigaddset(&set, 1) is errno), "Unexpected error");
+
+ assert(!(sigdelset(&set, 1) is errno), "Unexpected error");
+ assert(!(sigdelset(&set, 15) is errno), "Unexpected error");
+ assert(!(sigdelset(&set, NSIG) is errno), "Unexpected error");
+
+ // It's ok to delete a signal that is not present in the set.
+ assert(!(sigdelset(&set, 10) is errno), "Unexpected error");
+};
+
+// Test sigset operations fail for invalid signal numbers.
+@test fn sigset_invalid_signum() void = {
+ let set: sigset = sigset { ... };
+ sigemptyset(&set);
+
+ assert(sigismember(&set, -1) is errno, "Expected error");
+ assert(sigismember(&set, 0) is errno, "Expected error");
+ assert(sigismember(&set, NSIG + 1) is errno, "Expected error");
+
+ assert(sigaddset(&set, -1) is errno, "Expected error");
+ assert(sigaddset(&set, 0) is errno, "Expected error");
+ assert(sigaddset(&set, NSIG + 1) is errno, "Expected error");
+
+ assert(sigdelset(&set, -1) is errno, "Expected error");
+ assert(sigdelset(&set, 0) is errno, "Expected error");
+ assert(sigdelset(&set, NSIG + 1) is errno, "Expected error");
+};
diff --git a/rt/+netbsd/socket.ha b/rt/+netbsd/socket.ha
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+export type sa_family_t = u8;
+export type socklen_t = u32;
+
+export type in_addr = struct {
+ s_addr: u32
+};
+
+export type sockaddr_in = struct {
+ sin_len: u8,
+ sin_family: sa_family_t,
+ sin_port: u16,
+ sin_addr: in_addr,
+ __pad: [8]u8,
+};
+
+export type in6_addr = struct {
+ union {
+ s6_addr: [16]u8,
+ s6_addr16: [8]u16,
+ s6_addr32: [4]u32,
+ }
+};
+
+export type sockaddr_in6 = struct {
+ sin6_len: u8,
+ sin6_family: sa_family_t,
+ sin6_port: u16,
+ sin6_flowinfo: u32,
+ sin6_addr: in6_addr,
+ sin6_scope_id: u32,
+};
+
+export def UNIX_PATH_MAX: size = 104;
+
+export type sockaddr_un = struct {
+ sun_len: u8,
+ sun_family: sa_family_t,
+ sun_path: [UNIX_PATH_MAX]u8,
+};
+
+export type sockaddr = struct {
+ union {
+ in: sockaddr_in,
+ in6: sockaddr_in6,
+ un: sockaddr_un,
+ },
+};
+
+export def SCM_RIGHTS: int = 0x01;
+export def SCM_CREDENTIALS: int = 0x02;
+
+export type msghdr = struct {
+ msg_name: nullable *opaque,
+ msg_namelen: socklen_t,
+
+ msg_iov: nullable *[*]iovec,
+ msg_iovlen: int,
+
+ msg_control: nullable *opaque,
+ msg_controllen: socklen_t,
+
+ msg_flags: int
+};
+
+export type cmsghdr = struct {
+ cmsg_len: socklen_t,
+ cmsg_level: int,
+ cmsg_type: int,
+};
+
+export type cmsg = struct {
+ hdr: cmsghdr,
+ cmsg_data: [*]u8,
+};
+
+export def AF_UNSPEC: sa_family_t = 0;
+export def AF_UNIX: sa_family_t = 1;
+export def AF_LOCAL: sa_family_t = AF_UNIX;
+export def AF_INET: sa_family_t = 2;
+export def AF_IMPLINK: sa_family_t = 3;
+export def AF_PUP: sa_family_t = 4;
+export def AF_CHAOS: sa_family_t = 5;
+export def AF_NETBIOS: sa_family_t = 6;
+export def AF_ISO: sa_family_t = 7;
+export def AF_OSI: sa_family_t = AF_ISO;
+export def AF_ECMA: sa_family_t = 8;
+export def AF_DATAKIT: sa_family_t = 9;
+export def AF_CCITT: sa_family_t = 10;
+export def AF_SNA: sa_family_t = 11;
+export def AF_DECnet: sa_family_t = 12;
+export def AF_DLI: sa_family_t = 13;
+export def AF_LAT: sa_family_t = 14;
+export def AF_HYLINK: sa_family_t = 15;
+export def AF_APPLETALK: sa_family_t = 16;
+export def AF_ROUTE: sa_family_t = 17;
+export def AF_LINK: sa_family_t = 18;
+export def pseudo_AF_XTP: sa_family_t = 19;
+export def AF_COIP: sa_family_t = 20;
+export def AF_CNT: sa_family_t = 21;
+export def pseudo_AF_RTIP: sa_family_t = 22;
+export def AF_IPX: sa_family_t = 23;
+export def AF_SIP: sa_family_t = 24;
+export def pseudo_AF_PIP: sa_family_t = 25;
+export def AF_ISDN: sa_family_t = 26;
+export def AF_E164: sa_family_t = AF_ISDN;
+export def AF_INET6: sa_family_t = 28;
+export def AF_NATM: sa_family_t = 29;
+export def AF_ATM: sa_family_t = 30;
+export def AF_NETGRAPH: sa_family_t = 32;
+export def AF_SLOW: sa_family_t = 33;
+export def AF_SCLUSTER: sa_family_t = 34;
+export def AF_ARP: sa_family_t = 35;
+export def AF_BLUETOOTH: sa_family_t = 36;
+export def AF_IEEE80211: sa_family_t = 37;
+export def AF_INET_SDP: sa_family_t = 40;
+export def AF_INET6_SDP: sa_family_t = 42;
+export def AF_HYPERV: sa_family_t = 43;
+export def AF_MAX: sa_family_t = 43;
+export def AF_VENDOR00: sa_family_t = 39;
+export def AF_VENDOR01: sa_family_t = 41;
+export def AF_VENDOR03: sa_family_t = 45;
+export def AF_VENDOR04: sa_family_t = 47;
+export def AF_VENDOR05: sa_family_t = 49;
+export def AF_VENDOR06: sa_family_t = 51;
+export def AF_VENDOR07: sa_family_t = 53;
+export def AF_VENDOR08: sa_family_t = 55;
+export def AF_VENDOR09: sa_family_t = 57;
+export def AF_VENDOR10: sa_family_t = 59;
+export def AF_VENDOR11: sa_family_t = 61;
+export def AF_VENDOR12: sa_family_t = 63;
+export def AF_VENDOR13: sa_family_t = 65;
+export def AF_VENDOR14: sa_family_t = 67;
+export def AF_VENDOR15: sa_family_t = 69;
+export def AF_VENDOR16: sa_family_t = 71;
+export def AF_VENDOR17: sa_family_t = 73;
+export def AF_VENDOR18: sa_family_t = 75;
+export def AF_VENDOR19: sa_family_t = 77;
+export def AF_VENDOR20: sa_family_t = 79;
+export def AF_VENDOR21: sa_family_t = 81;
+export def AF_VENDOR22: sa_family_t = 83;
+export def AF_VENDOR23: sa_family_t = 85;
+export def AF_VENDOR24: sa_family_t = 87;
+export def AF_VENDOR25: sa_family_t = 89;
+export def AF_VENDOR26: sa_family_t = 91;
+export def AF_VENDOR27: sa_family_t = 93;
+export def AF_VENDOR28: sa_family_t = 95;
+export def AF_VENDOR29: sa_family_t = 97;
+export def AF_VENDOR30: sa_family_t = 99;
+export def AF_VENDOR31: sa_family_t = 101;
+export def AF_VENDOR32: sa_family_t = 103;
+export def AF_VENDOR33: sa_family_t = 105;
+export def AF_VENDOR34: sa_family_t = 107;
+export def AF_VENDOR35: sa_family_t = 109;
+export def AF_VENDOR36: sa_family_t = 111;
+export def AF_VENDOR37: sa_family_t = 113;
+export def AF_VENDOR38: sa_family_t = 115;
+export def AF_VENDOR39: sa_family_t = 117;
+export def AF_VENDOR40: sa_family_t = 119;
+export def AF_VENDOR41: sa_family_t = 121;
+export def AF_VENDOR42: sa_family_t = 123;
+export def AF_VENDOR43: sa_family_t = 125;
+export def AF_VENDOR44: sa_family_t = 127;
+export def AF_VENDOR45: sa_family_t = 129;
+export def AF_VENDOR46: sa_family_t = 131;
+export def AF_VENDOR47: sa_family_t = 133;
+
+export def SOCK_STREAM: int = 1;
+export def SOCK_DGRAM: int = 2;
+export def SOCK_RAW: int = 3;
+export def SOCK_RDM: int = 4;
+export def SOCK_SEQPACKET: int = 5;
+export def SOCK_CLOEXEC: int = 0x10000000;
+export def SOCK_NONBLOCK: int = 0x20000000;
+
+export def IPPROTO_IP: int = 0;
+export def IPPROTO_ICMP: int = 1;
+export def IPPROTO_TCP: int = 6;
+export def IPPROTO_UDP: int = 17;
+export def IPPROTO_IPV6: int = 41;
+export def IPPROTO_RAW: int = 255;
+export def IPPROTO_HOPOPTS: int = 0;
+export def IPPROTO_IGMP: int = 2;
+export def IPPROTO_GGP: int = 3;
+export def IPPROTO_IPV4: int = 4;
+export def IPPROTO_IPIP: int = IPPROTO_IPV4;
+export def IPPROTO_ST: int = 7;
+export def IPPROTO_EGP: int = 8;
+export def IPPROTO_PIGP: int = 9;
+export def IPPROTO_RCCMON: int = 10;
+export def IPPROTO_NVPII: int = 11;
+export def IPPROTO_PUP: int = 12;
+export def IPPROTO_ARGUS: int = 13;
+export def IPPROTO_EMCON: int = 14;
+export def IPPROTO_XNET: int = 15;
+export def IPPROTO_CHAOS: int = 16;
+export def IPPROTO_MUX: int = 18;
+export def IPPROTO_MEAS: int = 19;
+export def IPPROTO_HMP: int = 20;
+export def IPPROTO_PRM: int = 21;
+export def IPPROTO_IDP: int = 22;
+export def IPPROTO_TRUNK1: int = 23;
+export def IPPROTO_TRUNK2: int = 24;
+export def IPPROTO_LEAF1: int = 25;
+export def IPPROTO_LEAF2: int = 26;
+export def IPPROTO_RDP: int = 27;
+export def IPPROTO_IRTP: int = 28;
+export def IPPROTO_TP: int = 29;
+export def IPPROTO_BLT: int = 30;
+export def IPPROTO_NSP: int = 31;
+export def IPPROTO_INP: int = 32;
+export def IPPROTO_DCCP: int = 33;
+export def IPPROTO_3PC: int = 34;
+export def IPPROTO_IDPR: int = 35;
+export def IPPROTO_XTP: int = 36;
+export def IPPROTO_DDP: int = 37;
+export def IPPROTO_CMTP: int = 38;
+export def IPPROTO_TPXX: int = 39;
+export def IPPROTO_IL: int = 40;
+export def IPPROTO_SDRP: int = 42;
+export def IPPROTO_ROUTING: int = 43;
+export def IPPROTO_FRAGMENT: int = 44;
+export def IPPROTO_IDRP: int = 45;
+export def IPPROTO_RSVP: int = 46;
+export def IPPROTO_GRE: int = 47;
+export def IPPROTO_MHRP: int = 48;
+export def IPPROTO_BHA: int = 49;
+export def IPPROTO_ESP: int = 50;
+export def IPPROTO_AH: int = 51;
+export def IPPROTO_INLSP: int = 52;
+export def IPPROTO_SWIPE: int = 53;
+export def IPPROTO_NHRP: int = 54;
+export def IPPROTO_MOBILE: int = 55;
+export def IPPROTO_TLSP: int = 56;
+export def IPPROTO_SKIP: int = 57;
+export def IPPROTO_ICMPV6: int = 58;
+export def IPPROTO_NONE: int = 59;
+export def IPPROTO_DSTOPTS: int = 60;
+export def IPPROTO_AHIP: int = 61;
+export def IPPROTO_CFTP: int = 62;
+export def IPPROTO_HELLO: int = 63;
+export def IPPROTO_SATEXPAK: int = 64;
+export def IPPROTO_KRYPTOLAN: int = 65;
+export def IPPROTO_RVD: int = 66;
+export def IPPROTO_IPPC: int = 67;
+export def IPPROTO_ADFS: int = 68;
+export def IPPROTO_SATMON: int = 69;
+export def IPPROTO_VISA: int = 70;
+export def IPPROTO_IPCV: int = 71;
+export def IPPROTO_CPNX: int = 72;
+export def IPPROTO_CPHB: int = 73;
+export def IPPROTO_WSN: int = 74;
+export def IPPROTO_PVP: int = 75;
+export def IPPROTO_BRSATMON: int = 76;
+export def IPPROTO_ND: int = 77;
+export def IPPROTO_WBMON: int = 78;
+export def IPPROTO_WBEXPAK: int = 79;
+export def IPPROTO_EON: int = 80;
+export def IPPROTO_VMTP: int = 81;
+export def IPPROTO_SVMTP: int = 82;
+export def IPPROTO_VINES: int = 83;
+export def IPPROTO_TTP: int = 84;
+export def IPPROTO_IGP: int = 85;
+export def IPPROTO_DGP: int = 86;
+export def IPPROTO_TCF: int = 87;
+export def IPPROTO_IGRP: int = 88;
+export def IPPROTO_OSPFIGP: int = 89;
+export def IPPROTO_SRPC: int = 90;
+export def IPPROTO_LARP: int = 91;
+export def IPPROTO_MTP: int = 92;
+export def IPPROTO_AX25: int = 93;
+export def IPPROTO_IPEIP: int = 94;
+export def IPPROTO_MICP: int = 95;
+export def IPPROTO_SCCSP: int = 96;
+export def IPPROTO_ETHERIP: int = 97;
+export def IPPROTO_ENCAP: int = 98;
+export def IPPROTO_APES: int = 99;
+export def IPPROTO_GMTP: int = 100;
+export def IPPROTO_IPCOMP: int = 108;
+export def IPPROTO_SCTP: int = 132;
+export def IPPROTO_MH: int = 135;
+export def IPPROTO_UDPLITE: int = 136;
+export def IPPROTO_HIP: int = 139;
+export def IPPROTO_SHIM6: int = 140;
+export def IPPROTO_PIM: int = 103;
+export def IPPROTO_CARP: int = 112;
+export def IPPROTO_PGM: int = 113;
+export def IPPROTO_MPLS: int = 137;
+export def IPPROTO_PFSYNC: int = 240;
+export def IPPROTO_RESERVED_253: int = 253;
+export def IPPROTO_RESERVED_254: int = 254;
+
+export def MSG_OOB: int = 0x00000001;
+export def MSG_PEEK: int = 0x00000002;
+export def MSG_DONTROUTE: int = 0x00000004;
+export def MSG_EOR: int = 0x00000008;
+export def MSG_TRUNC: int = 0x00000010;
+export def MSG_CTRUNC: int = 0x00000020;
+export def MSG_WAITALL: int = 0x00000040;
+export def MSG_DONTWAIT: int = 0x00000080;
+export def MSG_EOF: int = 0x00000100;
+export def MSG_NOTIFICATION: int = 0x00002000;
+export def MSG_NBIO: int = 0x00004000;
+export def MSG_COMPAT: int = 0x00008000;
+export def MSG_NOSIGNAL: int = 0x00020000;
+export def MSG_CMSG_CLOEXEC: int = 0x00040000;
+export def MSG_WAITFORONE: int = 0x00080000;
+
+export def SO_DEBUG: int = 0x00000001;
+export def SO_ACCEPTCONN: int = 0x00000002;
+export def SO_REUSEADDR: int = 0x00000004;
+export def SO_KEEPALIVE: int = 0x00000008;
+export def SO_DONTROUTE: int = 0x00000010;
+export def SO_BROADCAST: int = 0x00000020;
+export def SO_USELOOPBACK: int = 0x00000040;
+export def SO_LINGER: int = 0x00000080;
+export def SO_OOBINLINE: int = 0x00000100;
+export def SO_REUSEPORT: int = 0x00000200;
+export def SO_TIMESTAMP: int = 0x00000400;
+export def SO_NOSIGPIPE: int = 0x00000800;
+export def SO_ACCEPTFILTER: int = 0x00001000;
+export def SO_BINTIME: int = 0x00002000;
+export def SO_NO_OFFLOAD: int = 0x00004000;
+export def SO_NO_DDP: int = 0x00008000;
+export def SO_REUSEPORT_LB: int = 0x00010000;
+export def SO_SNDBUF: int = 0x1001;
+export def SO_RCVBUF: int = 0x1002;
+export def SO_SNDLOWAT: int = 0x1003;
+export def SO_RCVLOWAT: int = 0x1004;
+export def SO_SNDTIMEO: int = 0x1005;
+export def SO_RCVTIMEO: int = 0x1006;
+export def SO_ERROR: int = 0x1007;
+export def SO_TYPE: int = 0x1008;
+export def SO_LABEL: int = 0x1009;
+export def SO_PEERLABEL: int = 0x1010;
+export def SO_LISTENQLIMIT: int = 0x1011;
+export def SO_LISTENQLEN: int = 0x1012;
+export def SO_LISTENINCQLEN: int = 0x1013;
+export def SO_SETFIB: int = 0x1014;
+export def SO_USER_COOKIE: int = 0x1015;
+export def SO_PROTOCOL: int = 0x1016;
+export def SO_PROTOTYPE: int = SO_PROTOCOL;
+export def SO_TS_CLOCK: int = 0x1017;
+export def SO_MAX_PACING_RATE: int = 0x1018;
+export def SO_DOMAIN: int = 0x1019;
+export def SO_TS_REALTIME_MICRO: int = 0;
+export def SO_TS_BINTIME: int = 1;
+export def SO_TS_REALTIME: int = 2;
+export def SO_TS_MONOTONIC: int = 3;
+export def SO_TS_DEFAULT: int = SO_TS_REALTIME_MICRO;
+export def SO_TS_CLOCK_MAX: int = SO_TS_MONOTONIC;
+
+export def SOL_SOCKET: int = 0xffff;
diff --git a/rt/+netbsd/start+test.ha b/rt/+netbsd/start+test.ha
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+@symbol("__test_main") fn test_main() size;
+
+const @symbol("__init_array_start") init_start: [*]*fn() void;
+const @symbol("__init_array_end") init_end: [*]*fn() void;
+const @symbol("__fini_array_start") fini_start: [*]*fn() void;
+const @symbol("__fini_array_end") fini_end: [*]*fn() void;
+
+export fn start_ha() never = {
+ init();
+ const nfail = test_main();
+ fini();
+ exit(if (nfail > 0) 1 else 0);
+};
diff --git a/rt/+netbsd/start+x86_64-libc.s b/rt/+netbsd/start+x86_64-libc.s
@@ -0,0 +1,16 @@
+.section ".note.netbsd.ident", "a"
+ .long 2f-1f
+ .long 4f-3f
+ .long 1
+1: .asciz "NetBSD"
+2: .p2align 2
+3: .long 199905
+4: .p2align 2
+
+.text
+.global _start
+_start:
+ xor %rbp, %rbp
+ movq %rsp, %rdi
+ and $-16, %rsp
+ call rt.start_netbsd
diff --git a/rt/+netbsd/start.ha b/rt/+netbsd/start.ha
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+@symbol("main") fn main() void;
+
+const @symbol("__init_array_start") init_start: [*]*fn() void;
+const @symbol("__init_array_end") init_end: [*]*fn() void;
+const @symbol("__fini_array_start") fini_start: [*]*fn() void;
+const @symbol("__fini_array_end") fini_end: [*]*fn() void;
+
+export fn start_ha() never = {
+ init();
+ main();
+ fini();
+ exit(0);
+};
diff --git a/rt/+netbsd/syscall+x86_64.s b/rt/+netbsd/syscall+x86_64.s
@@ -0,0 +1,81 @@
+.section .text
+error:
+ neg %rax
+ ret
+
+.section .text.rt.syscall0
+.global rt.syscall0
+rt.syscall0:
+ movq %rdi, %rax
+ syscall
+ jc error
+ ret
+
+.section .text.rt.syscall1
+.global rt.syscall1
+rt.syscall1:
+ movq %rdi, %rax
+ movq %rsi, %rdi
+ syscall
+ jc error
+ ret
+
+.section .text.rt.syscall2
+.global rt.syscall2
+rt.syscall2:
+ movq %rdi, %rax
+ movq %rsi, %rdi
+ movq %rdx, %rsi
+ syscall
+ jc error
+ ret
+
+.section .text.rt.syscall3
+.global rt.syscall3
+rt.syscall3:
+ movq %rdi, %rax
+ movq %rsi, %rdi
+ movq %rdx, %rsi
+ movq %rcx, %rdx
+ syscall
+ jc error
+ ret
+
+.section .text.rt.syscall4
+.global rt.syscall4
+rt.syscall4:
+ movq %rdi, %rax
+ movq %r8, %r10
+ movq %rsi, %rdi
+ movq %rdx, %rsi
+ movq %rcx, %rdx
+ syscall
+ jc error
+ ret
+
+.section .text.rt.syscall5
+.global rt.syscall5
+rt.syscall5:
+ movq %rdi, %rax
+ movq %r8, %r10
+ movq %rsi, %rdi
+ movq %r9, %r8
+ movq %rdx, %rsi
+ movq %rcx, %rdx
+ syscall
+ jc error
+ ret
+
+.section .text.rt.syscall6
+.global rt.syscall6
+rt.syscall6:
+ movq %rdi, %rax
+ movq %r8, %r10
+ movq %rsi, %rdi
+ movq %r9, %r8
+ movq %rdx, %rsi
+ movq 8(%rsp), %r9
+ movq %rcx, %rdx
+ syscall
+ jc error
+ ret
diff --git a/rt/+netbsd/syscallno.ha b/rt/+netbsd/syscallno.ha
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+export def SYS_syscall: u64 = 0;
+export def SYS_exit: u64 = 1;
+export def SYS_fork: u64 = 2;
+export def SYS_read: u64 = 3;
+export def SYS_write: u64 = 4;
+export def SYS_open: u64 = 5;
+export def SYS_close: u64 = 6;
+export def SYS_compat_50_wait4: u64 = 7;
+export def SYS_compat_43_ocreat: u64 = 8;
+export def SYS_link: u64 = 9;
+export def SYS_unlink: u64 = 10;
+export def SYS_chdir: u64 = 12;
+export def SYS_fchdir: u64 = 13;
+export def SYS_compat_50_mknod: u64 = 14;
+export def SYS_chmod: u64 = 15;
+export def SYS_chown: u64 = 16;
+export def SYS_break: u64 = 17;
+export def SYS_compat_20_getfsstat: u64 = 18;
+export def SYS_compat_43_olseek: u64 = 19;
+export def SYS_getpid: u64 = 20;
+export def SYS_compat_40_mount: u64 = 21;
+export def SYS_unmount: u64 = 22;
+export def SYS_setuid: u64 = 23;
+export def SYS_getuid: u64 = 24;
+export def SYS_geteuid: u64 = 25;
+export def SYS_ptrace: u64 = 26;
+export def SYS_recvmsg: u64 = 27;
+export def SYS_sendmsg: u64 = 28;
+export def SYS_recvfrom: u64 = 29;
+export def SYS_accept: u64 = 30;
+export def SYS_getpeername: u64 = 31;
+export def SYS_getsockname: u64 = 32;
+export def SYS_access: u64 = 33;
+export def SYS_chflags: u64 = 34;
+export def SYS_fchflags: u64 = 35;
+export def SYS_sync: u64 = 36;
+export def SYS_kill: u64 = 37;
+export def SYS_compat_43_stat43: u64 = 38;
+export def SYS_getppid: u64 = 39;
+export def SYS_compat_43_lstat43: u64 = 40;
+export def SYS_dup: u64 = 41;
+export def SYS_pipe: u64 = 42;
+export def SYS_getegid: u64 = 43;
+export def SYS_profil: u64 = 44;
+export def SYS_ktrace: u64 = 45;
+export def SYS_compat_13_sigaction13: u64 = 46;
+export def SYS_getgid: u64 = 47;
+export def SYS_compat_13_sigprocmask13: u64 = 48;
+export def SYS___getlogin: u64 = 49;
+export def SYS___setlogin: u64 = 50;
+export def SYS_acct: u64 = 51;
+export def SYS_compat_13_sigpending13: u64 = 52;
+export def SYS_compat_13_sigaltstack13: u64 = 53;
+export def SYS_ioctl: u64 = 54;
+export def SYS_compat_12_oreboot: u64 = 55;
+export def SYS_revoke: u64 = 56;
+export def SYS_symlink: u64 = 57;
+export def SYS_readlink: u64 = 58;
+export def SYS_execve: u64 = 59;
+export def SYS_umask: u64 = 60;
+export def SYS_chroot: u64 = 61;
+export def SYS_compat_43_fstat43: u64 = 62;
+export def SYS_compat_43_ogetkerninfo: u64 = 63;
+export def SYS_compat_43_ogetpagesize: u64 = 64;
+export def SYS_compat_12_msync: u64 = 65;
+export def SYS_vfork: u64 = 66;
+export def SYS_compat_43_ommap: u64 = 71;
+export def SYS_vadvise: u64 = 72;
+export def SYS_munmap: u64 = 73;
+export def SYS_mprotect: u64 = 74;
+export def SYS_madvise: u64 = 75;
+export def SYS_mincore: u64 = 78;
+export def SYS_getgroups: u64 = 79;
+export def SYS_setgroups: u64 = 80;
+export def SYS_getpgrp: u64 = 81;
+export def SYS_setpgid: u64 = 82;
+export def SYS_compat_50_setitimer: u64 = 83;
+export def SYS_compat_43_owait: u64 = 84;
+export def SYS_compat_12_oswapon: u64 = 85;
+export def SYS_compat_50_getitimer: u64 = 86;
+export def SYS_compat_43_ogethostname: u64 = 87;
+export def SYS_compat_43_osethostname: u64 = 88;
+export def SYS_compat_43_ogetdtablesize: u64 = 89;
+export def SYS_dup2: u64 = 90;
+export def SYS_getrandom: u64 = 91;
+export def SYS_fcntl: u64 = 92;
+export def SYS_compat_50_select: u64 = 93;
+export def SYS_fsync: u64 = 95;
+export def SYS_setpriority: u64 = 96;
+export def SYS_compat_30_socket: u64 = 97;
+export def SYS_connect: u64 = 98;
+export def SYS_compat_43_oaccept: u64 = 99;
+export def SYS_getpriority: u64 = 100;
+export def SYS_compat_43_osend: u64 = 101;
+export def SYS_compat_43_orecv: u64 = 102;
+export def SYS_compat_13_sigreturn13: u64 = 103;
+export def SYS_bind: u64 = 104;
+export def SYS_setsockopt: u64 = 105;
+export def SYS_listen: u64 = 106;
+export def SYS_compat_43_osigvec: u64 = 108;
+export def SYS_compat_43_osigblock: u64 = 109;
+export def SYS_compat_43_osigsetmask: u64 = 110;
+export def SYS_compat_13_sigsuspend13: u64 = 111;
+export def SYS_compat_43_osigstack: u64 = 112;
+export def SYS_compat_43_orecvmsg: u64 = 113;
+export def SYS_compat_43_osendmsg: u64 = 114;
+export def SYS_compat_50_gettimeofday: u64 = 116;
+export def SYS_compat_50_getrusage: u64 = 117;
+export def SYS_getsockopt: u64 = 118;
+export def SYS_readv: u64 = 120;
+export def SYS_writev: u64 = 121;
+export def SYS_compat_50_settimeofday: u64 = 122;
+export def SYS_fchown: u64 = 123;
+export def SYS_fchmod: u64 = 124;
+export def SYS_compat_43_orecvfrom: u64 = 125;
+export def SYS_rename: u64 = 128;
+export def SYS_compat_43_otruncate: u64 = 129;
+export def SYS_compat_43_oftruncate: u64 = 130;
+export def SYS_flock: u64 = 131;
+export def SYS_mkfifo: u64 = 132;
+export def SYS_sendto: u64 = 133;
+export def SYS_shutdown: u64 = 134;
+export def SYS_socketpair: u64 = 135;
+export def SYS_mkdir: u64 = 136;
+export def SYS_rmdir: u64 = 137;
+export def SYS_compat_50_utimes: u64 = 138;
+export def SYS_compat_50_adjtime: u64 = 140;
+export def SYS_compat_43_ogetpeername: u64 = 141;
+export def SYS_compat_43_ogethostid: u64 = 142;
+export def SYS_compat_43_osethostid: u64 = 143;
+export def SYS_compat_43_ogetrlimit: u64 = 144;
+export def SYS_compat_43_osetrlimit: u64 = 145;
+export def SYS_compat_43_okillpg: u64 = 146;
+export def SYS_setsid: u64 = 147;
+export def SYS_compat_50_quotactl: u64 = 148;
+export def SYS_compat_43_oquota: u64 = 149;
+export def SYS_compat_43_ogetsockname: u64 = 150;
+export def SYS_nfssvc: u64 = 155;
+export def SYS_compat_43_ogetdirentries: u64 = 156;
+export def SYS_compat_20_statfs: u64 = 157;
+export def SYS_compat_20_fstatfs: u64 = 158;
+export def SYS_compat_30_getfh: u64 = 161;
+export def SYS_compat_09_ogetdomainname: u64 = 162;
+export def SYS_compat_09_osetdomainname: u64 = 163;
+export def SYS_compat_09_ouname: u64 = 164;
+export def SYS_sysarch: u64 = 165;
+export def SYS_compat_10_osemsys: u64 = 169;
+export def SYS_compat_10_omsgsys: u64 = 170;
+export def SYS_compat_10_oshmsys: u64 = 171;
+export def SYS_pread: u64 = 173;
+export def SYS_pwrite: u64 = 174;
+export def SYS_compat_30_ntp_gettime: u64 = 175;
+export def SYS_ntp_adjtime: u64 = 176;
+export def SYS_setgid: u64 = 181;
+export def SYS_setegid: u64 = 182;
+export def SYS_seteuid: u64 = 183;
+export def SYS_lfs_bmapv: u64 = 184;
+export def SYS_lfs_markv: u64 = 185;
+export def SYS_lfs_segclean: u64 = 186;
+export def SYS_compat_50_lfs_segwait: u64 = 187;
+export def SYS_compat_12_stat12: u64 = 188;
+export def SYS_compat_12_fstat12: u64 = 189;
+export def SYS_compat_12_lstat12: u64 = 190;
+export def SYS_pathconf: u64 = 191;
+export def SYS_fpathconf: u64 = 192;
+export def SYS_getsockopt2: u64 = 193;
+export def SYS_getrlimit: u64 = 194;
+export def SYS_setrlimit: u64 = 195;
+export def SYS_compat_12_getdirentries: u64 = 196;
+export def SYS_mmap: u64 = 197;
+export def SYS___syscall: u64 = 198;
+export def SYS_lseek: u64 = 199;
+export def SYS_truncate: u64 = 200;
+export def SYS_ftruncate: u64 = 201;
+export def SYS___sysctl: u64 = 202;
+export def SYS_mlock: u64 = 203;
+export def SYS_munlock: u64 = 204;
+export def SYS_undelete: u64 = 205;
+export def SYS_compat_50_futimes: u64 = 206;
+export def SYS_getpgid: u64 = 207;
+export def SYS_reboot: u64 = 208;
+export def SYS_poll: u64 = 209;
+export def SYS_afssys: u64 = 210;
+export def SYS_compat_14___semctl: u64 = 220;
+export def SYS_semget: u64 = 221;
+export def SYS_semop: u64 = 222;
+export def SYS_semconfig: u64 = 223;
+export def SYS_compat_14_msgctl: u64 = 224;
+export def SYS_msgget: u64 = 225;
+export def SYS_msgsnd: u64 = 226;
+export def SYS_msgrcv: u64 = 227;
+export def SYS_shmat: u64 = 228;
+export def SYS_compat_14_shmctl: u64 = 229;
+export def SYS_shmdt: u64 = 230;
+export def SYS_shmget: u64 = 231;
+export def SYS_compat_50_clock_gettime: u64 = 232;
+export def SYS_compat_50_clock_settime: u64 = 233;
+export def SYS_compat_50_clock_getres: u64 = 234;
+export def SYS_timer_create: u64 = 235;
+export def SYS_timer_delete: u64 = 236;
+export def SYS_compat_50_timer_settime: u64 = 237;
+export def SYS_compat_50_timer_gettime: u64 = 238;
+export def SYS_timer_getoverrun: u64 = 239;
+export def SYS_compat_50_nanosleep: u64 = 240;
+export def SYS_fdatasync: u64 = 241;
+export def SYS_mlockall: u64 = 242;
+export def SYS_munlockall: u64 = 243;
+export def SYS_compat_50___sigtimedwait: u64 = 244;
+export def SYS_sigqueueinfo: u64 = 245;
+export def SYS_modctl: u64 = 246;
+export def SYS__ksem_init: u64 = 247;
+export def SYS__ksem_open: u64 = 248;
+export def SYS__ksem_unlink: u64 = 249;
+export def SYS__ksem_close: u64 = 250;
+export def SYS__ksem_post: u64 = 251;
+export def SYS__ksem_wait: u64 = 252;
+export def SYS__ksem_trywait: u64 = 253;
+export def SYS__ksem_getvalue: u64 = 254;
+export def SYS__ksem_destroy: u64 = 255;
+export def SYS__ksem_timedwait: u64 = 256;
+export def SYS_mq_open: u64 = 257;
+export def SYS_mq_close: u64 = 258;
+export def SYS_mq_unlink: u64 = 259;
+export def SYS_mq_getattr: u64 = 260;
+export def SYS_mq_setattr: u64 = 261;
+export def SYS_mq_notify: u64 = 262;
+export def SYS_mq_send: u64 = 263;
+export def SYS_mq_receive: u64 = 264;
+export def SYS_compat_50_mq_timedsend: u64 = 265;
+export def SYS_compat_50_mq_timedreceive: u64 = 266;
+export def SYS___posix_rename: u64 = 270;
+export def SYS_swapctl: u64 = 271;
+export def SYS_compat_30_getdents: u64 = 272;
+export def SYS_minherit: u64 = 273;
+export def SYS_lchmod: u64 = 274;
+export def SYS_lchown: u64 = 275;
+export def SYS_compat_50_lutimes: u64 = 276;
+export def SYS___msync13: u64 = 277;
+export def SYS_compat_30___stat13: u64 = 278;
+export def SYS_compat_30___fstat13: u64 = 279;
+export def SYS_compat_30___lstat13: u64 = 280;
+export def SYS___sigaltstack14: u64 = 281;
+export def SYS___vfork14: u64 = 282;
+export def SYS___posix_chown: u64 = 283;
+export def SYS___posix_fchown: u64 = 284;
+export def SYS___posix_lchown: u64 = 285;
+export def SYS_getsid: u64 = 286;
+export def SYS___clone: u64 = 287;
+export def SYS_fktrace: u64 = 288;
+export def SYS_preadv: u64 = 289;
+export def SYS_pwritev: u64 = 290;
+export def SYS_compat_16___sigaction14: u64 = 291;
+export def SYS___sigpending14: u64 = 292;
+export def SYS___sigprocmask14: u64 = 293;
+export def SYS___sigsuspend14: u64 = 294;
+export def SYS_compat_16___sigreturn14: u64 = 295;
+export def SYS___getcwd: u64 = 296;
+export def SYS_fchroot: u64 = 297;
+export def SYS_compat_30_fhopen: u64 = 298;
+export def SYS_compat_30_fhstat: u64 = 299;
+export def SYS_compat_20_fhstatfs: u64 = 300;
+export def SYS_compat_50_____semctl13: u64 = 301;
+export def SYS_compat_50___msgctl13: u64 = 302;
+export def SYS_compat_50___shmctl13: u64 = 303;
+export def SYS_lchflags: u64 = 304;
+export def SYS_issetugid: u64 = 305;
+export def SYS_utrace: u64 = 306;
+export def SYS_getcontext: u64 = 307;
+export def SYS_setcontext: u64 = 308;
+export def SYS__lwp_create: u64 = 309;
+export def SYS__lwp_exit: u64 = 310;
+export def SYS__lwp_self: u64 = 311;
+export def SYS__lwp_wait: u64 = 312;
+export def SYS__lwp_suspend: u64 = 313;
+export def SYS__lwp_continue: u64 = 314;
+export def SYS__lwp_wakeup: u64 = 315;
+export def SYS__lwp_getprivate: u64 = 316;
+export def SYS__lwp_setprivate: u64 = 317;
+export def SYS__lwp_kill: u64 = 318;
+export def SYS__lwp_detach: u64 = 319;
+export def SYS_compat_50__lwp_park: u64 = 320;
+export def SYS__lwp_unpark: u64 = 321;
+export def SYS__lwp_unpark_all: u64 = 322;
+export def SYS__lwp_setname: u64 = 323;
+export def SYS__lwp_getname: u64 = 324;
+export def SYS__lwp_ctl: u64 = 325;
+export def SYS_compat_60_sa_register: u64 = 330;
+export def SYS_compat_60_sa_stacks: u64 = 331;
+export def SYS_compat_60_sa_enable: u64 = 332;
+export def SYS_compat_60_sa_setconcurrency: u64 = 333;
+export def SYS_compat_60_sa_yield: u64 = 334;
+export def SYS_compat_60_sa_preempt: u64 = 335;
+export def SYS___sigaction_sigtramp: u64 = 340;
+export def SYS_rasctl: u64 = 343;
+export def SYS_kqueue: u64 = 344;
+export def SYS_compat_50_kevent: u64 = 345;
+export def SYS__sched_setparam: u64 = 346;
+export def SYS__sched_getparam: u64 = 347;
+export def SYS__sched_setaffinity: u64 = 348;
+export def SYS__sched_getaffinity: u64 = 349;
+export def SYS_sched_yield: u64 = 350;
+export def SYS__sched_protect: u64 = 351;
+export def SYS_fsync_range: u64 = 354;
+export def SYS_uuidgen: u64 = 355;
+export def SYS_getvfsstat: u64 = 356;
+export def SYS_statvfs1: u64 = 357;
+export def SYS_fstatvfs1: u64 = 358;
+export def SYS_compat_30_fhstatvfs1: u64 = 359;
+export def SYS_extattrctl: u64 = 360;
+export def SYS_extattr_set_file: u64 = 361;
+export def SYS_extattr_get_file: u64 = 362;
+export def SYS_extattr_delete_file: u64 = 363;
+export def SYS_extattr_set_fd: u64 = 364;
+export def SYS_extattr_get_fd: u64 = 365;
+export def SYS_extattr_delete_fd: u64 = 366;
+export def SYS_extattr_set_link: u64 = 367;
+export def SYS_extattr_get_link: u64 = 368;
+export def SYS_extattr_delete_link: u64 = 369;
+export def SYS_extattr_list_fd: u64 = 370;
+export def SYS_extattr_list_file: u64 = 371;
+export def SYS_extattr_list_link: u64 = 372;
+export def SYS_compat_50_pselect: u64 = 373;
+export def SYS_compat_50_pollts: u64 = 374;
+export def SYS_setxattr: u64 = 375;
+export def SYS_lsetxattr: u64 = 376;
+export def SYS_fsetxattr: u64 = 377;
+export def SYS_getxattr: u64 = 378;
+export def SYS_lgetxattr: u64 = 379;
+export def SYS_fgetxattr: u64 = 380;
+export def SYS_listxattr: u64 = 381;
+export def SYS_llistxattr: u64 = 382;
+export def SYS_flistxattr: u64 = 383;
+export def SYS_removexattr: u64 = 384;
+export def SYS_lremovexattr: u64 = 385;
+export def SYS_fremovexattr: u64 = 386;
+export def SYS_compat_50___stat30: u64 = 387;
+export def SYS_compat_50___fstat30: u64 = 388;
+export def SYS_compat_50___lstat30: u64 = 389;
+export def SYS___getdents30: u64 = 390;
+export def SYS_compat_30___fhstat30: u64 = 392;
+export def SYS_compat_50___ntp_gettime30: u64 = 393;
+export def SYS___socket30: u64 = 394;
+export def SYS___getfh30: u64 = 395;
+export def SYS___fhopen40: u64 = 396;
+export def SYS___fhstatvfs140: u64 = 397;
+export def SYS_compat_50___fhstat40: u64 = 398;
+export def SYS_aio_cancel: u64 = 399;
+export def SYS_aio_error: u64 = 400;
+export def SYS_aio_fsync: u64 = 401;
+export def SYS_aio_read: u64 = 402;
+export def SYS_aio_return: u64 = 403;
+export def SYS_compat_50_aio_suspend: u64 = 404;
+export def SYS_aio_write: u64 = 405;
+export def SYS_lio_listio: u64 = 406;
+export def SYS___mount50: u64 = 410;
+export def SYS_mremap: u64 = 411;
+export def SYS_pset_create: u64 = 412;
+export def SYS_pset_destroy: u64 = 413;
+export def SYS_pset_assign: u64 = 414;
+export def SYS__pset_bind: u64 = 415;
+export def SYS___posix_fadvise50: u64 = 416;
+export def SYS___select50: u64 = 417;
+export def SYS___gettimeofday50: u64 = 418;
+export def SYS___settimeofday50: u64 = 419;
+export def SYS___utimes50: u64 = 420;
+export def SYS___adjtime50: u64 = 421;
+export def SYS___lfs_segwait50: u64 = 422;
+export def SYS___futimes50: u64 = 423;
+export def SYS___lutimes50: u64 = 424;
+export def SYS___setitimer50: u64 = 425;
+export def SYS___getitimer50: u64 = 426;
+export def SYS___clock_gettime50: u64 = 427;
+export def SYS___clock_settime50: u64 = 428;
+export def SYS___clock_getres50: u64 = 429;
+export def SYS___nanosleep50: u64 = 430;
+export def SYS_____sigtimedwait50: u64 = 431;
+export def SYS___mq_timedsend50: u64 = 432;
+export def SYS___mq_timedreceive50: u64 = 433;
+export def SYS_compat_60__lwp_park: u64 = 434;
+export def SYS___kevent50: u64 = 435;
+export def SYS___pselect50: u64 = 436;
+export def SYS___pollts50: u64 = 437;
+export def SYS___aio_suspend50: u64 = 438;
+export def SYS___stat50: u64 = 439;
+export def SYS___fstat50: u64 = 440;
+export def SYS___lstat50: u64 = 441;
+export def SYS_____semctl50: u64 = 442;
+export def SYS___shmctl50: u64 = 443;
+export def SYS___msgctl50: u64 = 444;
+export def SYS___getrusage50: u64 = 445;
+export def SYS___timer_settime50: u64 = 446;
+export def SYS___timer_gettime50: u64 = 447;
+export def SYS___ntp_gettime50: u64 = 448;
+export def SYS___wait450: u64 = 449;
+export def SYS___mknod50: u64 = 450;
+export def SYS___fhstat50: u64 = 451;
+export def SYS_pipe2: u64 = 453;
+export def SYS_dup3: u64 = 454;
+export def SYS_kqueue1: u64 = 455;
+export def SYS_paccept: u64 = 456;
+export def SYS_linkat: u64 = 457;
+export def SYS_renameat: u64 = 458;
+export def SYS_mkfifoat: u64 = 459;
+export def SYS_mknodat: u64 = 460;
+export def SYS_mkdirat: u64 = 461;
+export def SYS_faccessat: u64 = 462;
+export def SYS_fchmodat: u64 = 463;
+export def SYS_fchownat: u64 = 464;
+export def SYS_fexecve: u64 = 465;
+export def SYS_fstatat: u64 = 466;
+export def SYS_utimensat: u64 = 467;
+export def SYS_openat: u64 = 468;
+export def SYS_readlinkat: u64 = 469;
+export def SYS_symlinkat: u64 = 470;
+export def SYS_unlinkat: u64 = 471;
+export def SYS_futimens: u64 = 472;
+export def SYS___quotactl: u64 = 473;
+export def SYS_posix_spawn: u64 = 474;
+export def SYS_recvmmsg: u64 = 475;
+export def SYS_sendmmsg: u64 = 476;
+export def SYS_clock_nanosleep: u64 = 477;
+export def SYS____lwp_park60: u64 = 478;
+export def SYS_posix_fallocate: u64 = 479;
+export def SYS_fdiscard: u64 = 480;
+export def SYS_wait6: u64 = 481;
+export def SYS_clock_getcpuclockid2: u64 = 482;
+export def SYS_MAXSYSCALL: u64 = 483;
+export def SYS_NSYSENT: u64 = 512;
diff --git a/rt/+netbsd/syscalls.ha b/rt/+netbsd/syscalls.ha
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+fn syscall0(u64) u64;
+fn syscall1(u64, u64) u64;
+fn syscall2(u64, u64, u64) u64;
+fn syscall3(u64, u64, u64, u64) u64;
+fn syscall4(u64, u64, u64, u64, u64) u64;
+fn syscall5(u64, u64, u64, u64, u64, u64) u64;
+fn syscall6(u64, u64, u64, u64, u64, u64, u64) u64;
+
+let pathbuf: [PATH_MAX]u8 = [0...];
+
+// The use of this function is discouraged, as it can create race conditions.
+// TOCTOU is preferred: attempt to simply use the resource you need and handle
+// any access errors which occur.
+export fn access(path: path, mode: int) (bool | errno) =
+ faccessat(AT_FDCWD, path, mode, 0);
+
+export fn fchdir(fd: int) (void | errno) = {
+ wrap_return(syscall1(SYS_fchdir, fd: u64))?;
+};
+
+export fn chdir(path: path) (void | errno) = {
+ let path = kpath(path)?;
+ wrap_return(syscall1(SYS_chdir, path: uintptr: u64))?;
+};
+
+export fn chroot(path: path) (void | errno) = {
+ let path = kpath(path)?;
+ wrap_return(syscall1(SYS_chroot, path: uintptr: u64))?;
+};
+
+export fn fchmod(fd: int, mode: uint) (void | errno) = {
+ wrap_return(syscall2(SYS_fchmod,
+ fd: u64, mode: u64))?;
+};
+
+export fn fchmodat(dirfd: int, path: path, mode: uint, flags: int) (void | errno) = {
+ let path = kpath(path)?;
+ wrap_return(syscall4(SYS_fchmodat,
+ dirfd: u64, path: uintptr: u64, mode: u64, flags: u64))?;
+};
+
+export fn fchown(fd: int, uid: uint, gid: uint) (void | errno) = {
+ wrap_return(syscall3(SYS_fchown,
+ fd: u64, uid: u32, gid: u32))?;
+};
+
+export fn fchownat(dirfd: int, path: path, uid: uint, gid: uint, flags: int) (void | errno) = {
+ let path = kpath(path)?;
+ wrap_return(syscall5(SYS_fchownat,
+ dirfd: u64, path: uintptr: u64, uid: u32, gid: u32, flags: u64))?;
+};
+
+export fn fstatat(fd: int, path: path, _stat: *st, flag: int) (void | errno) = {
+ let path = kpath(path)?;
+ let fbstat = stat { ... };
+ wrap_return(syscall4(SYS_fstatat, fd: u64,
+ path: uintptr: u64, &fbstat: uintptr: u64, flag: u64))?;
+ _stat.dev = fbstat.st_dev;
+ _stat.ino = fbstat.st_ino;
+ _stat.mode = fbstat.st_mode;
+ _stat.nlink = fbstat.st_nlink;
+ _stat.uid = fbstat.st_uid;
+ _stat.gid = fbstat.st_gid;
+ _stat.rdev = fbstat.st_rdev;
+ _stat.atime.tv_sec = fbstat.st_atim.tv_sec;
+ _stat.atime.tv_nsec = fbstat.st_atim.tv_nsec: i64;
+ _stat.mtime.tv_sec = fbstat.st_mtim.tv_sec;
+ _stat.mtime.tv_nsec = fbstat.st_mtim.tv_nsec: i64;
+ _stat.ctime.tv_sec = fbstat.st_ctim.tv_sec;
+ _stat.ctime.tv_nsec = fbstat.st_ctim.tv_nsec: i64;
+ _stat.btime.tv_sec = fbstat.st_birthtim.tv_sec;
+ _stat.btime.tv_nsec = fbstat.st_birthtim.tv_nsec: i64;
+ _stat.sz = fbstat.st_size;
+ _stat.blocks = fbstat.st_blocks;
+ _stat.blksz = fbstat.st_blksize;
+ _stat.flags = fbstat.st_flags;
+};
+
+export fn futimens(fd: int, ts: *[2]timespec) (void | errno) = {
+ wrap_return(syscall2(SYS_futimens,
+ fd: u64, ts: uintptr: u64))?;
+};
+
+export fn getdents(dirfd: int, buf: *opaque, nbytes: size) (size | errno) = {
+ return wrap_return(syscall3(SYS_compat_30_getdents, dirfd: u64,
+ buf: uintptr: u64, nbytes: u64))?: size;
+};
+
+// The return value is statically allocated and must be duplicated before
+// calling getcwd again.
+export fn getcwd() (*const u8 | errno) = {
+ static let pathbuf: [PATH_MAX]u8 = [0...];
+ wrap_return(syscall2(SYS___getcwd,
+ &pathbuf: *[*]u8: uintptr: u64,
+ PATH_MAX))?;
+ return &pathbuf: *const u8;
+};
+
+export fn mkdirat(dirfd: int, path: path, mode: uint) (void | errno) = {
+ let path = kpath(path)?;
+ wrap_return(syscall3(SYS_mkdirat,
+ dirfd: u64, path: uintptr: u64, mode: u64))?;
+};
+
+export fn openat(
+ dirfd: int,
+ path: path,
+ flags: int,
+ mode: uint,
+) (int | errno) = {
+ let path = kpath(path)?;
+ return wrap_return(syscall4(SYS_openat, dirfd: u64,
+ path: uintptr: u64, flags: u64, mode: u64))?: int;
+};
+
+export fn readlinkat(
+ dirfd: int,
+ path: path,
+ buf: []u8,
+) (size | errno) = {
+ let path = kpath(path)?;
+ return wrap_return(syscall4(SYS_readlinkat,
+ dirfd: u64, path: uintptr: u64,
+ buf: *[*]u8: uintptr: u64,
+ len(buf): u64))?: size;
+};
+
+export fn renameat(
+ olddirfd: int,
+ oldpath: str,
+ newdirfd: int,
+ newpath: str,
+) (void | errno) = {
+ let oldpath = kpath(oldpath)?;
+ static let newpathbuf: [PATH_MAX]u8 = [0...];
+ let newpath = copy_kpath(newpath, newpathbuf)?;
+ wrap_return(syscall4(SYS_renameat,
+ olddirfd: u64, oldpath: uintptr: u64,
+ newdirfd: u64, newpath: uintptr: u64))?;
+};
+
+export fn sysctl(name: []const u32, oldp: nullable *opaque, oldlenp: nullable *size,
+ newp: nullable *const opaque, newlen: size) (void | errno) = {
+ wrap_return(syscall6(SYS___sysctl,
+ &name[0]: uintptr: u64, len(name): u64,
+ oldp: uintptr: u64, oldlenp: uintptr: u64,
+ newp: uintptr: u64, newlen: u64))?;
+};
+
+export fn unlinkat(dirfd: int, path: path, flags: int) (void | errno) = {
+ let path = kpath(path)?;
+ wrap_return(syscall3(SYS_unlinkat,
+ dirfd: u64, path: uintptr: u64, flags: u64))?;
+};
+
+export fn utimensat(dirfd: int, path: str, ts: *[2]timespec, flags: int) (void | errno) = {
+ let path = kpath(path)?;
+ wrap_return(syscall4(SYS_utimensat,
+ dirfd: u64, path: uintptr: u64, ts: uintptr: u64, flags: u64))?;
+};
+
+export fn close(fd: int) (void | errno) = {
+ wrap_return(syscall1(SYS_close, fd: u64))?;
+};
+
+export fn lseek(fd: int, off: i64, whence: int) (i64 | errno) = {
+ return wrap_return(syscall3(SYS_lseek,
+ fd: u64, off: u64, whence: u64))?: i64;
+};
+
+export fn dup2(old: int, new: int) (int | errno) =
+ wrap_return(syscall2(SYS_dup2, old: u64, new: u64))?: int;
+
+export fn getpid() pid_t = syscall0(SYS_getpid): pid_t;
+
+export def EXIT_SUCCESS: int = 0;
+
+export fn exit(status: int) never = {
+ syscall1(SYS_exit, status: u64);
+ abort();
+};
+
+export fn fork() (pid_t | void | errno) = {
+ let n = wrap_return(syscall0(SYS_fork))?: pid_t;
+ switch (n) {
+ case 0 =>
+ return;
+ case =>
+ return n;
+ };
+};
+
+export fn execve(
+ path: *const u8,
+ argv: *[*]nullable *const u8,
+ envp: *[*]nullable *const u8,
+) int = syscall3(SYS_execve,
+ path: uintptr: u64,
+ argv: uintptr: u64,
+ envp: uintptr: u64): int;
+
+export fn wait4(
+ pid: pid_t,
+ wstatus: nullable *int,
+ options: int,
+ rusage: nullable *rusage,
+) (int | errno) = {
+ return wrap_return(syscall4(SYS_compat_50_wait4,
+ pid: u64, wstatus: uintptr: u64,
+ options: u64, rusage: uintptr: u64))?: int;
+};
+
+export fn wifexited(status: int) bool = wtermsig(status) == 0;
+export fn wexitstatus(status: int) int = (status & 0xff00) >> 8;
+
+export fn wtermsig(status: int) int = status & 0x7f;
+export fn wifsignaled(status: int) bool =
+ wtermsig(status) != 0o177 && wtermsig(status) != 0 && status != 0x13;
+
+export fn kill(pid: pid_t, signal: int) (void | errno) = {
+ wrap_return(syscall2(SYS_kill, pid: u64, signal: u64))?;
+};
+
+export fn pipe2(pipefd: *[2]int, flags: int) (void | errno) = {
+ wrap_return(syscall2(SYS_pipe2, pipefd: uintptr: u64, flags: u64))?;
+};
+
+export fn mmap(
+ addr: nullable *opaque,
+ length: size,
+ prot: uint,
+ flags: uint,
+ fd: int,
+ offs: size
+) (errno | *opaque) = {
+ return wrap_return(syscall6(SYS_mmap, addr: uintptr: u64, length: u64,
+ prot: u64, flags: u64, fd: u64, offs: u64)): uintptr: *opaque;
+};
+
+export fn munmap(addr: *opaque, length: size) (void | errno) = {
+ wrap_return(syscall2(SYS_munmap, addr: uintptr: u64, length: u64))?;
+};
+
+export type fcntl_arg = (void | int | *st_flock | *u64);
+
+export fn fcntl(fd: int, cmd: int, arg: fcntl_arg) (int | errno) = {
+ let _fd = fd: u64, _cmd = cmd: u64;
+ return wrap_return(match (arg) {
+ case void =>
+ yield syscall2(SYS_fcntl, _fd, _cmd);
+ case let i: int =>
+ yield syscall3(SYS_fcntl, _fd, _cmd, i: u64);
+ case let l: *st_flock =>
+ yield syscall3(SYS_fcntl, _fd, _cmd, l: uintptr: u64);
+ case let u: *u64 =>
+ yield syscall3(SYS_fcntl, _fd, _cmd, u: uintptr: u64);
+ })?: int;
+};
+
+export fn read(fd: int, buf: *opaque, count: size) (size | errno) = {
+ return wrap_return(syscall3(SYS_read,
+ fd: u64, buf: uintptr: u64, count: u64))?: size;
+};
+
+export fn write(fd: int, buf: *const opaque, count: size) (size | errno) = {
+ return wrap_return(syscall3(SYS_write,
+ fd: u64, buf: uintptr: u64, count: u64))?: size;
+};
+
+export fn readv(fd: int, iov: const *[*]iovec, iovcnt: int) (size | errno) = {
+ return wrap_return(syscall3(SYS_readv,
+ fd: u64, iov: uintptr: u64, iovcnt: u64))?: size;
+};
+
+export fn writev(fd: int, iov: const *[*]iovec, iovcnt: int) (size | errno) = {
+ return wrap_return(syscall3(SYS_writev,
+ fd: u64, iov: uintptr: u64, iovcnt: u64))?: size;
+};
+
+export fn flock(fd: int, op: int) (void | errno) = {
+ wrap_return(syscall2(SYS_flock,
+ fd: u64, op: u64))?;
+};
+
+export fn ftruncate(fd: int, ln: off_t) (void | errno) = {
+ wrap_return(syscall2(SYS_ftruncate, fd: u64, ln: u32))?;
+};
+
+export fn nanosleep(req: *const timespec, rem: *timespec) (void | errno) = {
+ wrap_return(syscall2(SYS_compat_50_nanosleep,
+ req: uintptr: u64, rem: uintptr: u64))?;
+};
+
+export fn clock_gettime(clock_id: int, tp: *timespec) (void | errno) = {
+ wrap_return(syscall2(SYS___clock_gettime50,
+ clock_id: u64, tp: uintptr: u64))?;
+};
+
+export fn faccessat(
+ dirfd: int,
+ path: path,
+ mode: int,
+ flags: int,
+) (bool | errno) = {
+ let path = kpath(path)?;
+ match (wrap_return(syscall4(SYS_faccessat, dirfd: u64,
+ path: uintptr: u64, mode: u64, flags: u64))) {
+ case let err: errno =>
+ switch (err) {
+ case EACCES =>
+ return false;
+ case =>
+ return err;
+ };
+ case let n: u64 =>
+ assert(n == 0);
+ return true;
+ };
+};
+
+// NUL terminates a string and stores it in a static buffer of PATH_MAX bytes in
+// length.
+fn kpath(path: path) (*const u8 | errno) = {
+ return copy_kpath(path, pathbuf);
+};
+
+fn copy_kpath(path: path, buf: []u8) (*const u8 | errno) = {
+ let path = match (path) {
+ case let c: *const u8 =>
+ return c;
+ case let s: str =>
+ let ptr = &s: *struct {
+ buf: *[*]u8,
+ length: size,
+ capacity: size,
+ };
+ yield ptr.buf[..ptr.length];
+ case let b: []u8 =>
+ yield b;
+ };
+ if (len(path) + 1 >= len(pathbuf)) {
+ return ENAMETOOLONG;
+ };
+ memcpy(buf: *[*]u8, path: *[*]u8, len(path));
+ buf[len(path)] = 0;
+ return buf: *[*]u8: *const u8;
+};
+
+export fn getgroups(gids: []gid_t) (uint | errno) = {
+ return wrap_return(syscall2(SYS_getgroups,
+ len(gids): u64, gids: *[*]gid_t: uintptr: u64))?: uint;
+};
+
+export fn setgroups(gids: []gid_t) (void | errno) = {
+ wrap_return(syscall2(SYS_setgroups,
+ len(gids): u64, gids: *[*]gid_t: uintptr: u64))?;
+};
+
+export fn getppid() pid_t = syscall0(SYS_getppid): pid_t;
+
+export fn getpgrp() pid_t = syscall0(SYS_getpgrp): pid_t;
+
+export fn getsid(pid: pid_t) (pid_t | errno) = {
+ return wrap_return(syscall1(SYS_getsid, pid))?: pid_t;
+};
+
+export fn getpriority(which: int, who: id_t) (int | errno) = {
+ return wrap_return(syscall2(SYS_getpriority,
+ which: u64, who: u64))?: int;
+};
+
+export fn setpriority(which: int, who: id_t, prio: int) (void | errno) = {
+ wrap_return(syscall3(SYS_setpriority, which: u64, who: u64, prio: u64))?;
+};
+
+export fn umask(mode: mode_t) (mode_t | errno) = {
+ return wrap_return(syscall1(SYS_umask, mode: u64))?: mode_t;
+};
+
+export fn getuid() (uid_t | errno) = {
+ return wrap_return(syscall0(SYS_getuid))?: uid_t;
+};
+
+export fn geteuid() (uid_t | errno) = {
+ return wrap_return(syscall0(SYS_geteuid))?: uid_t;
+};
+
+export fn getgid() (gid_t | errno) = {
+ return wrap_return(syscall0(SYS_getgid))?: gid_t;
+};
+
+export fn getegid() (gid_t | errno) = {
+ return wrap_return(syscall0(SYS_getegid))?: gid_t;
+};
+
+export fn setuid(uid: *uid_t) (void | errno) = {
+ wrap_return(syscall1(SYS_setuid,
+ uid: uintptr: u64))?;
+};
+
+export fn seteuid(euid: *uid_t) (void | errno) = {
+ wrap_return(syscall1(SYS_seteuid,
+ euid: uintptr: u64))?;
+};
+
+export fn setgid(gid: *gid_t) (void | errno) = {
+ wrap_return(syscall1(SYS_setgid,
+ gid: uintptr: u64))?;
+};
+
+export fn setegid(egid: *gid_t) (void | errno) = {
+ wrap_return(syscall1(SYS_setegid,
+ egid: uintptr: u64))?;
+};
+
+export fn sigaction(
+ signum: int,
+ act: *const sigact,
+ old: nullable *sigact,
+) (int | errno) = {
+ return wrap_return(syscall3(SYS_compat_13_sigaction13,
+ signum: u64, act: uintptr: u64, old: uintptr: u64))?: int;
+};
+
+export fn sigprocmask(
+ how: int,
+ set: nullable *const sigset,
+ old: nullable *sigset,
+) (int | errno) = {
+ return wrap_return(syscall3(SYS_compat_13_sigprocmask13,
+ how: u64, set: uintptr: u64, old: uintptr: u64))?: int;
+};
+
+export fn open(
+ path: path,
+ flags: int,
+ mode: uint,
+) (int | errno) = {
+ let path = kpath(path)?;
+ return wrap_return(syscall3(SYS_open,
+ path: uintptr: u64, flags: u64, mode: u64))?: int;
+};
+
+export fn fstat(fd: int, _stat: *st) (void | errno) = {
+ let fbstat = stat43 { ... };
+ wrap_return(syscall2(SYS_compat_43_fstat43, fd: u64,
+ &fbstat: uintptr: u64))?;
+ _stat.dev = fbstat.st_dev;
+ _stat.ino = fbstat.st_ino;
+ _stat.mode = fbstat.st_mode;
+ _stat.nlink = fbstat.st_nlink;
+ _stat.uid = fbstat.st_uid;
+ _stat.gid = fbstat.st_gid;
+ _stat.rdev = fbstat.st_rdev;
+ _stat.atime.tv_sec = fbstat.st_atim.tv_sec;
+ _stat.atime.tv_nsec = fbstat.st_atim.tv_nsec: i64;
+ _stat.mtime.tv_sec = fbstat.st_mtim.tv_sec;
+ _stat.mtime.tv_nsec = fbstat.st_mtim.tv_nsec: i64;
+ _stat.ctime.tv_sec = fbstat.st_ctim.tv_sec;
+ _stat.ctime.tv_nsec = fbstat.st_ctim.tv_nsec: i64;
+ _stat.btime.tv_sec = fbstat.st_birthtim.tv_sec;
+ _stat.btime.tv_nsec = fbstat.st_birthtim.tv_nsec: i64;
+ _stat.sz = fbstat.st_size;
+ _stat.blocks = fbstat.st_blocks;
+ _stat.blksz = fbstat.st_blksize;
+ _stat.flags = fbstat.st_flags;
+};
+
+export fn fexecve(fd: int, argv: *[*]nullable *const u8,
+ envp: *[*]nullable *const u8) errno = {
+ return match (wrap_return(syscall3(SYS_fexecve, fd: u64,
+ argv: uintptr: u64, envp: uintptr: u64))) {
+ case let err: errno =>
+ yield err;
+ case u64 =>
+ abort("unreachable");
+ };
+};
+
+export type ioctl_arg = (nullable *opaque | u64);
+
+export fn ioctl(fd: int, req: u64, arg: ioctl_arg) (int | errno) = {
+ let fd = fd: u64, req = req: u64;
+ return wrap_return(match (arg) {
+ case let u: u64 =>
+ yield syscall3(SYS_ioctl, fd, req, u);
+ case let v: nullable *opaque =>
+ yield syscall3(SYS_ioctl, fd, req, v: uintptr: u64);
+ })?: int;
+};
+
+export fn posix_openpt(flags: int) (int | errno) = {
+ return open("/dev/ptmx", flags, 0);
+};
+
+export fn sigaltstack(
+ ss: nullable *stack_t,
+ old_ss: nullable *stack_t,
+) (void | errno) = {
+ wrap_return(syscall2(SYS___sigaltstack14,
+ ss: uintptr: u64, old_ss: uintptr: u64))?;
+};
+
+export fn ppoll(
+ fds: *[*]pollfd,
+ nfds: nfds_t,
+ timeout: const nullable *timespec,
+ sigmask: const nullable *sigset,
+) (int | errno) = {
+ return wrap_return(syscall4(SYS___pollts50, fds: uintptr: u64,
+ nfds: u64, timeout: uintptr: u64,
+ sigmask: uintptr: u64))?: int;
+};
+
+export fn getrandom(buf: *opaque, bufln: size, flags: uint) (size | errno) = {
+ return wrap_return(syscall3(SYS_getrandom,
+ buf: uintptr: u64, bufln: u64, flags: u64))?: size;
+};
+
+export fn paccept(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32, mask: nullable *int, flags: int) (int | errno) = {
+ return wrap_return(syscall5(SYS_paccept, sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64, mask: uintptr: u64, flags: u64))?: int;
+};
+
+export fn accept4(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32, flags: int) (int | errno) = {
+ return paccept(sockfd, addr, addrlen, null, flags);
+};
+
+export fn sendmsg(fd: int, msg: *const msghdr, flags: int) (int | errno) = {
+ return wrap_return(syscall3(SYS_sendmsg,
+ fd: u64, msg: uintptr: u64, flags: u64))?: int;
+};
+
+export fn recvmsg(fd: int, msg: *const msghdr, flags: int) (int | errno) = {
+ return wrap_return(syscall3(SYS_recvmsg,
+ fd: u64, msg: uintptr: u64, flags: u64))?: int;
+};
+
+export fn shutdown(sockfd: int, how: int) (void | errno) = {
+ wrap_return(syscall2(SYS_shutdown,
+ sockfd: u64, how: u64))?;
+};
+
+export fn socket(domain: int, type_: int, protocol: int) (int | errno) = {
+ return wrap_return(syscall3(SYS___socket30,
+ domain: u64, type_: u64, protocol: u64))?: int;
+};
+
+export fn connect(sockfd: int, addr: *const sockaddr, addrlen: u32) (int | errno) = {
+ return wrap_return(syscall3(SYS_connect,
+ sockfd: u64, addr: uintptr: u64, addrlen: u64))?: int;
+};
+
+export fn bind(sockfd: int, addr: *const sockaddr, addrlen: u32) (int | errno) = {
+ return wrap_return(syscall3(SYS_bind,
+ sockfd: u64, addr: uintptr: u64, addrlen: u64))?: int;
+};
+
+export fn getsockname(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32) (int | errno) = {
+ return wrap_return(syscall3(SYS_getsockname,
+ sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64))?: int;
+};
+
+export fn send(sockfd: int, buf: *opaque, len_: size, flags: int) (size | errno) = {
+ return sendto(sockfd, buf, len_, flags, null, 0);
+};
+
+export fn sendto(sockfd: int, buf: *opaque, len_: size, flags: int,
+ dest_addr: nullable *sockaddr, addrlen: u32
+) (size | errno) = {
+ return wrap_return(syscall6(SYS_sendto,
+ sockfd: u64, buf: uintptr: u64, len_: u64, flags: u64,
+ dest_addr: uintptr: u64, addrlen: u64))?: size;
+};
+
+export fn recv(sockfd: int, buf: *opaque, len_: size, flags: int) (size | errno) = {
+ return recvfrom(sockfd, buf, len_, flags, null, null);
+};
+
+export fn setsockopt(sockfd: int, level: int, optname: int, optval: *opaque, optlen: u32) (int | errno) = {
+ return wrap_return(syscall5(SYS_setsockopt,
+ sockfd: u64, level: u64, optname: u64,
+ optval: uintptr: u64, optlen: u64))?: int;
+};
+
+export fn recvfrom(sockfd: int, buf: *opaque, len_: size, flags: int,
+ src_addr: nullable *sockaddr, addrlen: nullable *u32
+) (size | errno) = {
+ return wrap_return(syscall6(SYS_recvfrom,
+ sockfd: u64, buf: uintptr: u64, len_: u64, flags: u64,
+ src_addr: uintptr: u64, addrlen: uintptr: u64))?: size;
+};
+
+export fn listen(sockfd: int, backlog: u32) (int | errno) = {
+ return wrap_return(syscall2(SYS_listen,
+ sockfd: u64, backlog: u64))?: int;
+};
+
+export fn getpeername(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32) (int | errno) = {
+ return wrap_return(syscall3(SYS_getpeername,
+ sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64))?: int;
+};
+
+export fn socketpair(
+ domain: int,
+ type_: int,
+ protocol: int,
+ sv: *[*]int,
+) (int | errno) = {
+ return wrap_return(syscall4(SYS_socketpair, domain: u64,
+ type_: u64, protocol: u64, sv: uintptr : u64))?: int;
+};
diff --git a/rt/+netbsd/sysctl.ha b/rt/+netbsd/sysctl.ha
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+// Taken from NetBSD's sys/sysctl.h
+
+export def CTL_KERN: u32 = 1; // "high kernel": proc, limits
+export def CTL_VM: u32 = 2; // virtual memory
+export def CTL_VFS: u32 = 3; // file system, mount type is next
+export def CTL_NET: u32 = 4; // network, see socket.h
+export def CTL_DEBUG: u32 = 5; // debugging parameters
+export def CTL_HW: u32 = 6; // generic CPU/io
+export def CTL_MACHDEP: u32 = 7; // machine dependent
+export def CTL_USER: u32 = 8; // user-level
+export def CTL_DDB: u32 = 9; // in-kernel debugger
+export def CTL_PROC: u32 = 10; // per-proc attr
+export def CTL_VENDOR: u32 = 11; // vendor-specific data
+export def CTL_EMUL: u32 = 12; // emulation-specific data
+export def CTL_SECURITY: u32 = 13; // security
+
+export def KERN_OSTYPE: u32 = 1; // string: system version
+export def KERN_OSRELEASE: u32 = 2; // string: system release
+export def KERN_OSREV: u32 = 3; // int: system revision
+export def KERN_VERSION: u32 = 4; // string: compile time info
+export def KERN_MAXVNODES: u32 = 5; // int: max vnodes
+export def KERN_MAXPROC: u32 = 6; // int: max processes
+export def KERN_MAXFILES: u32 = 7; // int: max open files
+export def KERN_ARGMAX: u32 = 8; // int: max arguments to exec
+export def KERN_SECURELVL: u32 = 9; // int: system security level
+export def KERN_HOSTNAME: u32 = 10; // string: hostname
+export def KERN_HOSTID: u32 = 11; // int: host identifier
+export def KERN_CLOCKRATE: u32 = 12; // struct: struct clockinfo
+export def KERN_VNODE: u32 = 13; // struct: vnode structures
+export def KERN_PROC: u32 = 14; // struct: process entries
+export def KERN_FILE: u32 = 15; // struct: file entries
+export def KERN_PROF: u32 = 16; // node: kernel profiling info
+export def KERN_POSIX1: u32 = 17; // int: POSIX.1 version
+export def KERN_NGROUPS: u32 = 18; // int: # of supplemental group ids
+export def KERN_JOB_CONTROL: u32 = 19; // int: is job control available
+export def KERN_SAVED_IDS: u32 = 20; // int: saved set-user/group-ID
+export def KERN_OBOOTTIME: u32 = 21; // struct: time kernel was booted
+export def KERN_DOMAINNAME: u32 = 22; // string: (YP) domainname
+export def KERN_MAXPARTITIONS: u32 = 23; // int: number of partitions/disk
+export def KERN_RAWPARTITION: u32 = 24; // int: raw partition number
+export def KERN_NTPTIME: u32 = 25; // struct: extended-precision time
+export def KERN_TIMEX: u32 = 26; // struct: ntp timekeeping state
+export def KERN_AUTONICETIME: u32 = 27; // int: proc time before autonice
+export def KERN_AUTONICEVAL: u32 = 28; // int: auto nice value
+export def KERN_RTC_OFFSET: u32 = 29; // int: offset of rtc from gmt
+export def KERN_ROOT_DEVICE: u32 = 30; // string: root device
+export def KERN_MSGBUFSIZE: u32 = 31; // int: max # of chars in msg buffer
+export def KERN_FSYNC: u32 = 32; // int: file synchronization support
+export def KERN_OLDSYSVMSG: u32 = 33; // old: SysV message queue support
+export def KERN_OLDSYSVSEM: u32 = 34; // old: SysV semaphore support
+export def KERN_OLDSYSVSHM: u32 = 35; // old: SysV shared memory support
+export def KERN_OLDSHORTCORENAME: u32 = 36; // old, unimplemented
+export def KERN_SYNCHRONIZED_IO: u32 = 37; // int: POSIX synchronized I/O
+export def KERN_IOV_MAX: u32 = 38; // int: max iovec's for readv(2) etc.
+export def KERN_MBUF: u32 = 39; // node: mbuf parameters
+export def KERN_MAPPED_FILES: u32 = 40; // int: POSIX memory mapped files
+export def KERN_MEMLOCK: u32 = 41; // int: POSIX memory locking
+export def KERN_MEMLOCK_RANGE: u32 = 42; // int: POSIX memory range locking
+export def KERN_MEMORY_PROTECTION: u32 = 43; // int: POSIX memory protections
+export def KERN_LOGIN_NAME_MAX: u32 = 44; // int: max length login name + NUL
+export def KERN_DEFCORENAME: u32 = 45; // old: sort core name format
+export def KERN_LOGSIGEXIT: u32 = 46; // int: log signaled processes
+export def KERN_PROC2: u32 = 47; // struct: process entries
+export def KERN_PROC_ARGS: u32 = 48; // struct: process argv/env
+export def KERN_FSCALE: u32 = 49; // int: fixpt FSCALE
+export def KERN_CCPU: u32 = 50; // old: fixpt ccpu
+export def KERN_CP_TIME: u32 = 51; // struct: CPU time counters
+export def KERN_OLDSYSVIPC_INFO: u32 = 52; // old: number of valid kern ids
+export def KERN_MSGBUF: u32 = 53; // kernel message buffer
+export def KERN_CONSDEV: u32 = 54; // dev_t: console terminal device
+export def KERN_MAXPTYS: u32 = 55; // int: maximum number of ptys
+export def KERN_PIPE: u32 = 56; // node: pipe limits
+export def KERN_MAXPHYS: u32 = 57; // int: kernel value of MAXPHYS
+export def KERN_SBMAX: u32 = 58; // int: max socket buffer size
+export def KERN_TKSTAT: u32 = 59; // tty in/out counters
+export def KERN_MONOTONIC_CLOCK: u32 = 60; // int: POSIX monotonic clock
+export def KERN_URND: u32 = 61; // int: random integer from urandom
+export def KERN_LABELSECTOR: u32 = 62; // int: disklabel sector
+export def KERN_LABELOFFSET: u32 = 63; // int: offset of label within sector
+export def KERN_LWP: u32 = 64; // struct: lwp entries
+export def KERN_FORKFSLEEP: u32 = 65; // int: sleep length on failed fork
+export def KERN_POSIX_THREADS: u32 = 66; // int: POSIX Threads option
+export def KERN_POSIX_SEMAPHORES: u32 = 67; // int: POSIX Semaphores option
+export def KERN_POSIX_BARRIERS: u32 = 68; // int: POSIX Barriers option
+export def KERN_POSIX_TIMERS: u32 = 69; // int: POSIX Timers option
+export def KERN_POSIX_SPIN_LOCKS: u32 = 70; // int: POSIX Spin Locks option
+export def KERN_POSIX_READER_WRITER_LOCKS: u32 = 71; // int: POSIX R/W Locks option
+export def KERN_DUMP_ON_PANIC: u32 = 72; // int: dump on panic
+export def KERN_SOMAXKVA: u32 = 73; // int: max socket kernel virtual mem
+export def KERN_ROOT_PARTITION: u32 = 74; // int: root partition
+export def KERN_DRIVERS: u32 = 75; // struct: driver names and majors #s
+export def KERN_BUF: u32 = 76; // struct: buffers
+export def KERN_FILE2: u32 = 77; // struct: file entries
+export def KERN_VERIEXEC: u32 = 78; // node: verified exec
+export def KERN_CP_ID: u32 = 79; // struct: cpu id numbers
+export def KERN_HARDCLOCK_TICKS: u32 = 80; // int: number of hardclock ticks
+export def KERN_ARND: u32 = 81; // void *buf, size_t siz random
+export def KERN_SYSVIPC: u32 = 82; // node: SysV IPC parameters
+export def KERN_BOOTTIME: u32 = 83; // struct: time kernel was booted
+export def KERN_EVCNT: u32 = 84; // struct: evcnts
+export def KERN_SOFIXEDBUF: u32 = 85; // bool: fixed socket buffer sizes
+export def KERN_PROC_ALL: u32 = 0; // everything
+export def KERN_PROC_PID: u32 = 1; // by process id
+export def KERN_PROC_PGRP: u32 = 2; // by process group id
+export def KERN_PROC_SESSION: u32 = 3; // by session of pid
+export def KERN_PROC_TTY: u32 = 4; // by controlling tty
+export def KERN_PROC_UID: u32 = 5; // by effective uid
+export def KERN_PROC_RUID: u32 = 6; // by real uid
+export def KERN_PROC_GID: u32 = 7; // by effective gid
+export def KERN_PROC_RGID: u32 = 8; // by real gid
+export def KERN_PROC_TTY_NODEV: u32 = NODEV: u32; // no controlling tty
+export def KERN_PROC_TTY_REVOKE: u32 = -2; // revoked tty
+export def KERN_PROC_ARGV: u32 = 1; // argv
+export def KERN_PROC_NARGV: u32 = 2; // number of strings in above
+export def KERN_PROC_ENV: u32 = 3; // environ
+export def KERN_PROC_NENV: u32 = 4; // number of strings in above
+export def KERN_PROC_PATHNAME: u32 = 5; // path to executable
+export def KERN_PROC_CWD: u32 = 6; // current working dir
+export def KERN_SYSVIPC_INFO: u32 = 1; // struct: number of valid kern ids
+export def KERN_SYSVIPC_MSG: u32 = 2; // int: SysV message queue support
+export def KERN_SYSVIPC_SEM: u32 = 3; // int: SysV semaphore support
+export def KERN_SYSVIPC_SHM: u32 = 4; // int: SysV shared memory support
+export def KERN_SYSVIPC_SHMMAX: u32 = 5; // int: max shared memory segment size (bytes)
+export def KERN_SYSVIPC_SHMMNI: u32 = 6; // int: max number of shared memory identifiers
+export def KERN_SYSVIPC_SHMSEG: u32 = 7; // int: max shared memory segments per process
+export def KERN_SYSVIPC_SHMMAXPGS: u32 = 8; // int: max amount of shared memory (pages)
+export def KERN_SYSVIPC_SHMUSEPHYS: u32 = 9; // int: physical memory usage
+export def KERN_SYSVIPC_MSG_INFO: u32 = 4; // msginfo and msgid_ds
+export def KERN_SYSVIPC_SEM_INFO: u32 = 5; // seminfo and semid_ds
+export def KERN_SYSVIPC_SHM_INFO: u32 = 6; // shminfo and shmid_ds
+export def KERN_TKSTAT_NIN: u32 = 1; // total input character
+export def KERN_TKSTAT_NOUT: u32 = 2; // total output character
+export def KERN_TKSTAT_CANCC: u32 = 3; // canonical input character
+export def KERN_TKSTAT_RAWCC: u32 = 4; // raw input character
+export def KERN_BUF_ALL: u32 = 0; // all buffers
+export def KERN_BUFSLOP: u32 = 20;
+export def KERN_FILE_BYFILE: u32 = 1;
+export def KERN_FILE_BYPID: u32 = 2;
+export def KERN_FILESLOP: u32 = 10;
+export def KERN_EVCNT_COUNT_ANY: u32 = 0;
+export def KERN_EVCNT_COUNT_NONZERO: u32 = 1;
+
+export def HW_MACHINE: u32 = 1; // string: machine class
+export def HW_MODEL: u32 = 2; // string: specific machine model
+export def HW_NCPU: u32 = 3; // int: number of cpus
+export def HW_BYTEORDER: u32 = 4; // int: machine byte order
+export def HW_PHYSMEM: u32 = 5; // int: total memory (bytes)
+export def HW_USERMEM: u32 = 6; // int: non-kernel memory (bytes)
+export def HW_PAGESIZE: u32 = 7; // int: software page size
+export def HW_DISKNAMES: u32 = 8; // string: disk drive names
+export def HW_IOSTATS: u32 = 9; // struct: iostats[]
+export def HW_MACHINE_ARCH: u32 = 10; // string: machine architecture
+export def HW_ALIGNBYTES: u32 = 11; // int: ALIGNBYTES for the kernel
+export def HW_CNMAGIC: u32 = 12; // string: console magic sequence(s)
+export def HW_PHYSMEM64: u32 = 13; // quad: total memory (bytes)
+export def HW_USERMEM64: u32 = 14; // quad: non-kernel memory (bytes)
+export def HW_IOSTATNAMES: u32 = 15; // string: iostat names
+export def HW_NCPUONLINE: u32 = 16; // number CPUs online
+
+export def USER_CS_PATH: u32 = 1; // string: _CS_PATH
+export def USER_BC_BASE_MAX: u32 = 2; // int: BC_BASE_MAX
+export def USER_BC_DIM_MAX: u32 = 3; // int: BC_DIM_MAX
+export def USER_BC_SCALE_MAX: u32 = 4; // int: BC_SCALE_MAX
+export def USER_BC_STRING_MAX: u32 = 5; // int: BC_STRING_MAX
+export def USER_COLL_WEIGHTS_MAX: u32 = 6; // int: COLL_WEIGHTS_MAX
+export def USER_EXPR_NEST_MAX: u32 = 7; // int: EXPR_NEST_MAX
+export def USER_LINE_MAX: u32 = 8; // int: LINE_MAX
+export def USER_RE_DUP_MAX: u32 = 9; // int: RE_DUP_MAX
+export def USER_POSIX2_VERSION: u32 = 10; // int: POSIX2_VERSION
+export def USER_POSIX2_C_BIND: u32 = 11; // int: POSIX2_C_BIND
+export def USER_POSIX2_C_DEV: u32 = 12; // int: POSIX2_C_DEV
+export def USER_POSIX2_CHAR_TERM: u32 = 13; // int: POSIX2_CHAR_TERM
+export def USER_POSIX2_FORT_DEV: u32 = 14; // int: POSIX2_FORT_DEV
+export def USER_POSIX2_FORT_RUN: u32 = 15; // int: POSIX2_FORT_RUN
+export def USER_POSIX2_LOCALEDEF: u32 = 16; // int: POSIX2_LOCALEDEF
+export def USER_POSIX2_SW_DEV: u32 = 17; // int: POSIX2_SW_DEV
+export def USER_POSIX2_UPE: u32 = 18; // int: POSIX2_UPE
+export def USER_STREAM_MAX: u32 = 19; // int: POSIX2_STREAM_MAX
+export def USER_TZNAME_MAX: u32 = 20; // int: _POSIX_TZNAME_MAX
+export def USER_ATEXIT_MAX: u32 = 21; // int: {ATEXIT_MAX}
+
+export def PROC_CURPROC: u32 = ~(1 << 31);
+export def PROC_PID_CORENAME: u32 = 1;
+export def PROC_PID_LIMIT: u32 = 2;
+export def PROC_PID_STOPFORK: u32 = 3;
+export def PROC_PID_STOPEXEC: u32 = 4;
+export def PROC_PID_STOPEXIT: u32 = 5;
+export def PROC_PID_PAXFLAGS: u32 = 6;
+export def PROC_PID_LIMIT_CPU: u32 = (RLIMIT_CPU+1): u32;
+export def PROC_PID_LIMIT_FSIZE: u32 = (RLIMIT_FSIZE+1): u32;
+export def PROC_PID_LIMIT_DATA: u32 = (RLIMIT_DATA+1): u32;
+export def PROC_PID_LIMIT_STACK: u32 = (RLIMIT_STACK+1): u32;
+export def PROC_PID_LIMIT_CORE: u32 = (RLIMIT_CORE+1): u32;
+export def PROC_PID_LIMIT_RSS: u32 = (RLIMIT_RSS+1): u32;
+export def PROC_PID_LIMIT_MEMLOCK: u32 = (RLIMIT_MEMLOCK+1): u32;
+export def PROC_PID_LIMIT_NPROC: u32 = (RLIMIT_NPROC+1): u32;
+export def PROC_PID_LIMIT_NOFILE: u32 = (RLIMIT_NOFILE+1): u32;
+export def PROC_PID_LIMIT_SBSIZE: u32 = (RLIMIT_SBSIZE+1): u32;
+export def PROC_PID_LIMIT_AS: u32 = (RLIMIT_AS+1): u32;
+export def PROC_PID_LIMIT_NTHR: u32 = (RLIMIT_NTHR+1): u32;
+export def PROC_PID_LIMIT_TYPE_SOFT: u32 = 1;
+export def PROC_PID_LIMIT_TYPE_HARD: u32 = 2;
+
+export def EMUL_LINUX: u32 = 1;
+export def EMUL_LINUX32: u32 = 5;
diff --git a/rt/+netbsd/types.ha b/rt/+netbsd/types.ha
@@ -0,0 +1,518 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+export type time_t = i64;
+export type suseconds_t = i64;
+export type dev_t = u64;
+export type ino_t = u64;
+export type nlink_t = u64;
+export type id_t = uint;
+export type pid_t = u64;
+export type uid_t = u32;
+export type gid_t = u32;
+export type off_t = i64;
+export type blkcnt_t = i64;
+export type blksize_t = i32;
+export type fflags_t = u32;
+export type mode_t = u32;
+export type nfds_t = uint;
+export type rlim_t = u64;
+
+export type path = (str | []u8 | *const u8);
+
+// Maximum length of a file path including the NUL terminator.
+export def PATH_MAX = 1024z;
+
+export def NGROUPS_MAX: size = 1023;
+export def NSIG: int = 32;
+
+export type sigset = struct {
+ __bits: [4]u32,
+};
+
+export def SA_ONSTACK: u64 = 0x0001;
+export def SA_RESTART: u64 = 0x0002;
+export def SA_RESETHAND: u64 = 0x0004;
+export def SA_NOCLDSTOP: u64 = 0x0008;
+export def SA_NODEFER: u64 = 0x0010;
+export def SA_NOCLDWAIT: u64 = 0x0020;
+export def SA_SIGINFO: u64 = 0x0040;
+
+export def SIG_ERR: uintptr = -1;
+export def SIG_DFL: uintptr = 0;
+export def SIG_IGN: uintptr = 1;
+export def SIG_CATCH: uintptr = 2;
+export def SIG_HOLD: uintptr = 3;
+
+export type sigact = struct {
+ union {
+ sa_handler: *fn (int) void,
+ sa_sigaction: *fn (int, *siginfo, *opaque) void,
+ },
+ sa_flags: int,
+ sa_mask: sigset,
+};
+
+export def SIG_BLOCK: int = 1;
+export def SIG_UNBLOCK: int = 2;
+export def SIG_SETMASK: int = 3;
+
+export type sigval = union {
+ sival_t: int,
+ sival_ptr: *opaque,
+};
+
+export type stack_t = struct {
+ ss_sp: *opaque,
+ ss_size: size,
+ ss_flags: int,
+};
+
+export type pollfd = struct {
+ fd: int,
+ events: i16,
+ revents: i16,
+};
+
+export type timespec = struct {
+ tv_sec: time_t,
+ tv_nsec: i64,
+};
+
+export def UTIME_OMIT = -0x2;
+
+export type timeval = struct {
+ tv_sec: time_t,
+ tv_usec: suseconds_t,
+};
+
+export type st_flock = struct {
+ l_start: off_t,
+ l_len: off_t,
+ l_pid: pid_t,
+ l_type: i16,
+ l_whence: i16,
+ l_sysid: int,
+};
+
+export type st = struct {
+ dev: dev_t,
+ ino: ino_t,
+ nlink: nlink_t,
+ mode: mode_t,
+ uid: uid_t,
+ gid: gid_t,
+ rdev: dev_t,
+ atime: timespec,
+ mtime: timespec,
+ ctime: timespec,
+ btime: timespec,
+ sz: off_t,
+ blocks: blkcnt_t,
+ blksz: blksize_t,
+ flags: fflags_t,
+};
+
+export type stat = struct {
+ st_dev: u64,
+ st_mode: mode_t,
+ __pad0: u32,
+ st_ino: u64,
+ st_nlink: u32,
+ st_uid: uid_t,
+ st_gid: gid_t,
+ __pad1: u32,
+ st_rdev: u64,
+ st_atim: timespec,
+ st_mtim: timespec,
+ st_ctim: timespec,
+ st_birthtim: timespec,
+ st_size: off_t,
+ st_blocks: blkcnt_t,
+ st_blksize: blksize_t,
+ st_flags: fflags_t,
+ st_gen: u32,
+ st_spare: [2]u32,
+};
+
+export type stat43 = struct {
+ st_dev: u32,
+ st_ino: u32,
+ st_mode: mode_t,
+ st_nlink: u16,
+ st_uid: uid_t,
+ st_gid: gid_t,
+ st_rdev: u32,
+ st_atim: timespec,
+ st_mtim: timespec,
+ st_ctim: timespec,
+ st_size: off_t,
+ st_blocks: blkcnt_t,
+ st_blksize: blksize_t,
+ st_flags: fflags_t,
+ st_gen: u32,
+ st_lspare: u32,
+ st_birthtim: timespec,
+};
+
+export type dirent = struct {
+ d_fileno: u32,
+ d_reclen: u16,
+ d_type: u8,
+ d_namlen: u8,
+ d_name: [*]u8,
+};
+
+export type iovec = struct {
+ iov_base: *opaque,
+ iov_len: size
+};
+
+export type winsize = struct {
+ ws_row: u16,
+ ws_col: u16,
+ ws_xpixel: u16,
+ ws_ypixel: u16,
+};
+
+export type termios = struct {
+ c_iflag: tcflag,
+ c_oflag: tcflag,
+ c_cflag: tcflag,
+ c_lflag: tcflag,
+ c_cc: [NCCS]cc,
+};
+
+export def NCCS: size = 20;
+
+export type tcflag = enum uint {
+ // c_iflag bits
+ IGNBRK = 0x00000001,
+ BRKINT = 0x00000002,
+ IGNPAR = 0x00000004,
+ PARMRK = 0x00000008,
+ INPCK = 0x00000010,
+ ISTRIP = 0x00000020,
+ INLCR = 0x00000040,
+ IGNCR = 0x00000080,
+ ICRNL = 0x00000100,
+ IXON = 0x00000200,
+ IXOFF = 0x00000400,
+ IXANY = 0x00000800,
+ IMAXBEL = 0x00002000,
+
+ // c_oflag bits
+ OPOST = 0x00000001,
+ ONLCR = 0x00000002,
+ TABDLY = 0x00000004,
+ TAB0 = 0x00000000,
+ TAB3 = 0x00000004,
+ ONOEOT = 0x00000008,
+ OCRNL = 0x00000010,
+ ONOCR = 0x00000020,
+ ONLRET = 0x00000040,
+
+ // c_cflag bits
+ CIGNORE = 0x00000001,
+ CSIZE = 0x00000300,
+ CS5 = 0x00000000,
+ CS6 = 0x00000100,
+ CS7 = 0x00000200,
+ CS8 = 0x00000300,
+ CSTOPB = 0x00000400,
+ CREAD = 0x00000800,
+ PARENB = 0x00001000,
+ PARODD = 0x00002000,
+ HUPCL = 0x00004000,
+ CLOCAL = 0x00008000,
+ CCTS_OFLOW = 0x00010000,
+ CRTS_IFLOW = 0x00020000,
+ CRTSCTS = (CCTS_OFLOW | CRTS_IFLOW),
+ CDTR_IFLOW = 0x00040000,
+ CDSR_OFLOW = 0x00080000,
+ CCAR_OFLOW = 0x00100000,
+ CNO_RTSDTR = 0x00200000,
+
+ // c_lflag bits
+ ECHOKE = 0x00000001,
+ ECHOE = 0x00000002,
+ ECHOK = 0x00000004,
+ ECHO = 0x00000008,
+ ECHONL = 0x00000010,
+ ECHOPRT = 0x00000020,
+ ECHOCTL = 0x00000040,
+ ISIG = 0x00000080,
+ ICANON = 0x00000100,
+ ALTWERASE = 0x00000200,
+ IEXTEN = 0x00000400,
+ EXTPROC = 0x00000800,
+ TOSTOP = 0x00400000,
+ FLUSHO = 0x00800000,
+ NOKERNINFO = 0x02000000,
+ PENDIN = 0x20000000,
+ NOFLSH = 0x80000000,
+};
+
+export type cc = enum u8 {
+ VEOF = 0,
+ VEOL = 1,
+ VEOL2 = 2,
+ VERASE = 3,
+ VWERASE = 4,
+ VKILL = 5,
+ VREPRINT = 6,
+ VERASE2 = 7,
+ VINTR = 8,
+ VQUIT = 9,
+ VSUSP = 10,
+ VDSUSP = 11,
+ VSTART = 12,
+ VSTOP = 13,
+ VLNEXT = 14,
+ VDISCARD = 15,
+ VMIN = 16,
+ VTIME = 17,
+ VSTATUS = 18,
+};
+
+export def TIOCGWINSZ: u64 = 0x40087468;
+export def TIOCSWINSZ: u64 = 0x80087467;
+export def TIOCGETA: u64 = 0x402c7413;
+export def TIOCSETA: u64 = 0x802c7414;
+export def TIOCPTSNAME: u64 = 0x48087448;
+export def TIOCSPGRP: u64 = 0x80047476;
+export def FIODGNAME: u64 = 0x80106678;
+
+export type ptmget = struct {
+ cfd: int,
+ sfd: int,
+ cn: [PATH_MAX]u8,
+ sn: [PATH_MAX]u8,
+};
+
+export type rusage = struct {
+ ru_utime: timeval,
+ ru_stime: timeval,
+ ru_maxrss: i64,
+ ru_ixrss: i64,
+ ru_idrss: i64,
+ ru_isrss: i64,
+ ru_minflt: i64,
+ ru_majflt: i64,
+ ru_nswap: i64,
+ ru_inblock: i64,
+ ru_oublock: i64,
+ ru_msgsnd: i64,
+ ru_msgrcv: i64,
+ ru_nsignals: i64,
+ ru_nvcsw: i64,
+ ru_nivcsw: i64,
+};
+
+export def DT_UNKNOWN: u8 = 0;
+export def DT_FIFO: u8 = 1;
+export def DT_CHR: u8 = 2;
+export def DT_DIR: u8 = 4;
+export def DT_BLK: u8 = 6;
+export def DT_REG: u8 = 8;
+export def DT_LNK: u8 = 10;
+export def DT_SOCK: u8 = 12;
+export def DT_WHT: u8 = 14;
+
+export def O_RDONLY: int = 0x0000;
+export def O_WRONLY: int = 0x0001;
+export def O_RDWR: int = 0x0002;
+export def O_ACCMODE: int = 0x0003;
+export def O_NONBLOCK: int = 0x0004;
+export def O_APPEND: int = 0x0008;
+export def O_SHLOCK: int = 0x0010;
+export def O_EXLOCK: int = 0x0020;
+export def O_ASYNC: int = 0x0040;
+export def O_FSYNC: int = 0x0080;
+export def O_SYNC: int = 0x0080;
+export def O_NOFOLLOW: int = 0x0100;
+export def O_CREAT: int = 0x0200;
+export def O_TRUNC: int = 0x0400;
+export def O_EXCL: int = 0x0800;
+export def O_NOCTTY: int = 0x8000;
+export def O_DIRECT: int = 0x00010000;
+export def O_DIRECTORY: int = 0x00020000;
+export def O_EXEC: int = 0x00040000;
+export def O_TTY_INIT: int = 0x00080000;
+export def O_CLOEXEC: int = 0x00400000;
+export def O_DSYNC: int = 0x01000000;
+
+export def AT_FDCWD: int = -100;
+export def AT_EACCESS: int = 0x0100;
+export def AT_SYMLINK_NOFOLLOW: int = 0x0200;
+export def AT_SYMLINK_FOLLOW: int = 0x0400;
+export def AT_REMOVEDIR: int = 0x0800;
+export def AT_RESOLVE_BENEATH: int = 0x2000;
+
+export def S_IFIFO: mode_t = 0o010000;
+export def S_IFCHR: mode_t = 0o020000;
+export def S_IFDIR: mode_t = 0o040000;
+export def S_IFBLK: mode_t = 0o060000;
+export def S_IFREG: mode_t = 0o100000;
+export def S_IFLNK: mode_t = 0o120000;
+export def S_IFSOCK: mode_t = 0o140000;
+
+export def MAP_SHARED: uint = 0x0001;
+export def MAP_PRIVATE: uint = 0x0002;
+export def MAP_FIXED: uint = 0x0010;
+export def MAP_HASSEMAPHORE: uint = 0x0200;
+export def MAP_STACK: uint = 0x0400;
+export def MAP_NOSYNC: uint = 0x0800;
+export def MAP_FILE: uint = 0x0000;
+export def MAP_ANON: uint = 0x1000;
+export def MAP_GUARD: uint = 0x00002000;
+export def MAP_EXCL: uint = 0x00004000;
+export def MAP_NOCORE: uint = 0x00020000;
+export def MAP_PREFAULT_READ: uint = 0x00040000;
+export def MAP_32BIT: uint = 0x00080000;
+
+export def PROT_NONE: uint = 0x00;
+export def PROT_READ: uint = 0x01;
+export def PROT_WRITE: uint = 0x02;
+export def PROT_EXEC: uint = 0x04;
+
+export def SIGHUP: int = 1;
+export def SIGINT: int = 2;
+export def SIGQUIT: int = 3;
+export def SIGILL: int = 4;
+export def SIGTRAP: int = 5;
+export def SIGABRT: int = 6;
+export def SIGIOT: int = SIGABRT;
+export def SIGEMT: int = 7;
+export def SIGFPE: int = 8;
+export def SIGKILL: int = 9;
+export def SIGBUS: int = 10;
+export def SIGSEGV: int = 11;
+export def SIGSYS: int = 12;
+export def SIGPIPE: int = 13;
+export def SIGALRM: int = 14;
+export def SIGTERM: int = 15;
+export def SIGURG: int = 16;
+export def SIGSTOP: int = 17;
+export def SIGTSTP: int = 18;
+export def SIGCONT: int = 19;
+export def SIGCHLD: int = 20;
+export def SIGTTIN: int = 21;
+export def SIGTTOU: int = 22;
+export def SIGIO: int = 23;
+export def SIGXCPU: int = 24;
+export def SIGXFSZ: int = 25;
+export def SIGVTALRM: int = 26;
+export def SIGPROF: int = 27;
+export def SIGWINCH: int = 28;
+export def SIGINFO: int = 29;
+export def SIGUSR1: int = 30;
+export def SIGUSR2: int = 31;
+export def SIGTHR: int = 32;
+export def SIGLWP: int = SIGTHR;
+export def SIGLIBRT: int = 33;
+
+export def F_DUPFD: int = 0;
+export def F_GETFD: int = 1;
+export def F_SETFD: int = 2;
+export def F_GETFL: int = 3;
+export def F_SETFL: int = 4;
+export def F_GETOWN: int = 5;
+export def F_SETOWN: int = 6;
+export def F_OGETLK: int = 7;
+export def F_OSETLK: int = 8;
+export def F_OSETLKW: int = 9;
+export def F_DUP2FD: int = 10;
+export def F_GETLK: int = 11;
+export def F_SETLK: int = 12;
+export def F_SETLKW: int = 13;
+export def F_SETLK_REMOTE: int = 14;
+export def F_READAHEAD: int = 15;
+export def F_RDAHEAD: int = 16;
+export def F_DUPFD_CLOEXEC: int = 12;
+export def F_DUP2FD_CLOEXEC: int = 18;
+export def F_ADD_SEALS: int = 16;
+export def F_GET_SEALS: int = 17;
+export def F_ISUNIONSTACK: int = 21;
+
+export def F_SEAL_SEAL: int = 0x0001;
+export def F_SEAL_SHRINK: int = 0x0002;
+export def F_SEAL_GROW: int = 0x0004;
+export def F_SEAL_WRITE: int = 0x0008;
+
+export def FD_CLOEXEC: int = 1;
+export def F_UNLCKSYS: int = 4;
+export def F_CANCEL: int = 5;
+
+export def F_RDLCK: i16 = 1;
+export def F_UNLCK: i16 = 2;
+export def F_WRLCK: i16 = 3;
+
+export def PRIO_PROCESS: int = 0;
+export def PRIO_PGRP: int = 1;
+export def PRIO_USER: int = 2;
+
+export def F_OK: int = 0;
+export def X_OK: int = 0x01;
+export def W_OK: int = 0x02;
+export def R_OK: int = 0x04;
+
+export def CLOCK_REALTIME: int = 0;
+export def CLOCK_VIRTUAL: int = 1;
+export def CLOCK_PROF: int = 2;
+export def CLOCK_MONOTONIC: int = 3;
+export def CLOCK_THREAD_CPUTIME_ID: int = 0x20000000;
+export def CLOCK_PROCESS_CPUTIME_ID: int = 0x40000000;
+
+export def WNOHANG: int = 1;
+export def WUNTRACED: int = 2;
+export def WSTOPPED: int = WUNTRACED;
+export def WCONTINUED: int = 4;
+export def WNOWAIT: int = 8;
+export def WEXITED: int = 16;
+export def WTRAPPED: int = 32;
+
+export def STDIN_FILENO: int = 0;
+export def STDOUT_FILENO: int = 1;
+export def STDERR_FILENO: int = 2;
+
+export def SEEK_SET: int = 0;
+export def SEEK_CUR: int = 1;
+export def SEEK_END: int = 2;
+
+// Flock operations
+export def LOCK_SH: int = 1;
+export def LOCK_EX: int = 2;
+export def LOCK_NB: int = 4;
+export def LOCK_UN: int = 8;
+
+export type rlimit = struct {
+ rlim_cur: rlim_t,
+ rlim_max: rlim_t,
+};
+
+export def RLIM_INFINITY: rlim_t = -1;
+
+export def RLIMIT_CPU: int = 0;
+export def RLIMIT_FSIZE: int = 1;
+export def RLIMIT_DATA: int = 2;
+export def RLIMIT_STACK: int = 3;
+export def RLIMIT_CORE: int = 4;
+export def RLIMIT_RSS: int = 5;
+export def RLIMIT_MEMLOCK: int = 6;
+export def RLIMIT_NPROC: int = 7;
+export def RLIMIT_NOFILE: int = 8;
+export def RLIMIT_SBSIZE: int = 9;
+export def RLIMIT_VMEM: int = 10;
+export def RLIMIT_AS: int = RLIMIT_VMEM;
+export def RLIMIT_NPTS: int = 11;
+export def RLIMIT_SWAP: int = 12;
+export def RLIMIT_KQUEUES: int = 13;
+export def RLIMIT_UMTXP: int = 14;
+export def RLIMIT_NTHR: int = 11; // number of threads
+
+export def SHUT_RD: int = 0;
+export def SHUT_WR: int = 1;
+export def SHUT_RDWR: int = 2;
+
+export def NODEV: int = -1; // non-existent device
diff --git a/scripts/genbootstrap b/scripts/genbootstrap
@@ -3,7 +3,7 @@
cd "$(dirname "$0")/.."
mkdir -p "${BINOUT:-.bin}" makefiles/
hare build -o "${BINOUT:-.bin}"/genbootstrap cmd/genbootstrap
-for platform in linux freebsd openbsd; do
+for platform in linux freebsd openbsd netbsd; do
platformtags=
if [ $platform = "openbsd" ]; then
platformtags=libc
diff --git a/temp/+netbsd.ha b/temp/+netbsd.ha
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use crypto::random;
+use encoding::hex;
+use errors;
+use fmt;
+use fs;
+use io;
+use memio;
+use os;
+use path;
+
+fn get_tmpdir() str = os::tryenv("TMPDIR", "/tmp");
+
+// Creates an unnamed temporary file. The file may or may not have a name; not
+// all systems support the creation of temporary inodes which are not linked to
+// 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]].
+export fn file(iomode: io::mode, mode: fs::mode) (io::file | fs::error) = {
+ // TODO: Add a custom "close" function which removes the named file
+ let file = named(os::cwd, get_tmpdir(), iomode, mode)?;
+ free(file.1);
+ return file.0;
+};
+
+// Creates a named temporary file in the given directory of the given
+// filesystem. The caller is responsible for closing and removing the file when
+// they're done with it. The name is statically allocated, and will be
+// overwritten on subsequent calls.
+//
+// The I/O mode must be either [[io::mode::WRITE]] or [[io::mode::RDWR]].
+export fn named(
+ fs: *fs::fs,
+ path: str,
+ iomode: io::mode,
+ mode: fs::mode,
+) ((io::file, str) | fs::error) = {
+ assert(iomode == io::mode::WRITE || iomode == io::mode::RDWR);
+
+ let oflags = fs::flag::EXCL;
+ if (iomode == io::mode::RDWR) {
+ oflags |= fs::flag::RDWR;
+ } else {
+ oflags |= fs::flag::WRONLY;
+ };
+
+ static let pathbuf = path::buffer { ... };
+ static let namebuf: [32]u8 = [0...];
+ for (true) {
+ let rand: [size(u64)]u8 = [0...];
+ random::buffer(rand);
+
+ const id = *(&rand[0]: *u64);
+ const name = fmt::bsprintf(namebuf, "temp.{}", id);
+ const path = path::set(&pathbuf, path, name)!;
+
+ match (fs::create_file(fs, path, mode, oflags)) {
+ case errors::exists =>
+ continue;
+ case let err: fs::error =>
+ return err;
+ case let f: io::file =>
+ return (f, path);
+ };
+ };
+};
+
+// 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]].
+//
+// The return value is statically allocated and will be overwritten on
+// subsequent calls.
+export fn dir() str = {
+ let buf: [8]u8 = [0...], name: [16]u8 = [0...];
+ random::buffer(buf[..]);
+
+ let sink = memio::fixed(name);
+ const enc = hex::newencoder(&sink);
+ io::write(&enc, buf)!;
+ let name = memio::string(&sink)!;
+
+ static let buf = path::buffer { ... };
+ path::set(&buf, get_tmpdir(), name)!;
+ const path = path::string(&buf);
+ match (os::mkdir(path,0o755)) {
+ case let err: fs::error => abort("Could not create temp directory");
+ case void => void;
+ };
+ return path;
+};
diff --git a/time/+netbsd/functions.ha b/time/+netbsd/functions.ha
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use rt;
+
+// Converts a [[duration]] to an [[rt::timespec]]. This function is
+// non-portable.
+export fn duration_to_timespec(n: duration) rt::timespec = rt::timespec {
+ tv_sec = n / SECOND,
+ tv_nsec = n % SECOND,
+};
+
+// Converts a [[duration]] to an [[rt::timeval]]. This function is
+// non-portable.
+export fn duration_to_timeval(d: duration) rt::timeval = rt::timeval {
+ tv_sec = d / SECOND,
+ tv_usec = d % SECOND / 1000,
+};
+
+// Converts an [[instant]] to an [[rt::timespec]]. This function is
+// non-portable.
+export fn instant_to_timespec(t: instant) rt::timespec = rt::timespec {
+ tv_sec = t.sec,
+ tv_nsec = t.nsec,
+};
+
+// Converts a [[rt::timespec]] to an [[instant]]. This function is
+// non-portable.
+export fn timespec_to_instant(ts: rt::timespec) instant = instant {
+ sec = ts.tv_sec,
+ nsec = ts.tv_nsec,
+};
+
+// Yields the process to the kernel and returns after the requested duration.
+export fn sleep(d: duration) void = {
+ let req = duration_to_timespec(d);
+
+ for (true) {
+ let res = rt::timespec { ... };
+ match (rt::nanosleep(&req, &res)) {
+ case void =>
+ return;
+ case let err: rt::errno =>
+ switch (err) {
+ case rt::EINTR =>
+ req = res;
+ case =>
+ abort("Unexpected error from nanosleep");
+ };
+ };
+ };
+};
+
+// An enumeration of clocks available on this system. Different clocks represent
+// times from different epochs, and have different characteristics with regards
+// to leap seconds, NTP adjustments, and so on. All systems provide the REALTIME
+// and MONOTONIC clocks at least; use of other clocks is not guaranteed to be
+// portable.
+export type clock = enum {
+ // The current wall-clock time. This may jump forwards or backwards in
+ // time to account for leap seconds, NTP adjustments, etc.
+ REALTIME = rt::CLOCK_REALTIME,
+
+ // The current monotonic time. This clock measures from some undefined
+ // epoch and is not affected by leap seconds, NTP adjustments, and
+ // changes to the system time: it always increases by one second per
+ // second.
+ MONOTONIC = rt::CLOCK_MONOTONIC,
+
+ // Increments only when the CPU is running in user mode on behalf of the
+ // calling process.
+ VIRTUAL = rt::CLOCK_VIRTUAL,
+
+ // Increments when the CPU is running in user or kernel mode.
+ PROF = rt::CLOCK_PROF,
+
+ // The thread CPU clock. It begins at zero and is advanced while the
+ // calling thread is running in user or kernel mode.
+ THREAD_CPUTIME_ID = rt::CLOCK_THREAD_CPUTIME_ID,
+
+ // The process CPU clock. It begins at zero and is advanced while the
+ // calling process is running in user or kernel mode.
+ PROCESS_CPUTIME_ID = rt::CLOCK_PROCESS_CPUTIME_ID,
+};
+
+// Returns the current time for a given clock.
+export fn now(clock: clock) instant = {
+ let tp = rt::timespec { ... };
+ match (rt::clock_gettime(clock, &tp)) {
+ case void =>
+ return timespec_to_instant(tp);
+ case let err: rt::errno =>
+ abort("Unexpected error from clock_gettime");
+ };
+};
diff --git a/time/chrono/+netbsd.ha b/time/chrono/+netbsd.ha
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+def LOCALTIME_PATH: str = "/etc/localtime";
+def TZDB_PATH: str = "/usr/share/zoneinfo/";
+
+// The filepath of the system's "leap-seconds.list" file, which contains UTC/TAI
+// leap second data.
+export def UTC_LEAPSECS_PATH: str = "/usr/share/zoneinfo/leap-seconds.list";
diff --git a/unix/+netbsd/creds.ha b/unix/+netbsd/creds.ha
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+// Unix credentials types & functions; ref credentials(7)
+
+use errors;
+use rt;
+
+// Process ID.
+export type pid = rt::pid_t;
+
+// User ID.
+export type uid = rt::uid_t;
+
+// Group ID.
+export type gid = rt::gid_t;
+
+// Returns the current process user ID.
+export fn getuid() uid = rt::getuid() as rt::uid_t: uid;
+
+// Returns the current process effective user ID.
+export fn geteuid() uid = rt::geteuid() as rt::uid_t: uid;
+
+// Returns the current process group ID.
+export fn getgid() gid = rt::getgid() as rt::gid_t: gid;
+
+// Returns the current process effective group ID.
+export fn getegid() gid = rt::getegid() as rt::gid_t: gid;
+
+// Sets the caller's user ID to the specified value. This generally requires
+// elevated permissions from the calling process.
+//
+// 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, and take extreme care to handle errors correctly.
+export fn setuid(uid: uid) void = rt::setuid(&uid)!;
+
+// Sets the caller's effective user ID to the specified value. This generally
+// requires elevated permissions from the calling process.
+//
+// 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, and take extreme care to handle errors correctly.
+export fn seteuid(euid: uid) void = rt::seteuid(&euid)!;
+
+// Sets the caller's group ID to the specified value. This generally requires
+// elevated permissions from the calling process.
+//
+// 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, and take extreme care to handle errors correctly.
+export fn setgid(gid: gid) void = rt::setgid(&gid)!;
+
+// Sets the caller's effective group ID to the specified value. This generally
+// requires elevated permissions from the calling process.
+//
+// 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, and take extreme care to handle errors correctly.
+export fn setegid(egid: gid) void = rt::setegid(&egid)!;
+
+// Returns a list of supplementary group IDs for the current process. The
+// returned slice is statically allocated.
+export fn getgroups() []gid = {
+ static let gids: [rt::NGROUPS_MAX]rt::gid_t = [0...];
+ const n = rt::getgroups(gids)!;
+ return gids[..n]: []gid;
+};
+
+// Sets the list of supplementary group IDs which apply to the current process.
+// This generally requires elevated permissions.
+//
+// If the system returns an error, this function will abort the program. Failing
+// to handle errors from setgroups 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, and take extreme care to handle errors correctly.
+export fn setgroups(gids: []gid) void = rt::setgroups(gids: []rt::gid_t)!;
+
+// Returns the current process ID.
+export fn getpid() pid = rt::getpid();
+
+// Returns the parent process ID.
+export fn getppid() pid = rt::getppid();
+
+// Returns the current process group ID.
+export fn getpgrp() pid = rt::getpgrp();
+
+// Returns the current process's session ID.
+export fn getsid() pid = rt::getsid(0)!;
+
+// Returns the session ID associated with the given process.
+export fn getpsid(pid: pid) (pid | errors::noentry | errors::noaccess) = {
+ match (rt::getsid(pid)) {
+ case let pid: rt::pid_t =>
+ return pid;
+ case let err: rt::errno =>
+ assert(err == rt::ESRCH);
+ return errors::noentry;
+ };
+};
diff --git a/unix/+netbsd/nice.ha b/unix/+netbsd/nice.ha
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use rt;
+
+// Adds the argument to the niceness of the current process. The input should be
+// between -20 and 19 (inclusive); lower numbers represent a higher priority.
+// Generally, you must have elevated permissions to reduce your niceness, but
+// not to increase it.
+export fn nice(inc: int) (void | errors::error) = {
+ let prio = inc;
+ if (inc > -40 && inc <= 40) {
+ prio += rt::getpriority(rt::PRIO_PROCESS, 0) as int;
+ };
+ if (prio > 19) {
+ prio = 19;
+ };
+ if (prio < -20) {
+ prio = -20;
+ };
+ match (rt::setpriority(rt::PRIO_PROCESS, 0, prio)) {
+ case void => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
diff --git a/unix/+netbsd/pipe.ha b/unix/+netbsd/pipe.ha
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use rt;
+
+// Flags to use for the [[io::file]]s returned by [[pipe]].
+// Only NOCLOEXEC and NONBLOCK are guaranteed to be available.
+export type pipe_flag = enum {
+ NOCLOEXEC = rt::O_CLOEXEC,
+ NONBLOCK = rt::O_NONBLOCK,
+};
+
+// Create a pair of two linked [[io::file]]s, such that any data written to the
+// second [[io::file]] may be read from the first.
+export fn pipe(flags: pipe_flag = 0) ((io::file, io::file) | errors::error) = {
+ let fds: [2]int = [0...];
+ flags ^= pipe_flag::NOCLOEXEC; // invert CLOEXEC
+ match (rt::pipe2(&fds, flags)) {
+ case void => void;
+ case let e: rt::errno =>
+ return errors::errno(e);
+ };
+ return (fds[0], fds[1]);
+};
diff --git a/unix/+netbsd/umask.ha b/unix/+netbsd/umask.ha
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use fs;
+use rt;
+
+// Sets the file mode creation mask for the current process and return the
+// previous value of the mask.
+export fn umask(mode: fs::mode) fs::mode = rt::umask(mode: rt::mode_t)!: fs::mode;
diff --git a/unix/hosts/+netbsd.ha b/unix/hosts/+netbsd.ha
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+def PATH: str = "/etc/hosts";
diff --git a/unix/poll/+netbsd.ha b/unix/poll/+netbsd.ha
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use rt;
+use time;
+
+// Events bitfield for the events and revents field of [[pollfd]].
+export type event = enum i16 {
+ POLLIN = 1,
+ POLLPRI = 2,
+ POLLOUT = 4,
+ POLLERR = 8,
+ POLLHUP = 16,
+};
+
+// A single file descriptor to be polled.
+export type pollfd = struct {
+ fd: io::file,
+ events: i16,
+ revents: i16,
+};
+
+// Pass this [[time::duration]] to [[poll]] to cause it wait indefinitely for
+// the next event.
+export def INDEF: time::duration = -1;
+
+// Pass this [[time::duration]] to [[poll]] to cause it to return immediately if
+// no events are available.
+export def NONBLOCK: time::duration = 0;
+
+// Polls for the desired events on a slice of [[pollfd]]s, blocking until an
+// event is available, or the timeout expires. Set the timeout to [[INDEF]] to
+// block forever, or [[NONBLOCK]] to return immediately if no events are
+// available. Returns the number of [[pollfd]] items which have events, i.e.
+// those which have revents set to a nonzero value.
+export fn poll(
+ fds: []pollfd,
+ timeout: time::duration,
+) (uint | error) = {
+ let ts = time::duration_to_timespec(timeout);
+ let ts = if (timeout == INDEF) null else &ts;
+ match (rt::ppoll(fds: *[*]pollfd: *[*]rt::pollfd, len(fds): rt::nfds_t, ts, null)) {
+ case let err: rt::errno =>
+ return errors::errno(err);
+ case let n: int =>
+ return n: uint;
+ };
+};
diff --git a/unix/resolvconf/+netbsd.ha b/unix/resolvconf/+netbsd.ha
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+def PATH: str = "/etc/resolv.conf";
diff --git a/unix/signal/+netbsd.ha b/unix/signal/+netbsd.ha
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use rt;
+use time;
+use unix;
+
+// Requests that [[sig::ALRM]] is delivered to the calling process in (about)
+// "sec" seconds. Returns the number of seconds until the previously scheduled
+// alarm, or zero if none was scheduled.
+export fn alarm(sec: uint) uint = {
+ return rt::alarm(sec);
+};
+
+// Configures a new signal handler, returning the old details (which can be
+// passed to [[restore]] to restore its behavior).
+//
+// The variadic parameters specify either [[flag]]s to enable or a signal mask
+// to use via [[sigset]]; if the latter is provided no more than one may be
+// used.
+export fn handle(
+ signum: sig,
+ handler: *handler,
+ opt: (flag | sigset)...
+) sigaction = {
+ let sa_mask = newsigset();
+
+ let sa_flags = rt::SA_SIGINFO: int, nmask = 0;
+ for (let i = 0z; i < len(opt); i += 1) {
+ match (opt[i]) {
+ case let flag: flag =>
+ sa_flags |= flag: int;
+ case let mask: sigset =>
+ assert(nmask == 0, "Multiple signal masks provided to signal::handle");
+ nmask += 1;
+ sa_mask = mask;
+ };
+ };
+
+ let new = rt::sigact {
+ sa_sigaction = handler: *fn(int, *rt::siginfo, *opaque) void,
+ sa_mask = sa_mask,
+ sa_flags = sa_flags,
+ };
+ let old = rt::sigact {
+ sa_sigaction = null: *fn(int, *rt::siginfo, *opaque) void,
+ ...
+ };
+ match (rt::sigaction(signum, &new, &old)) {
+ case rt::errno =>
+ abort("sigaction failed (invalid signal?)");
+ case int => void;
+ };
+ return old;
+};
+
+// Restores previous signal behavior following [[handle]].
+export fn restore(signum: sig, action: *sigaction) void = {
+ match (rt::sigaction(signum, action: *rt::sigact, null)) {
+ case rt::errno =>
+ abort("sigaction failed (invalid signal?)");
+ case int => void;
+ };
+};
+
+// Unregisters signal handlers for the specified signal.
+export fn reset(signum: sig) void = {
+ handle(signum, rt::SIG_DFL: *handler);
+};
+
+// Unregisters all signal handlers.
+export fn resetall() void = {
+ // sig::KILL and sig::STOP deliberately omitted; see sigaction(2)
+ reset(sig::HUP);
+ reset(sig::INT);
+ reset(sig::QUIT);
+ reset(sig::ILL);
+ reset(sig::TRAP);
+ reset(sig::ABRT);
+ reset(sig::EMT);
+ reset(sig::FPE);
+ reset(sig::BUS);
+ reset(sig::SEGV);
+ reset(sig::SYS);
+ reset(sig::PIPE);
+ reset(sig::ALRM);
+ reset(sig::TERM);
+ reset(sig::URG);
+ reset(sig::TSTP);
+ reset(sig::CONT);
+ reset(sig::CHLD);
+ reset(sig::TTIN);
+ reset(sig::TTOU);
+ reset(sig::IO);
+ reset(sig::XCPU);
+ reset(sig::XFSZ);
+ reset(sig::VTALRM);
+ reset(sig::PROF);
+ reset(sig::WINCH);
+ reset(sig::INFO);
+ reset(sig::USR1);
+ reset(sig::USR2);
+ reset(sig::THR);
+ reset(sig::LIBRT);
+};
+
+// Prevents given signal from arriving to the current process.
+// One common use case is to ignore SIGCHLD to avoid zombie child processes.
+export fn ignore(signum: sig) void = {
+ handle(signum, rt::SIG_IGN: *handler);
+};
+
+// Adds the given list of signals to the process's current signal mask,
+// returning the old signal mask. This is a convenience function around
+// [[setprocmask]].
+export fn block(signals: sig...) sigset = {
+ let new = newsigset(signals...);
+ return setprocmask(how::BLOCK, &new);
+};
+
+// Removes the given list of signals from the process's current signal mask,
+// returning the old signal mask. This is a convenience function around
+// [[setprocmask]].
+export fn unblock(signals: sig...) sigset = {
+ let new = newsigset(signals...);
+ return setprocmask(how::UNBLOCK, &new);
+};
+
+// Sets the process's signal mask, returning the previous mask.
+export fn setprocmask(how: how, mask: *sigset) sigset = {
+ let old = sigset { ... };
+ rt::sigprocmask(how, mask: *rt::sigset, &old)!;
+ return old;
+};
+
+// Gets the current process's signal mask.
+export fn getprocmask() sigset = {
+ let old = sigset { ... };
+ rt::sigprocmask(how::SETMASK, null, &old)!;
+ return old;
+};
+
+// Defines the modes of operation for [[setprocmask]].
+export type how = enum int {
+ // Adds the given set of signals to the current mask.
+ BLOCK = rt::SIG_BLOCK,
+ // Removes the given set of signals from the current mask.
+ UNBLOCK = rt::SIG_UNBLOCK,
+ // Sets the process mask to the given set.
+ SETMASK = rt::SIG_SETMASK,
+};
+
+export type sigaction = rt::sigact;
+
+export type sigset = rt::sigset;
+
+// Creates a new signal set filled in with the provided signals (or empty if
+// none are provided).
+export fn newsigset(items: sig...) sigset = {
+ let set = sigset { ... };
+ rt::sigemptyset(&set);
+ sigset_add(&set, items...);
+ return set;
+};
+
+// Sets a [[sigset]] to empty.
+export fn sigset_empty(set: *sigset) void = {
+ rt::sigemptyset(set: *rt::sigset);
+};
+
+// Adds signals to a [[sigset]].
+export fn sigset_add(set: *sigset, items: sig...) void = {
+ for (let i = 0z; i < len(items); i += 1) {
+ rt::sigaddset(set: *rt::sigset, items[i])!;
+ };
+};
+
+// Removes signals from a [[sigset]].
+export fn sigset_del(set: *sigset, items: sig...) void = {
+ for (let i = 0z; i < len(items); i += 1) {
+ rt::sigdelset(set: *rt::sigset, items[i])!;
+ };
+};
+
+// Adds all platform-defined signals to a [[sigset]].
+export fn sigset_fill(set: *sigset) void = {
+ rt::sigfillset(set: *rt::sigset)!;
+};
+
+// Returns true if the given signal is a member of this [[sigset]].
+export fn sigset_member(set: *sigset, item: sig) bool = {
+ return rt::sigismember(set: *rt::sigset, item)!;
+};
+
+// Waits for a signal among the given [[sigset]] to be delivered, then returns
+// the signal number.
+//
+// If a signal is received while waiting, [[errors::interrupted]] is returned.
+// Most consumers of this function will likely wish to block all signals and
+// handle them exclusively through [[wait]] et al, in which case this error
+// cannot occur.
+//
+// See also [[waitinfo]] and [[timedwait]].
+export fn wait(set: *sigset) (sig | errors::interrupted) = {
+ let signal = 0i;
+ match (rt::sigwait(set: *rt::sigset, &signal)) {
+ case let err: rt::errno =>
+ assert(err == rt::EINTR);
+ return errors::interrupted;
+ case void =>
+ return signal: sig;
+ };
+};
+
+// Waits for a signal among the given [[sigset]] to be delivered, then returns
+// the corresponding [[siginfo]] data.
+//
+// See notes on [[wait]] regarding the [[errors::interrupted]] case.
+//
+// This function is designed to provide the portable subset of the semantics of
+// sigwaitinfo(3) as defined by POSIX.1-2008. To access the complete siginfo_t
+// structure provided by the underlying platform, use [[rt::sigwaitinfo]] and
+// [[rt::siginfo_t]] directly.
+//
+// Note that this function is not supported on NetBSD.
+export fn waitinfo(set: *sigset) (siginfo | errors::interrupted) = {
+ let info = rt::siginfo {
+ si_addr = null: *opaque,
+ si_value = rt::sigval { ... },
+ ...
+ };
+ match (rt::sigwaitinfo(set: *rt::sigset, &info)) {
+ case let err: rt::errno =>
+ assert(err == rt::EINTR);
+ return errors::interrupted;
+ case int =>
+ return *(&info: *siginfo);
+ };
+};
+
+// Waits for a signal among the given [[sigset]] to be delivered, then returns
+// the corresponding [[siginfo]] data.
+//
+// Returns a [[siginfo]] if a signal is successfully processed through this
+// function, or [[errors::again]] if the timeout expired. See notes on [[wait]]
+// regarding the [[errors::interrupted]] case.
+//
+// This function is designed to provide the portable subset of the semantics of
+// sigtimedwait(3) as defined by POSIX.1-2008. To access the complete siginfo_t
+// structure provided by the underlying platform, use [[rt::sigtimedwait]] and
+// [[rt::siginfo_t]] directly.
+//
+// Note that this function is not supported on OpenBSD.
+export fn timedwait(
+ set: *sigset,
+ timeout: time::duration,
+) (siginfo | errors::interrupted | errors::again) = {
+ let info = rt::siginfo {
+ si_addr = null: *opaque,
+ si_value = rt::sigval { ... },
+ ...
+ };
+ let to = time::duration_to_timeval(timeout);
+ match (rt::sigwaitinfo(set: *rt::sigset, &info)) {
+ case let err: rt::errno =>
+ switch (err) {
+ case rt::EINTR =>
+ return errors::interrupted;
+ case rt::EAGAIN =>
+ return errors::again;
+ case => abort();
+ };
+ case int =>
+ return *(&info: *siginfo);
+ };
+};
+
+// Provides additional information about signal deliveries. Only the members
+// defined by POSIX are available here; cast to [[rt::siginfo]] to access
+// non-portable members.
+export type siginfo = union {
+ struct {
+ // The signal number being delivered.
+ signo: sig,
+ // The errno, if any, associated with this signal. See
+ // [[errors::errno]] to convert to a Hare-native error.
+ errno: rt::errno,
+ // The signal code, if any.
+ code: code,
+ // Process ID of the sender.
+ pid: unix::pid,
+ // Real user ID of the sending process.
+ uid: unix::uid,
+ // Exit value or signal.
+ status: int,
+ // Address of the faulting instruction.
+ addr: *opaque,
+ },
+ // Pads the structure out to the length used by the kernel; do not use.
+ _si_pad: [128 - 3 * size(int)]u8,
+};
+
+// A code indicating why a signal was sent.
+export type code = enum int {
+ USER = 0, // sent by userspace program (kill)
+ KERNEL = 128, // sent by kernel
+ QUEUE = -1, // sent by sigqueue
+ TIMER = -2, // generated by expiration of a timer
+ MESQ = -3, // generated by arrival of a message on an empty queue
+ ASYNCIO = -4, // generated by completion of an asynchronous I/O request
+ SIGIO = -5,
+ TKILL = -6, // sent by userspace program (tkill, tgkill)
+ ASYNCNL = -60,
+
+ ILLOPC = 1, // sig::ILL: illegal opcode
+ ILLOPN = 2, // sig::ILL: illegal operand
+ ILLADR = 3, // sig::ILL: illegal addressing mode
+ ILLTRP = 4, // sig::ILL: illegal trap
+ PRVOPC = 5, // sig::ILL: privileged opcode
+ PRVREG = 6, // sig::ILL: privileged register
+ COPROC = 7, // sig::ILL: coprocessor error
+ BADSTK = 8, // sig::ILL: internal stack error
+
+ INTOVF = 1, // sig::FPE: integer overflow
+ INTDIV = 2, // sig::FPE: integer divide by zero
+ FLTDIV = 3, // sig::FPE: floating-point divide by zero
+ FLTOVF = 4, // sig::FPE: floating-point overflow
+ FLTUND = 5, // sig::FPE: floating-point underflow
+ FLTRES = 6, // sig::FPE: floating-point inexact result
+ FLTINV = 7, // sig::FPE: invalid floating-point operation
+ FLTSUB = 8, // sig::FPE: subscript out of range
+
+ MAPERR = 1, // sig::SEGV: address not mapped to object
+ ACCERR = 2, // sig::SEGV: invalid permissions for mapped object
+ PKUERR = 100, // sig::SEGV: access was denied by memory protection keys (x86_64)
+
+ ADRALN = 1, // sig::BUS: invalid address alignment
+ ADRERR = 2, // sig::BUS: nonexistent physical address
+ OBJERR = 3, // sig::BUS: object-specific hardware error
+ OOMERR = 100, // sig::BUS: out of memory
+
+ BRKPT = 1, // sig::TRAP: process breakpoint
+ TRACE = 2, // sig::TRAP: process trace trap
+ DTRACE = 3, // sig::TRAP: DTrace induced trap
+ CAP = 4, // sig::TRAP: capabilities protection trap
+
+ EXITED = 1, // sig::CHLD: child exited
+ KILLED = 2, // sig::CHLD: child terminated abnormally without a core file
+ DUMPED = 3, // sig::CHLD: child terminated abnormally with a core file
+ TRAPPED = 4, // sig::CHLD: traced child has trapped
+ STOPPED = 5, // sig::CHLD: child has stopped
+ CONTINUED = 6, // sig::CHLD: stopped child has continued
+
+ IN = 1, // sig::IO: data input available
+ OUT = 2, // sig::IO: output buffers available
+ MSG = 3, // sig::IO: input message available
+ ERR = 4, // sig::IO: I/O error
+ PRI = 5, // sig::IO: high priority input available
+ HUP = 6, // sig::IO: device disconnected
+};
+
+// Flags used to configure the behavior of a signal handler.
+export type flag = enum int {
+ // For use with sig::CHLD. Prevents notifications when child processes
+ // stop (e.g. via sig::STOP) or resume (i.e. sig::CONT).
+ NOCLDSTOP = rt::SA_NOCLDSTOP: int,
+ // For use with sig::CHLD. Do not transform children into zombies when
+ // they terminate. Note that POSIX leaves the delivery of sig::CHLD
+ // unspecified when this flag is present; some systems will still
+ // deliver a signal and others may not.
+ NOCLDWAIT = rt::SA_NOCLDWAIT: int,
+ // Uses an alternate stack when handling this signal. See
+ // [[setaltstack]] and [[getaltstack]] for details.
+ ONSTACK = rt::SA_ONSTACK: int,
+ // Do not add the signal to the signal mask while executing the signal
+ // handler. This can cause the same signal to be delivered again during
+ // the execution of the signal handler.
+ NODEFER = rt::SA_NODEFER: int,
+ // Restore the signal handler to the default behavior upon entering the
+ // signal handler.
+ RESETHAND = rt::SA_RESETHAND: int,
+ // Makes certain system calls restartable across signals. See signal(7)
+ // or similar documentation for your local system for details.
+ RESTART = rt::SA_RESTART: int,
+};
+
+// All possible signals.
+export type sig = enum int {
+ HUP = rt::SIGHUP, // Hangup.
+ INT = rt::SIGINT, // Terminal interrupt.
+ QUIT = rt::SIGQUIT, // Terminal quit.
+ ILL = rt::SIGILL, // Illegal instruction.
+ TRAP = rt::SIGTRAP, // Trace/breakpoint trap.
+ ABRT = rt::SIGABRT, // Process abort.
+ IOT = rt::SIGIOT, // Synonym for ABRT, provided for compatibility.
+ EMT = rt::SIGEMT, // Emulate instruction executed.
+ FPE = rt::SIGFPE, // Erroneous arithmetic operation.
+ KILL = rt::SIGKILL, // Kill (cannot be caught or ignored).
+ BUS = rt::SIGBUS, // Access to an undefined portion of a memory object.
+ SEGV = rt::SIGSEGV, // Invalid memory reference.
+ SYS = rt::SIGSYS, // Bad system call.
+ PIPE = rt::SIGPIPE, // Write on a pipe with no one to read it.
+ ALRM = rt::SIGALRM, // Alarm clock.
+ TERM = rt::SIGTERM, // Termination.
+ URG = rt::SIGURG, // High bandwidth data is available at a socket.
+ STOP = rt::SIGSTOP, // Stop executing (cannot be caught or ignored).
+ TSTP = rt::SIGTSTP, // Terminal stop.
+ CONT = rt::SIGCONT, // Continue executing, if stopped.
+ CHLD = rt::SIGCHLD, // Child process terminated, stopped, or continued.
+ TTIN = rt::SIGTTIN, // Background process attempting read.
+ TTOU = rt::SIGTTOU, // Background process attempting write.
+ IO = rt::SIGIO, // I/O now possible.
+ XCPU = rt::SIGXCPU, // CPU time limit exceeded.
+ XFSZ = rt::SIGXFSZ, // File size limit exceeded.
+ VTALRM = rt::SIGVTALRM, // Virtual timer expired.
+ PROF = rt::SIGPROF, // Profiling timer expired.
+ WINCH = rt::SIGWINCH, // Window resize.
+ INFO = rt::SIGINFO, // Status request from keyboard.
+ USR1 = rt::SIGUSR1, // User-defined signal 1.
+ USR2 = rt::SIGUSR2, // User-defined signal 2.
+ THR = rt::SIGTHR, // Thread interrupt.
+ LIBRT = rt::SIGLIBRT, // Real-time library interrupt.
+};
+
+// Returns the human friendly name of a given signal.
+export fn signame(sig: sig) const str = {
+ switch (sig) {
+ case sig::HUP =>
+ return "SIGHUP";
+ case sig::INT =>
+ return "SIGINT";
+ case sig::QUIT =>
+ return "SIGQUIT";
+ case sig::ILL =>
+ return "SIGILL";
+ case sig::TRAP =>
+ return "SIGTRAP";
+ case sig::ABRT =>
+ return "SIGABRT";
+ case sig::EMT =>
+ return "SIGEMT";
+ case sig::FPE =>
+ return "SIGFPE";
+ case sig::KILL =>
+ return "SIGKILL";
+ case sig::BUS =>
+ return "SIGBUS";
+ case sig::SEGV =>
+ return "SIGSEGV";
+ case sig::SYS =>
+ return "SIGSYS";
+ case sig::PIPE =>
+ return "SIGPIPE";
+ case sig::ALRM =>
+ return "SIGALRM";
+ case sig::TERM =>
+ return "SIGTERM";
+ case sig::URG =>
+ return "SIGURG";
+ case sig::STOP =>
+ return "SIGSTOP";
+ case sig::TSTP =>
+ return "SIGTSTP";
+ case sig::CONT =>
+ return "SIGCONT";
+ case sig::CHLD =>
+ return "SIGCHLD";
+ case sig::TTIN =>
+ return "SIGTTIN";
+ case sig::TTOU =>
+ return "SIGTTOU";
+ case sig::IO =>
+ return "SIGIO";
+ case sig::XCPU =>
+ return "SIGXCPU";
+ case sig::XFSZ =>
+ return "SIGXFSZ";
+ case sig::VTALRM =>
+ return "SIGVTALRM";
+ case sig::PROF =>
+ return "SIGPROF";
+ case sig::WINCH =>
+ return "SIGWINCH";
+ case sig::INFO =>
+ return "SIGINFO";
+ case sig::USR1 =>
+ return "SIGUSR1";
+ case sig::USR2 =>
+ return "SIGUSR2";
+ case sig::THR =>
+ return "SIGTHR";
+ case sig::LIBRT =>
+ return "SIGLIBRT";
+ };
+};
diff --git a/unix/tty/+netbsd/isatty.ha b/unix/tty/+netbsd/isatty.ha
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use io;
+use os;
+use rt;
+
+// Returns whether the given stream is connected to a terminal.
+export fn isatty(fd: io::file) bool = {
+ let wsz = rt::winsize { ... };
+ match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *opaque)) {
+ case let e: rt::errno =>
+ return false;
+ case let r: int =>
+ return r == 0;
+ };
+};
diff --git a/unix/tty/+netbsd/open.ha b/unix/tty/+netbsd/open.ha
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use fs;
+use io;
+use os;
+
+// Returns a stream connected to the TTY of the current process. The caller must
+// close it using [[io::close]].
+export fn open() (io::file | error) = {
+ match (os::open("/dev/tty", fs::flag::RDWR)) {
+ case let f: io::file =>
+ return f;
+ case fs::error =>
+ return errors::noentry;
+ };
+};
diff --git a/unix/tty/+netbsd/pgid.ha b/unix/tty/+netbsd/pgid.ha
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use os::exec;
+use rt;
+
+// Sets the process group on the foreground of this terminal.
+export fn tcsetpgrp(fd: io::file, pg: exec::process) (void | errors::error) = {
+ match (rt::ioctl(fd, rt::TIOCSPGRP, pg: u64)) {
+ case int => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
diff --git a/unix/tty/+netbsd/pty.ha b/unix/tty/+netbsd/pty.ha
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use fmt;
+use fs;
+use io;
+use os;
+use rt;
+use types::c;
+use strings;
+
+// Opens an available pseudoterminal and returns the file descriptors of the
+// master and slave.
+export fn openpty() ((io::file, io::file) | fs::error) = {
+ let master = open_master()?;
+ let slave = match (get_slave(master)) {
+ case let e: fs::error =>
+ io::close(master)!;
+ return e;
+ case let s: io::file =>
+ yield s;
+ };
+ return (master, slave);
+};
+
+// Opens an available pseudoterminal master.
+fn open_master() (io::file | fs::error) = {
+ match (rt::posix_openpt(rt::O_RDWR | rt::O_NOCTTY)) {
+ case let e: rt::errno =>
+ return errors::errno(e);
+ case let i: int =>
+ return io::fdopen(i);
+ };
+};
+
+// Returns a file descriptor referring to the pseudoterminal slave for a
+// pseudoterminal master.
+fn get_slave(master: io::file) (io::file | fs::error) =
+ os::open(ptsname(master)?, fs::flag::RDWR);
+
+// Returns the filename of the pseudoterminal slave.
+export fn ptsname(master: io::file) (str | error) = {
+ let pm = rt::ptmget { ... };
+ match (rt::ioctl(master, rt::TIOCPTSNAME, &pm)) {
+ case let e: rt::errno =>
+ switch (e) {
+ case rt::EBADF =>
+ return errors::invalid;
+ case rt::EINVAL, rt::ENOTTY =>
+ return errors::unsupported;
+ case =>
+ abort("Unexpected error from ioctl");
+ };
+ case => void;
+ };
+ return strings::dup(c::tostr(&pm.sn: *const c::char)!);
+};
+
+// Sets the dimensions of the underlying pseudoterminal for an [[io::file]].
+export fn set_winsize(pty: io::file, sz: ttysize) (void | error) = {
+ let wsz = rt::winsize { ws_row = sz.rows, ws_col = sz.columns, ... };
+ match (rt::ioctl(pty, rt::TIOCSWINSZ, &wsz)) {
+ case let e: rt::errno =>
+ switch (e) {
+ case rt::EBADF, rt::EINVAL =>
+ return errors::invalid;
+ case rt::ENOTTY =>
+ return errors::unsupported;
+ case =>
+ abort("Unexpected error from ioctl");
+ };
+ case => void;
+ };
+};
diff --git a/unix/tty/+netbsd/termios.ha b/unix/tty/+netbsd/termios.ha
@@ -0,0 +1,77 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use rt;
+
+export type termios = struct {
+ file: io::file,
+ saved: rt::termios,
+ current: rt::termios,
+};
+
+// Retrieves serial port settings of the given terminal.
+export fn termios_query(file: io::file) (termios | errors::error) = {
+ let settings = rt::termios { ... };
+
+ match (rt::ioctl(file, rt::TIOCGETA, &settings)) {
+ case int => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+
+ return termios {
+ file = file,
+ saved = settings,
+ current = settings,
+ };
+};
+
+// Restores original serial port settings.
+export fn termios_restore(termios: *const termios) void = {
+ rt::ioctl(termios.file, rt::TIOCSETA, &termios.saved): void;
+};
+
+// Sets serial port settings.
+export fn termios_set(termios: *const termios) (void | errors::error) = {
+ match (rt::ioctl(termios.file, rt::TIOCSETA, &termios.current)) {
+ case int => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+};
+
+// Enables "raw" mode for this terminal, disabling echoing, line
+// editing, and signal handling. Users should call [[termios_query]]
+// prior to this to save the previous terminal settings, and
+// [[termios_restore]] to restore them before exiting.
+export fn makeraw(termios: *termios) (void | errors::error) = {
+ // Disable break signal and CR<->LF processing
+ termios.current.c_iflag &= ~(rt::tcflag::IGNBRK | rt::tcflag::BRKINT
+ | rt::tcflag::INLCR | rt::tcflag::IGNCR
+ | rt::tcflag::ICRNL);
+ // Disable output post-processing
+ termios.current.c_oflag &= ~rt::tcflag::OPOST;
+ // Disable character echo, canonical mode, implementation defined
+ // extensions and INTR/QUIT/SUSP characters
+ termios.current.c_lflag &= ~(rt::tcflag::ECHO | rt::tcflag::ECHONL
+ | rt::tcflag::ICANON | rt::tcflag::IEXTEN
+ | rt::tcflag::ISIG);
+
+ termios_set(termios)?;
+};
+
+// Disables "echo" on this terminal. Users should call [[termios_restore]] to
+// restore settings.
+export fn noecho(termios: *termios) (void | errors::error) = {
+ termios.current.c_lflag &= ~rt::tcflag::ECHO;
+ termios_set(termios)?;
+};
+
+// Enables "noncanonical" mode for this terminal, disabling line buffering and
+// line editing. Users should call [[termios_restore]] to restore settings.
+export fn noncanonical(termios: *termios) (void | errors::error) = {
+ termios.current.c_lflag &= ~rt::tcflag::ICANON;
+ termios_set(termios)?;
+};
diff --git a/unix/tty/+netbsd/winsize.ha b/unix/tty/+netbsd/winsize.ha
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: MPL-2.0
+// (c) Hare authors <https://harelang.org>
+
+use errors;
+use io;
+use os;
+use rt;
+
+// Returns the dimensions of underlying terminal for an [[io::file]].
+export fn winsize(fd: io::file) (ttysize | error) = {
+ let wsz = rt::winsize { ... };
+ match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *opaque)) {
+ case let e: rt::errno =>
+ switch (e) {
+ case rt::EBADF =>
+ return errors::invalid;
+ case rt::ENOTTY =>
+ return errors::unsupported;
+ case =>
+ abort("Unexpected error from ioctl");
+ };
+ case int =>
+ return ttysize {
+ rows = wsz.ws_row,
+ columns = wsz.ws_col,
+ };
+ };
+};