logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git/

env.c (3841B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #define _XOPEN_SOURCE 800 // wordexp
  6. #include <assert.h> // assert
  7. #include <errno.h> // errno
  8. #include <stdbool.h> // bool, true, false
  9. #include <stdio.h> // puts, fprintf
  10. #include <stdlib.h> // putenv
  11. #include <string.h> // strchr, strerror
  12. #include <unistd.h> // getopt, opt*
  13. #ifdef HAS_WORDEXP
  14. #include <wordexp.h>
  15. #endif
  16. #ifdef HAS_GETOPT_LONG
  17. #include <getopt.h>
  18. #endif
  19. extern char **environ;
  20. char *envclear[1];
  21. static int
  22. do_export(void)
  23. {
  24. int i = 0;
  25. for(; environ[i] != NULL; i++)
  26. {
  27. if(puts(environ[i]) < 0)
  28. {
  29. fprintf(stderr, "env: error: Failed writing environ: %s\n", strerror(errno));
  30. return 1;
  31. }
  32. }
  33. return 0;
  34. }
  35. static void
  36. usage(void)
  37. {
  38. fprintf(stderr, "env [-i] [-u key] [key=value ...] [command [args]]\n");
  39. }
  40. int
  41. main(int argc, char *argv[])
  42. {
  43. bool flag_i = false;
  44. #ifdef HAS_WORDEXP
  45. bool flag_S = false;
  46. wordexp_t opt_S;
  47. #define OPTARGS ":iu:S:"
  48. #else
  49. #define OPTARGS ":iu:"
  50. #endif
  51. int c = -1;
  52. #ifdef HAS_GETOPT_LONG
  53. // Strictly for GNUisms compatibility so no long-only options
  54. // clang-format off
  55. static struct option opts[] = {
  56. {"ignore-environment", no_argument, 0, 'i'},
  57. {"unset", required_argument, 0, 'u'},
  58. #ifdef HAS_WORDEXP
  59. {"split-string", required_argument, 0, 'S'},
  60. #endif
  61. {0, 0, 0, 0},
  62. };
  63. // clang-format on
  64. // Need + as first character to get POSIX-style option parsing
  65. while((c = getopt_long(argc, argv, "+" OPTARGS, opts, NULL)) != -1)
  66. #else
  67. while((c = getopt(argc, argv, OPTARGS)) != -1)
  68. #endif
  69. {
  70. switch(c)
  71. {
  72. case 'i':
  73. flag_i = true;
  74. break;
  75. case 'u':
  76. unsetenv(optarg);
  77. break;
  78. #ifdef HAS_WORDEXP
  79. case 'S':
  80. flag_S = true;
  81. int ret = wordexp(optarg, &opt_S, WRDE_NOCMD);
  82. switch(ret)
  83. {
  84. case 0:
  85. break;
  86. case WRDE_BADCHAR:
  87. fputs("env: error: wordexpr returned WRDE_BADCHAR\n", stderr);
  88. return 1;
  89. case WRDE_BADVAL:
  90. fputs("env: error: Undefined shell variable (WRDE_BADVAL)\n", stderr);
  91. return 1;
  92. case WRDE_CMDSUB:
  93. fputs("env: error: Command substitution forbidden (WRDE_CMDSUB)\n", stderr);
  94. return 1;
  95. case WRDE_NOSPACE:
  96. fputs("env: error: Out of memory (WRDE_NOSPACE)\n", stderr);
  97. return 1;
  98. case WRDE_SYNTAX:
  99. fputs("env: error: Syntax Error (WRDE_SYNTAX)\n", stderr);
  100. return 1;
  101. default:
  102. fprintf(stderr, "env: error: Unknown error %d from wordexp\n", ret);
  103. return 1;
  104. }
  105. break;
  106. #endif
  107. case ':':
  108. fprintf(stderr, "env: error: Missing operand for option: '-%c'\n", optopt);
  109. usage();
  110. return 1;
  111. case '?':
  112. fprintf(stderr, "env: error: Unrecognised option: '-%c'\n", optopt);
  113. usage();
  114. return 1;
  115. default:
  116. assert(false);
  117. }
  118. }
  119. argc -= optind;
  120. argv += optind;
  121. if(flag_i)
  122. {
  123. environ = envclear;
  124. envclear[0] = NULL;
  125. }
  126. for(; argv[0]; argv++, argc--)
  127. {
  128. char *sep = strchr(argv[0], '=');
  129. if(sep == NULL)
  130. {
  131. break;
  132. }
  133. *sep = 0;
  134. sep++;
  135. if(setenv(argv[0], sep, 1))
  136. {
  137. fprintf(stderr,
  138. "env: error: Failed setting environment variable '%s' to '%s': %s\n",
  139. argv[0],
  140. sep,
  141. strerror(errno));
  142. return 1;
  143. }
  144. }
  145. #ifdef HAS_WORDEXP
  146. if(flag_S)
  147. {
  148. errno = 0;
  149. if(execvp(opt_S.we_wordv[0], opt_S.we_wordv) < 0)
  150. {
  151. fprintf(
  152. stderr, "env: error: Failed executing '%s': %s\n", opt_S.we_wordv[0], strerror(errno));
  153. return (errno == ENOENT) ? 127 : 126;
  154. }
  155. assert(false);
  156. }
  157. #endif
  158. if(argc < 1)
  159. {
  160. return do_export();
  161. }
  162. assert(argv[0]);
  163. errno = 0;
  164. if(execvp(argv[0], argv) < 0)
  165. {
  166. fprintf(stderr, "env: error: Failed executing '%s': %s\n", argv[0], strerror(errno));
  167. return (errno == ENOENT) ? 127 : 126;
  168. }
  169. assert(false);
  170. }