logo

utils-std

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

env.c (4226B)


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