commit e0ed8fa32b0c4d66fd1ec1c2bd4d684cba14e471
parent 478ca35964a855842d5dfebe3063e8e69d100204
Author: Ember Sawady <ecs@d2evs.net>
Date: Wed, 7 Jun 2023 08:06:41 +0000
os: add setenv and unsetenv
Signed-off-by: Ember Sawady <ecs@d2evs.net>
Diffstat:
9 files changed, 283 insertions(+), 296 deletions(-)
diff --git a/os/+freebsd/dirfdfs.ha b/os/+freebsd/dirfdfs.ha
@@ -2,7 +2,6 @@
// (c) 2021-2022 Drew DeVault <sir@cmpwn.com>
// (c) 2021 Ember Sawady <ecs@d2evs.net>
// (c) 2022 Jon Eskin <eskinjp@gmail.com>
-use bytes;
use errors;
use encoding::utf8;
use fs;
diff --git a/os/+freebsd/environ.ha b/os/+freebsd/environ.ha
@@ -1,138 +0,0 @@
-// License: MPL-2.0
-// (c) 2021 Drew DeVault <sir@cmpwn.com>
-// (c) 2021 Ember Sawady <ecs@d2evs.net>
-use bytes;
-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 init_environ() 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 fini_environ() void = {
- if (rt::argc >= len(args_static)) {
- free(args);
- };
- free(envp);
-};
-
-// Looks up an environment variable and returns its value, or void if unset.
-export fn getenv(name: const str) (str | void) = {
- const name_b = strings::toutf8(name);
- for (let i = 0z; rt::envp[i] != null; i += 1) {
- const item = rt::envp[i]: *[*]u8;
- const ln = c::strlen(item: *c::char);
- const eq: size = match (bytes::index(item[..ln], '=')) {
- case void =>
- abort("Environment violates System-V invariants");
- case let i: size =>
- yield i;
- };
- if (bytes::equal(name_b, item[..eq])) {
- const ln = c::strlen(item: *const c::char);
- return strings::fromutf8(item[eq+1..ln])!;
- };
- };
-};
-
-// Looks up an environment variable and returns its value, or a default value if
-// unset.
-export fn tryenv(name: const str, default: str) str = match (getenv(name)) {
-case let s: str =>
- yield s;
-case void =>
- yield default;
-};
-
-let envp: []str = [];
-
-// 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) {
- append(envp, c::tostr(rt::envp[i]: *const c::char)!);
- };
- return envp;
-};
-
-// Returns the host kernel name
-export fn sysname() const str = {
- static let buf: [512]u8 = [0...];
- let sz: size = len(buf);
- rt::sysctlbyname("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::sysctlbyname("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::sysctlbyname("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::sysctlbyname("kern.version", &buf, &sz, null, 0)!;
- return strings::fromutf8(buf[..(sz - 1)])!;
-};
-
-// Returns the host CPU architecture
-export fn machine() const str = {
- static let buf: [512]u8 = [0...];
- let sz: size = len(buf);
- rt::sysctlbyname("hw.machine", &buf, &sz, null, 0)!;
- const mach = strings::fromutf8(buf[..(sz - 1)])!;
- // Translate to Hare names
- switch (mach) {
- case "amd64" =>
- return "x86_64";
- case =>
- return mach;
- };
-};
-
-// Returns the number of usable CPUs.
-export fn cpucount() (int | errors::error) = {
- let count = 0;
- let length = size(int);
- match (rt::sysctlbyname("hw.ncpu", &count, &length, null, 0)) {
- case void => void;
- case let err: rt::errno =>
- return errors::errno(err);
- };
- return count;
-};
diff --git a/os/+freebsd/platform_environ.ha b/os/+freebsd/platform_environ.ha
@@ -0,0 +1,107 @@
+// License: MPL-2.0
+// (c) 2021 Drew DeVault <sir@cmpwn.com>
+// (c) 2021 Ember Sawady <ecs@d2evs.net>
+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::sysctlbyname("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::sysctlbyname("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::sysctlbyname("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::sysctlbyname("kern.version", &buf, &sz, null, 0)!;
+ return strings::fromutf8(buf[..(sz - 1)])!;
+};
+
+// Returns the host CPU architecture
+export fn machine() const str = {
+ static let buf: [512]u8 = [0...];
+ let sz: size = len(buf);
+ rt::sysctlbyname("hw.machine", &buf, &sz, null, 0)!;
+ const mach = strings::fromutf8(buf[..(sz - 1)])!;
+ // Translate to Hare names
+ switch (mach) {
+ case "amd64" =>
+ return "x86_64";
+ case =>
+ return mach;
+ };
+};
+
+// Returns the number of usable CPUs.
+export fn cpucount() (int | errors::error) = {
+ let count = 0;
+ let length = size(int);
+ match (rt::sysctlbyname("hw.ncpu", &count, &length, null, 0)) {
+ case void => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+ return count;
+};
diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha
@@ -4,7 +4,6 @@
// (c) 2021-2022 Drew DeVault <sir@cmpwn.com>
// (c) 2021 Ember Sawady <ecs@d2evs.net>
// (c) 2022 Jon Eskin <eskinjp@gmail.com>
-use bytes;
use errors;
use encoding::utf8;
use fs;
diff --git a/os/+linux/environ.ha b/os/+linux/environ.ha
@@ -1,144 +0,0 @@
-// License: MPL-2.0
-// (c) 2021 Drew DeVault <sir@cmpwn.com>
-// (c) 2021 Ember Sawady <ecs@d2evs.net>
-use bytes;
-use errors;
-use math;
-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 init_environ() 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 fini_environ() void = {
- if (rt::argc >= len(args_static)) {
- free(args);
- };
- free(envp);
-};
-
-// Looks up an environment variable and returns its value, or void if unset.
-export fn getenv(name: const str) (str | void) = {
- const name_b = strings::toutf8(name);
- for (let i = 0z; rt::envp[i] != null; i += 1) {
- const item = rt::envp[i]: *[*]u8;
- const ln = c::strlen(item: *c::char);
- const eq: size = match (bytes::index(item[..ln], '=')) {
- case void =>
- abort("Environment violates System-V invariants");
- case let i: size =>
- yield i;
- };
- if (bytes::equal(name_b, item[..eq])) {
- const ln = c::strlen(item: *const c::char);
- return strings::fromutf8(item[eq+1..ln])!;
- };
- };
-};
-
-// Looks up an environment variable and returns its value, or a default value if
-// unset.
-export fn tryenv(name: const str, default: str) str = match (getenv(name)) {
-case let s: str =>
- yield s;
-case void =>
- yield default;
-};
-
-let envp: []str = [];
-
-// 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) {
- append(envp, c::tostr(rt::envp[i]: *const c::char)!);
- };
- return envp;
-};
-
-let uts: rt::utsname = rt::utsname { ... };
-let uts_valid: bool = false;
-
-// Returns the host kernel name
-export fn sysname() const str = {
- if (!uts_valid) {
- rt::uname(&uts) as void;
- uts_valid = true;
- };
- return c::tostr(&uts.sysname: *const c::char)!;
-};
-
-// Returns the host system hostname
-export fn hostname() const str = {
- if (!uts_valid) {
- rt::uname(&uts) as void;
- uts_valid = true;
- };
- return c::tostr(&uts.nodename: *const c::char)!;
-};
-
-// Returns the host kernel version
-export fn release() const str = {
- if (!uts_valid) {
- rt::uname(&uts) as void;
- uts_valid = true;
- };
- return c::tostr(&uts.release: *const c::char)!;
-};
-
-// Returns the host operating system version
-export fn version() const str = {
- if (!uts_valid) {
- rt::uname(&uts) as void;
- uts_valid = true;
- };
- return c::tostr(&uts.version: *const c::char)!;
-};
-
-// Returns the host CPU architecture
-export fn machine() const str = {
- if (!uts_valid) {
- rt::uname(&uts) as void;
- uts_valid = true;
- };
- return c::tostr(&uts.machine: *const c::char)!;
-};
-
-// Returns the number of usable CPUs.
-export fn cpucount() (int | errors::error) = {
- let set = rt::cpu_set { ... };
- match (rt::sched_getaffinity(rt::getpid(), size(rt::cpu_set), &set)) {
- case void => void;
- case let err: rt::errno =>
- return errors::errno(err);
- };
-
- let ret = 0;
- for (let i = 0z; i < len(set.__bits); i += 1) {
- ret += math::pop_count_u64(set.__bits[i]): int;
- };
- return ret;
-};
diff --git a/os/+linux/platform_environ.ha b/os/+linux/platform_environ.ha
@@ -0,0 +1,113 @@
+// License: MPL-2.0
+// (c) 2021 Drew DeVault <sir@cmpwn.com>
+// (c) 2021 Ember Sawady <ecs@d2evs.net>
+use errors;
+use math;
+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;
+};
+
+let uts: rt::utsname = rt::utsname { ... };
+let uts_valid: bool = false;
+
+// Returns the host kernel name
+export fn sysname() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ uts_valid = true;
+ };
+ return c::tostr(&uts.sysname: *const c::char)!;
+};
+
+// Returns the host system hostname
+export fn hostname() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ uts_valid = true;
+ };
+ return c::tostr(&uts.nodename: *const c::char)!;
+};
+
+// Returns the host kernel version
+export fn release() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ uts_valid = true;
+ };
+ return c::tostr(&uts.release: *const c::char)!;
+};
+
+// Returns the host operating system version
+export fn version() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ uts_valid = true;
+ };
+ return c::tostr(&uts.version: *const c::char)!;
+};
+
+// Returns the host CPU architecture
+export fn machine() const str = {
+ if (!uts_valid) {
+ rt::uname(&uts) as void;
+ uts_valid = true;
+ };
+ return c::tostr(&uts.machine: *const c::char)!;
+};
+
+// Returns the number of usable CPUs.
+export fn cpucount() (int | errors::error) = {
+ let set = rt::cpu_set { ... };
+ match (rt::sched_getaffinity(rt::getpid(), size(rt::cpu_set), &set)) {
+ case void => void;
+ case let err: rt::errno =>
+ return errors::errno(err);
+ };
+
+ let ret = 0;
+ for (let i = 0z; i < len(set.__bits); i += 1) {
+ ret += math::pop_count_u64(set.__bits[i]): int;
+ };
+ return ret;
+};
diff --git a/os/environ.ha b/os/environ.ha
@@ -0,0 +1,45 @@
+use errors;
+use strings;
+
+let envp: []str = [];
+
+@fini fn envp() void = strings::freeall(envp);
+
+// Looks up an environment variable and returns its value, or void if unset.
+export fn getenv(name: const str) (str | void) = {
+ getenvs(); // populate envp
+ for (let i = 0z; i < len(envp); i += 1) {
+ let (key, value) = strings::cut(envp[i], "=");
+ if (key == name) return value;
+ };
+};
+
+// Looks up an environment variable and returns its value, or a default value if
+// unset.
+export fn tryenv(name: const str, default: str) str = match (getenv(name)) {
+case let s: str => return s;
+case void => return default;
+};
+
+// Sets an environment variable, overwriting it if it's already set. The name
+// may not contain '=' or '\0', and the value may not contain '\0'.
+export fn setenv(name: const str, value: const str) (void | errors::invalid) = {
+ if (strings::contains(value, '\0')) return errors::invalid;
+ unsetenv(name)?;
+ append(envp, strings::join("=", name, value));
+};
+
+// Unsets an environment variable. Does nothing if the variable isn't set. The
+// name may not contain '=' or '\0'.
+export fn unsetenv(name: const str) (void | errors::invalid) = {
+ if (strings::contains(name, '=', '\0')) return errors::invalid;
+ getenvs(); // populate envp
+ for (let i = 0z; i < len(envp); i += 1) {
+ if (strings::cut(envp[i], "=").0 == name) {
+ free(envp[i]);
+ delete(envp[i]);
+ break;
+ };
+ };
+};
+
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -1164,25 +1164,27 @@ os() {
gen_srcs -plinux os \
+linux/dirfdfs.ha \
- +linux/environ.ha \
+ +linux/platform_environ.ha \
+linux/$exit \
+linux/fs.ha \
+linux/memory.ha \
+linux/status.ha \
+linux/stdfd.ha \
+ environ.ha \
os.ha
- gen_ssa -plinux os io strings fs encoding::utf8 bytes bufio \
+ gen_ssa -plinux os io strings fs encoding::utf8 bufio \
errors math types::c
gen_srcs -pfreebsd os \
- +freebsd/environ.ha \
+ +freebsd/platform_environ.ha \
+freebsd/$exit \
+freebsd/dirfdfs.ha \
+freebsd/status.ha \
+freebsd/stdfd.ha \
+freebsd/fs.ha \
+ environ.ha \
os.ha
- gen_ssa -pfreebsd os io strings fs encoding::utf8 bytes bufio \
+ gen_ssa -pfreebsd os io strings fs encoding::utf8 bufio \
errors types::c
}
diff --git a/stdlib.mk b/stdlib.mk
@@ -1881,15 +1881,16 @@ $(HARECACHE)/net/uri/net_uri-any.ssa: $(stdlib_net_uri_any_srcs) $(stdlib_rt) $(
# os (+linux)
stdlib_os_linux_srcs = \
$(STDLIB)/os/+linux/dirfdfs.ha \
- $(STDLIB)/os/+linux/environ.ha \
+ $(STDLIB)/os/+linux/platform_environ.ha \
$(STDLIB)/os/+linux/exit.ha \
$(STDLIB)/os/+linux/fs.ha \
$(STDLIB)/os/+linux/memory.ha \
$(STDLIB)/os/+linux/status.ha \
$(STDLIB)/os/+linux/stdfd.ha \
+ $(STDLIB)/os/environ.ha \
$(STDLIB)/os/os.ha
-$(HARECACHE)/os/os-linux.ssa: $(stdlib_os_linux_srcs) $(stdlib_rt) $(stdlib_io_$(PLATFORM)) $(stdlib_strings_$(PLATFORM)) $(stdlib_fs_$(PLATFORM)) $(stdlib_encoding_utf8_$(PLATFORM)) $(stdlib_bytes_$(PLATFORM)) $(stdlib_bufio_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_math_$(PLATFORM)) $(stdlib_types_c_$(PLATFORM))
+$(HARECACHE)/os/os-linux.ssa: $(stdlib_os_linux_srcs) $(stdlib_rt) $(stdlib_io_$(PLATFORM)) $(stdlib_strings_$(PLATFORM)) $(stdlib_fs_$(PLATFORM)) $(stdlib_encoding_utf8_$(PLATFORM)) $(stdlib_bufio_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_math_$(PLATFORM)) $(stdlib_types_c_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/os
@$(stdlib_env) $(HAREC) $(HAREFLAGS) -o $@ -Nos \
@@ -1897,15 +1898,16 @@ $(HARECACHE)/os/os-linux.ssa: $(stdlib_os_linux_srcs) $(stdlib_rt) $(stdlib_io_$
# os (+freebsd)
stdlib_os_freebsd_srcs = \
- $(STDLIB)/os/+freebsd/environ.ha \
+ $(STDLIB)/os/+freebsd/platform_environ.ha \
$(STDLIB)/os/+freebsd/exit.ha \
$(STDLIB)/os/+freebsd/dirfdfs.ha \
$(STDLIB)/os/+freebsd/status.ha \
$(STDLIB)/os/+freebsd/stdfd.ha \
$(STDLIB)/os/+freebsd/fs.ha \
+ $(STDLIB)/os/environ.ha \
$(STDLIB)/os/os.ha
-$(HARECACHE)/os/os-freebsd.ssa: $(stdlib_os_freebsd_srcs) $(stdlib_rt) $(stdlib_io_$(PLATFORM)) $(stdlib_strings_$(PLATFORM)) $(stdlib_fs_$(PLATFORM)) $(stdlib_encoding_utf8_$(PLATFORM)) $(stdlib_bytes_$(PLATFORM)) $(stdlib_bufio_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_types_c_$(PLATFORM))
+$(HARECACHE)/os/os-freebsd.ssa: $(stdlib_os_freebsd_srcs) $(stdlib_rt) $(stdlib_io_$(PLATFORM)) $(stdlib_strings_$(PLATFORM)) $(stdlib_fs_$(PLATFORM)) $(stdlib_encoding_utf8_$(PLATFORM)) $(stdlib_bufio_$(PLATFORM)) $(stdlib_errors_$(PLATFORM)) $(stdlib_types_c_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/os
@$(stdlib_env) $(HAREC) $(HAREFLAGS) -o $@ -Nos \
@@ -4332,15 +4334,16 @@ $(TESTCACHE)/net/uri/net_uri-any.ssa: $(testlib_net_uri_any_srcs) $(testlib_rt)
# os (+linux)
testlib_os_linux_srcs = \
$(STDLIB)/os/+linux/dirfdfs.ha \
- $(STDLIB)/os/+linux/environ.ha \
+ $(STDLIB)/os/+linux/platform_environ.ha \
$(STDLIB)/os/+linux/exit+test.ha \
$(STDLIB)/os/+linux/fs.ha \
$(STDLIB)/os/+linux/memory.ha \
$(STDLIB)/os/+linux/status.ha \
$(STDLIB)/os/+linux/stdfd.ha \
+ $(STDLIB)/os/environ.ha \
$(STDLIB)/os/os.ha
-$(TESTCACHE)/os/os-linux.ssa: $(testlib_os_linux_srcs) $(testlib_rt) $(testlib_io_$(PLATFORM)) $(testlib_strings_$(PLATFORM)) $(testlib_fs_$(PLATFORM)) $(testlib_encoding_utf8_$(PLATFORM)) $(testlib_bytes_$(PLATFORM)) $(testlib_bufio_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_math_$(PLATFORM)) $(testlib_types_c_$(PLATFORM))
+$(TESTCACHE)/os/os-linux.ssa: $(testlib_os_linux_srcs) $(testlib_rt) $(testlib_io_$(PLATFORM)) $(testlib_strings_$(PLATFORM)) $(testlib_fs_$(PLATFORM)) $(testlib_encoding_utf8_$(PLATFORM)) $(testlib_bufio_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_math_$(PLATFORM)) $(testlib_types_c_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/os
@$(testlib_env) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nos \
@@ -4348,15 +4351,16 @@ $(TESTCACHE)/os/os-linux.ssa: $(testlib_os_linux_srcs) $(testlib_rt) $(testlib_i
# os (+freebsd)
testlib_os_freebsd_srcs = \
- $(STDLIB)/os/+freebsd/environ.ha \
+ $(STDLIB)/os/+freebsd/platform_environ.ha \
$(STDLIB)/os/+freebsd/exit+test.ha \
$(STDLIB)/os/+freebsd/dirfdfs.ha \
$(STDLIB)/os/+freebsd/status.ha \
$(STDLIB)/os/+freebsd/stdfd.ha \
$(STDLIB)/os/+freebsd/fs.ha \
+ $(STDLIB)/os/environ.ha \
$(STDLIB)/os/os.ha
-$(TESTCACHE)/os/os-freebsd.ssa: $(testlib_os_freebsd_srcs) $(testlib_rt) $(testlib_io_$(PLATFORM)) $(testlib_strings_$(PLATFORM)) $(testlib_fs_$(PLATFORM)) $(testlib_encoding_utf8_$(PLATFORM)) $(testlib_bytes_$(PLATFORM)) $(testlib_bufio_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_types_c_$(PLATFORM))
+$(TESTCACHE)/os/os-freebsd.ssa: $(testlib_os_freebsd_srcs) $(testlib_rt) $(testlib_io_$(PLATFORM)) $(testlib_strings_$(PLATFORM)) $(testlib_fs_$(PLATFORM)) $(testlib_encoding_utf8_$(PLATFORM)) $(testlib_bufio_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_types_c_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/os
@$(testlib_env) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nos \