logo

utils-std

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

env.c (3507B)


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