logo

utils-std

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

env.c (4332B)


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