env.c (3841B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // 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
- #include <stdio.h> // puts, fprintf
- #include <stdlib.h> // putenv
- #include <string.h> // strchr, strerror
- #include <unistd.h> // getopt, opt*
- #ifdef HAS_WORDEXP
- #include <wordexp.h>
- #endif
- #ifdef HAS_GETOPT_LONG
- #include <getopt.h>
- #endif
- extern char **environ;
- char *envclear[1];
- static int
- do_export(void)
- {
- int i = 0;
- for(; environ[i] != NULL; i++)
- {
- if(puts(environ[i]) < 0)
- {
- fprintf(stderr, "env: error: Failed writing environ: %s\n", strerror(errno));
- return 1;
- }
- }
- return 0;
- }
- static void
- usage(void)
- {
- fprintf(stderr, "env [-i] [-u key] [key=value ...] [command [args]]\n");
- }
- int
- main(int argc, char *argv[])
- {
- bool flag_i = false;
- #ifdef HAS_WORDEXP
- bool flag_S = false;
- wordexp_t opt_S;
- #define OPTARGS ":iu:S:"
- #else
- #define OPTARGS ":iu:"
- #endif
- int c = -1;
- #ifdef HAS_GETOPT_LONG
- // Strictly for GNUisms compatibility so no long-only options
- // clang-format off
- static struct option opts[] = {
- {"ignore-environment", no_argument, 0, 'i'},
- {"unset", required_argument, 0, 'u'},
- #ifdef HAS_WORDEXP
- {"split-string", required_argument, 0, 'S'},
- #endif
- {0, 0, 0, 0},
- };
- // clang-format on
- // Need + as first character to get POSIX-style option parsing
- while((c = getopt_long(argc, argv, "+" OPTARGS, opts, NULL)) != -1)
- #else
- while((c = getopt(argc, argv, OPTARGS)) != -1)
- #endif
- {
- switch(c)
- {
- case 'i':
- flag_i = true;
- break;
- case 'u':
- unsetenv(optarg);
- break;
- #ifdef HAS_WORDEXP
- 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: error: Unknown error %d from wordexp\n", ret);
- return 1;
- }
- break;
- #endif
- case ':':
- fprintf(stderr, "env: error: Missing operand for option: '-%c'\n", optopt);
- usage();
- return 1;
- case '?':
- fprintf(stderr, "env: error: Unrecognised option: '-%c'\n", optopt);
- usage();
- return 1;
- default:
- assert(false);
- }
- }
- argc -= optind;
- argv += optind;
- if(flag_i)
- {
- environ = envclear;
- envclear[0] = NULL;
- }
- for(; argv[0]; argv++, argc--)
- {
- char *sep = strchr(argv[0], '=');
- if(sep == NULL)
- {
- break;
- }
- *sep = 0;
- sep++;
- if(setenv(argv[0], sep, 1))
- {
- fprintf(stderr,
- "env: error: Failed setting environment variable '%s' to '%s': %s\n",
- argv[0],
- sep,
- strerror(errno));
- return 1;
- }
- }
- #ifdef HAS_WORDEXP
- if(flag_S)
- {
- errno = 0;
- if(execvp(opt_S.we_wordv[0], opt_S.we_wordv) < 0)
- {
- fprintf(
- stderr, "env: error: Failed executing '%s': %s\n", opt_S.we_wordv[0], strerror(errno));
- return (errno == ENOENT) ? 127 : 126;
- }
- assert(false);
- }
- #endif
- if(argc < 1)
- {
- return do_export();
- }
- assert(argv[0]);
- errno = 0;
- if(execvp(argv[0], argv) < 0)
- {
- fprintf(stderr, "env: error: Failed executing '%s': %s\n", argv[0], strerror(errno));
- return (errno == ENOENT) ? 127 : 126;
- }
- assert(false);
- }