commit: 3df989fa876c2b72bd7714574a17ef4ff706728d
parent b0af37a039de92e89f5739e56a6ff47e3dbe1468
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Wed, 16 Feb 2022 03:57:38 +0100
bin/env: New
Diffstat:
3 files changed, 141 insertions(+), 0 deletions(-)
diff --git a/bin/env.c b/bin/env.c
@@ -0,0 +1,97 @@
+// Collection of Unix tools, comparable to coreutils
+// Copyright 2017-2022 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
+
+#define _POSIX_C_SOURCE 200809L
+#include <stdbool.h> // bool, true, false
+#include <stdio.h> // puts, perror, fprintf
+#include <stdlib.h> // putenv
+#include <string.h> // strchr
+#include <unistd.h> // getopt, opt*
+
+extern char **environ;
+
+int export()
+{
+ int i = 0;
+
+ for(; environ[i] != NULL; i++)
+ {
+ if(puts(environ[i]) < 0)
+ {
+ perror("env: puts");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+usage()
+{
+ fprintf(stderr, "env [-i] [key=value ...] [command [args]]\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ bool flag_i = false;
+
+ while((c = getopt(argc, argv, ":i")) != -1)
+ {
+ switch(c)
+ {
+ case 'i': flag_i = true; break;
+ case ':':
+ fprintf(stderr, "Error: Missing operand for option: '-%c'\n", optopt);
+ usage();
+ return 127;
+ case '?':
+ fprintf(stderr, "Error: Unrecognised option: '-%c'\n", optopt);
+ usage();
+ return 127;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(flag_i)
+ {
+ char *envclear[] = {NULL};
+ environ = envclear;
+ }
+
+ for(; argv[0]; argv++, argc--)
+ {
+ char *sep = strchr(argv[0], '=');
+ if(sep == NULL)
+ {
+ break;
+ }
+
+ *sep = 0;
+ sep++;
+
+ if(setenv(argv[0], sep, 1))
+ {
+ perror("env: setenv:");
+ return 127;
+ }
+ }
+
+ if(argc < 1)
+ {
+ return export();
+ }
+
+ if(execvp(argv[0], argv) < 0)
+ {
+ perror("env: execve");
+ return 127;
+ }
+
+ return 0;
+}
diff --git a/test-bin/Kyuafile b/test-bin/Kyuafile
@@ -11,6 +11,7 @@ atf_test_program{name="cat", required_files=basedir.."/bin/cat"}
atf_test_program{name="date", required_files=basedir.."/bin/date"}
atf_test_program{name="dirname", required_files=basedir.."/bin/dirname"}
atf_test_program{name="echo", required_files=basedir.."/bin/echo"}
+atf_test_program{name="env", required_files=basedir.."/bin/env"}
atf_test_program{name="false", required_files=basedir.."/bin/false"}
atf_test_program{name="humanize", required_files=basedir.."/bin/humanize"}
atf_test_program{name="pwd", required_files=basedir.."/bin/pwd"}
diff --git a/test-bin/env b/test-bin/env
@@ -0,0 +1,43 @@
+#!/usr/bin/env atf-sh
+atf_test_case noargs
+noargs_body() {
+ atf_check -o "inline:FOO=BAR\n" env -i FOO=BAR ../bin/env
+}
+
+atf_test_case badarg
+badarg_body() {
+ atf_check -s not-exit:0 -e "inline:Error: Unrecognised option: '-f'\nenv [-i] [key=value ...] [command [args]]\n" ../bin/env -f
+}
+
+atf_test_case iflag
+iflag_body() {
+ atf_check -o "inline:FOO=BAR\n" ../bin/env -i FOO=BAR ../bin/env
+ atf_check -o "inline:FOO=BAR\n" ../bin/env -i FOO=BAR
+ atf_check -o "not-inline:FOO=BAR\n" ../bin/env FOO=BAR ../bin/env
+ atf_check -o "not-inline:FOO=BAR\n" ../bin/env FOO=BAR
+}
+
+atf_test_case devfull
+devfull_body() {
+ atf_check -s exit:1 -e 'inline:env: puts: No space left on device\n' sh -c '../bin/env >/dev/full'
+}
+
+atf_test_case noutil
+noutil_body() {
+ atf_check -s exit:127 -e 'inline:env: execve: No such file or directory\n' ../bin/env /var/empty/e/no/ent
+}
+
+atf_test_case false
+false_body() {
+ atf_check -s exit:1 ../bin/env false
+}
+
+atf_init_test_cases() {
+ cd "$(atf_get_srcdir)" || exit 1
+ atf_add_test_case noargs
+ atf_add_test_case badarg
+ atf_add_test_case iflag
+ atf_add_test_case devfull
+ atf_add_test_case noutil
+ atf_add_test_case false
+}