From 636de88d729b95267762847bbfb3e256fbe0968e Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Thu, 19 Sep 2024 09:56:31 +0800 Subject: [PATCH] implement env (#168) --- README.md | 2 +- src/env/args.v | 38 ++++++++++++++++++++++++++++++++++++++ src/env/args_test.v | 15 +++++++++++++++ src/env/delete.me | 0 src/env/env.v | 40 ++++++++++++++++++++++++++++++++++++++++ src/env/env_test.v | 25 +++++++++++++++++++++++++ 6 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/env/args.v create mode 100644 src/env/args_test.v delete mode 100644 src/env/delete.me create mode 100644 src/env/env.v create mode 100644 src/env/env_test.v diff --git a/README.md b/README.md index 7d9070c2..3b9e6d2c 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ compare against the true GNU coreutils version on the Linux-based tests first. | ✓ | dirname | Strip last file name component | | | du | Estimate file space usage | | ✓ | echo | Print a line of text | -| | env | Run a command in a modified environment | +| ✓ | env | Run a command in a modified environment | | ✓ | expand | Convert tabs to spaces | | ✓ | expr | Evaluate expressions | | ✓ | factor | Print prime factors | diff --git a/src/env/args.v b/src/env/args.v new file mode 100644 index 00000000..c0f24d99 --- /dev/null +++ b/src/env/args.v @@ -0,0 +1,38 @@ +import common + +const app_name = 'env' +const app_description = 'run a program in a modified environment' + +struct EnvArg { + // -0 + nul_terminated bool + + // -u + unsets []string + + // -i + ignore bool + + // command and arguments + cmd_args []string +} + +pub fn new_args(args []string) !EnvArg { + mut fp := common.flag_parser(args) + fp.application(app_name) + fp.description(app_description) + fp.version(common.coreutils_version()) + + nul := fp.bool('null', `0`, false, 'end each output line with NUL, not newline') + unsets := fp.string_multi('unset', `u`, 'remove variable from the environment') + ignore := fp.bool('ignore-environment', `i`, false, 'start with an empty environment') + + cmd_args := fp.finalize() or { return err } + + return EnvArg{ + nul_terminated: nul + unsets: unsets + ignore: ignore + cmd_args: cmd_args + } +} diff --git a/src/env/args_test.v b/src/env/args_test.v new file mode 100644 index 00000000..2e548ca6 --- /dev/null +++ b/src/env/args_test.v @@ -0,0 +1,15 @@ +module main + +fn test_ignore() { + args := new_args(['t', '-i'])! + assert args.ignore == true +} + +fn test_unset() { + args := new_args(['t', '-i', '-u', 'ABC'])! + assert 'ABC' in args.unsets + + multi_args := new_args(['t', '-i', '-u', 'ABC', '-u', 'EFG'])! + assert 'ABC' in multi_args.unsets + assert 'EFG' in multi_args.unsets +} diff --git a/src/env/delete.me b/src/env/delete.me deleted file mode 100644 index e69de29b..00000000 diff --git a/src/env/env.v b/src/env/env.v new file mode 100644 index 00000000..4b4ebfa0 --- /dev/null +++ b/src/env/env.v @@ -0,0 +1,40 @@ +// env - run a program in a modified environment +import os +import maps + +fn main() { + args := new_args(os.args) or { + eprintln(err) + return + } + run(args) +} + +pub fn run(args EnvArg) { + mut envs := if args.ignore { + map[string]string{} + } else { + os.environ() + } + + for key in args.unsets { + envs.delete(key) + } + + if args.cmd_args.len == 0 { + eol := if args.nul_terminated { '\0' } else { '\n' } + for key, val in envs { + print('${key}=${val}' + eol) + } + return + } + + cmd_path := os.find_abs_path_of_executable(args.cmd_args[0]) or { + eprintln(err) + return + } + + os.execve(cmd_path, args.cmd_args[1..], maps.to_array(envs, fn (k string, v string) string { + return '${k}=${v}\0' + })) or { eprintln(err) } +} diff --git a/src/env/env_test.v b/src/env/env_test.v new file mode 100644 index 00000000..85b9cae5 --- /dev/null +++ b/src/env/env_test.v @@ -0,0 +1,25 @@ +import os + +fn test_help() { + result := os.execute_or_panic('${os.quoted_path(@VEXE)} run . -h') + assert result.exit_code == 0 + assert result.output.contains('a modified environment') +} + +fn test_same_env() { + result := os.execute_or_panic('${os.quoted_path(@VEXE)} run . -0') + assert result.exit_code == 0 + + env := os.execute_or_panic('env -0') + assert env.exit_code == 0 + assert env.output == result.output +} + +fn test_same_unset() { + result := os.execute_or_panic('${os.quoted_path(@VEXE)} run . -u PATH') + assert result.exit_code == 0 + + env := os.execute_or_panic('env -u PATH') + assert env.exit_code == 0 + assert env.output == result.output +}