logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git
commit: 958ed3917a9baad63a296dbe1067393afa32fe56
parent bcfadc38f52e4c83ce0c7f93f84983725dcd31d0
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Sun,  1 Sep 2024 16:49:15 +0200

cmd/env: add support for -S option

Diffstat:

Mcmd/env.131++++++++++++++++++++++++++++---
Mcmd/env.c49+++++++++++++++++++++++++++++++++++++++++++++++--
Mtest-cmd/env.sh9+++++++--
3 files changed, 82 insertions(+), 7 deletions(-)

diff --git a/cmd/env.1 b/cmd/env.1 @@ -13,10 +13,18 @@ .Op Fl u Ar name .Op Ar name Ns = Ns Ar value .Op Ar command Op Ar argument... +.Nm +.Op Fl i +.Op Fl u Ar name +.Op Ar name Ns = Ns Ar value +.Fl S Ar command_string .Sh DESCRIPTION .Nm -with no arguments prints the environment, or if specified runs -.Ar command . +if specified runs +.Ar command +or +.Ar command_string , +and otherwise prints the environment. .Pp The environment can be modified via the following options: .Bl -tag -width Ds @@ -35,6 +43,15 @@ into the new environment, it cannot itself contain the .Qq = character. .El +.Pp +The +.Fl S Ar command_string +option allows to split a full command passed as a single argument +similarly to a shell, useful for shebangs where most Unix-likes +do not do argument splitting. +.br +For example: +.Dl #!/usr/bin/env -S perl -w -T .Sh EXIT STATUS If .Ar command @@ -67,6 +84,14 @@ specification. .Pp The .Fl u -flag is an extension known to be present in FreeBSD, NetBSD, GNU coreutils, BusyBox, ... +flag is an extension known to be present in +.Fx , +.Nx , +GNU coreutils, BusyBox, ... +.br +The +.Fl S +flag in an extension known to be present in GNU coreutils and +.Fx . .Sh AUTHORS .An Haelwenn (lanodan) Monnier Aq Mt contact+utils@hacktivis.me diff --git a/cmd/env.c b/cmd/env.c @@ -3,6 +3,8 @@ // SPDX-License-Identifier: MPL-2.0 #define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 800 // wordexp + #include <assert.h> // assert #include <errno.h> // errno #include <stdbool.h> // bool, true, false @@ -10,6 +12,7 @@ #include <stdlib.h> // putenv #include <string.h> // strchr, strerror #include <unistd.h> // getopt, opt* +#include <wordexp.h> // wordexp #ifdef HAS_GETOPT_LONG #include <getopt.h> #endif @@ -44,6 +47,8 @@ int main(int argc, char *argv[]) { bool flag_i = false; + bool flag_S = false; + wordexp_t opt_S; int c = -1; #ifdef HAS_GETOPT_LONG @@ -52,14 +57,15 @@ main(int argc, char *argv[]) static struct option opts[] = { {"ignore-environment", no_argument, 0, 'i'}, {"unset", required_argument, 0, 'u'}, + {"split-string", required_argument, 0, 'S'}, {0, 0, 0, 0}, }; // clang-format on // Need + as first character to get POSIX-style option parsing - while((c = getopt_long(argc, argv, "+:iu:", opts, NULL)) != -1) + while((c = getopt_long(argc, argv, "+:iu:S:", opts, NULL)) != -1) #else - while((c = getopt(argc, argv, ":iu:")) != -1) + while((c = getopt(argc, argv, ":iu:S:")) != -1) #endif { switch(c) @@ -70,6 +76,33 @@ main(int argc, char *argv[]) case 'u': unsetenv(optarg); break; + case 'S': + flag_S = true; + int ret = wordexp(optarg, &opt_S, WRDE_NOCMD); + switch(ret) + { + case 0: + break; + case WRDE_BADCHAR: + fputs("env: Error: wordexpr returned WRDE_BADCHAR\n", stderr); + return 1; + case WRDE_BADVAL: + fputs("env: Error: Undefined shell variable (WRDE_BADVAL)\n", stderr); + return 1; + case WRDE_CMDSUB: + fputs("env: Error: Command substitution forbidden (WRDE_CMDSUB)\n", stderr); + return 1; + case WRDE_NOSPACE: + fputs("env: Error: Out of memory (WRDE_NOSPACE)\n", stderr); + return 1; + case WRDE_SYNTAX: + fputs("env: Error: Syntax Error (WRDE_SYNTAX)\n", stderr); + return 1; + default: + fprintf(stderr, "env: Unknown error %d from wordexp\n", ret); + return 1; + } + break; case ':': fprintf(stderr, "env: Error: Missing operand for option: '-%c'\n", optopt); usage(); @@ -110,6 +143,18 @@ main(int argc, char *argv[]) } } + if(flag_S) + { + errno = 0; + if(execvp(opt_S.we_wordv[0], opt_S.we_wordv) < 0) + { + fprintf(stderr, "env: execvp(\"%s\", ...): %s\n", opt_S.we_wordv[0], strerror(errno)); + + return (errno == ENOENT) ? 127 : 126; + } + assert(false); + } + if(argc < 1) { return do_export(); diff --git a/test-cmd/env.sh b/test-cmd/env.sh @@ -7,8 +7,8 @@ target="${WD}/cmd/env" . "${WD}/test_functions.sh" -plans=9 -gentoo_sandbox && plans=5 +plans=10 +gentoo_sandbox && plans=6 . "${WD}/test-cmd/tap.sh" t exec_true 'true' '' @@ -21,6 +21,11 @@ t --exit=127 enoent '/var/empty/e/no/ent' 'env: execvp("/var/empty/e/no/ent", .. t posix_me_harder "-i FOO=BAR sh -c echo" ' ' +t_args opt_S 'foo +bar +baz +' "-S printf '%s\n' foo bar baz" + if ! gentoo_sandbox then t reset '-i FOO=BAR' 'FOO=BAR