Signed-off-by: Ember Sawady <ecs@d2evs.net>
---
v3: improve docs and check for nul. name validity checks in setenv() are
already handled by the unsetenv() call, like how os::exec:: does it
os/+freebsd/dirfdfs.ha | 1 -
.../{environ.ha => platform_environ.ha} | 39 ++--------------
os/+linux/dirfdfs.ha | 1 -
os/+linux/{environ.ha => platform_environ.ha} | 39 ++--------------
os/environ.ha | 45 +++++++++++++++++++
scripts/gen-stdlib | 10 +++--
stdlib.mk | 20 +++++----
7 files changed, 71 insertions(+), 84 deletions(-)
rename os/+freebsd/{environ.ha => platform_environ.ha} (72%)
rename os/+linux/{environ.ha => platform_environ.ha} (71%)
create mode 100644 os/environ.ha
diff --git a/os/+freebsd/dirfdfs.ha b/os/+freebsd/dirfdfs.ha
index 13903709..50c41c83 100644
--- 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/platform_environ.ha
similarity index 72%
rename from os/+freebsd/environ.ha
rename to os/+freebsd/platform_environ.ha
index 9dd81a94..931a49b2 100644
--- a/os/+freebsd/environ.ha
+++ b/os/+freebsd/platform_environ.ha
@@ -1,7 +1,6 @@
// 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;
@@ -15,7 +14,7 @@ export let args: []str = [];
// syscall if we don't need it.
let args_static: [32]str = [""...];
-@init fn init_environ() void = {
+@init fn args() void = {
if (rt::argc < len(args_static)) {
args = args_static[..rt::argc];
for (let i = 0z; i < rt::argc; i += 1) {
@@ -30,50 +29,20 @@ let args_static: [32]str = [""...];
};
-@fini fn fini_environ() void = {
+@fini fn args() 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)!);
+ let s = c::tostr(rt::envp[i]: *const c::char)!;
+ append(envp, strings::dup(s));
};
return envp;
};
diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha
index c7fcd451..bd658100 100644
--- 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/platform_environ.ha
similarity index 71%
rename from os/+linux/environ.ha
rename to os/+linux/platform_environ.ha
index 6acd0507..c14fb914 100644
--- a/os/+linux/environ.ha
+++ b/os/+linux/platform_environ.ha
@@ -1,7 +1,6 @@
// 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;
@@ -16,7 +15,7 @@ export let args: []str = [];
// syscall if we don't need it.
let args_static: [32]str = [""...];
-@init fn init_environ() void = {
+@init fn args() void = {
if (rt::argc < len(args_static)) {
args = args_static[..rt::argc];
for (let i = 0z; i < rt::argc; i += 1) {
@@ -31,50 +30,20 @@ let args_static: [32]str = [""...];
};
-@fini fn fini_environ() void = {
+@fini fn args() 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)!);
+ let s = c::tostr(rt::envp[i]: *const c::char)!;
+ append(envp, strings::dup(s));
};
return envp;
};
diff --git a/os/environ.ha b/os/environ.ha
new file mode 100644
index 00000000..9551516a
--- /dev/null
+++ 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
index 544af061..460a21c4 100755
--- a/scripts/gen-stdlib
+++ b/scripts/gen-stdlib
@@ -1186,25 +1186,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
index c5c49937..9c8483ab 100644
--- a/stdlib.mk
+++ b/stdlib.mk
@@ -1923,15 +1923,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 \
@@ -1939,15 +1940,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 \
@@ -4416,15 +4418,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 \
@@ -4432,15 +4435,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 \
--
2.41.0