maint: go back to using ‘error’
[coreutils.git] / src / env.c
blob58879737ba8c4d286b0fb9c7659d1646832aa792
1 /* env - run a program in a modified environment
2 Copyright (C) 1986-2023 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 /* Richard Mlynarik and David MacKenzie */
19 #include <config.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <getopt.h>
23 #include <c-ctype.h>
24 #include <signal.h>
26 #include "system.h"
27 #include "idx.h"
28 #include "operand2sig.h"
29 #include "quote.h"
30 #include "sig2str.h"
32 /* The official name of this program (e.g., no 'g' prefix). */
33 #define PROGRAM_NAME "env"
35 #define AUTHORS \
36 proper_name ("Richard Mlynarik"), \
37 proper_name ("David MacKenzie"), \
38 proper_name ("Assaf Gordon")
40 /* Array of envvars to unset. */
41 static char const **usvars;
42 static size_t usvars_alloc;
43 static idx_t usvars_used;
45 /* Annotate the output with extra info to aid the user. */
46 static bool dev_debug;
48 /* Buffer and length of extracted envvars in -S strings. */
49 static char *varname;
50 static idx_t vnlen;
52 /* Possible actions on each signal. */
53 enum SIGNAL_MODE {
54 UNCHANGED = 0,
55 DEFAULT, /* Set to default handler (SIG_DFL). */
56 DEFAULT_NOERR, /* Ditto, but ignore sigaction(2) errors. */
57 IGNORE, /* Set to ignore (SIG_IGN). */
58 IGNORE_NOERR /* Ditto, but ignore sigaction(2) errors. */
60 static enum SIGNAL_MODE *signals;
62 /* Set of signals to block. */
63 static sigset_t block_signals;
65 /* Set of signals to unblock. */
66 static sigset_t unblock_signals;
68 /* Whether signal mask adjustment requested. */
69 static bool sig_mask_changed;
71 /* Whether to list non default handling. */
72 static bool report_signal_handling;
74 /* The isspace characters in the C locale. */
75 #define C_ISSPACE_CHARS " \t\n\v\f\r"
77 static char const shortopts[] = "+C:iS:u:v0" C_ISSPACE_CHARS;
79 /* For long options that have no equivalent short option, use a
80 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
81 enum
83 DEFAULT_SIGNAL_OPTION = CHAR_MAX + 1,
84 IGNORE_SIGNAL_OPTION,
85 BLOCK_SIGNAL_OPTION,
86 LIST_SIGNAL_HANDLING_OPTION,
89 static struct option const longopts[] =
91 {"ignore-environment", no_argument, nullptr, 'i'},
92 {"null", no_argument, nullptr, '0'},
93 {"unset", required_argument, nullptr, 'u'},
94 {"chdir", required_argument, nullptr, 'C'},
95 {"default-signal", optional_argument, nullptr, DEFAULT_SIGNAL_OPTION},
96 {"ignore-signal", optional_argument, nullptr, IGNORE_SIGNAL_OPTION},
97 {"block-signal", optional_argument, nullptr, BLOCK_SIGNAL_OPTION},
98 {"list-signal-handling", no_argument, nullptr, LIST_SIGNAL_HANDLING_OPTION},
99 {"debug", no_argument, nullptr, 'v'},
100 {"split-string", required_argument, nullptr, 'S'},
101 {GETOPT_HELP_OPTION_DECL},
102 {GETOPT_VERSION_OPTION_DECL},
103 {nullptr, 0, nullptr, 0}
106 void
107 usage (int status)
109 if (status != EXIT_SUCCESS)
110 emit_try_help ();
111 else
113 printf (_("\
114 Usage: %s [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]\n"),
115 program_name);
116 fputs (_("\
117 Set each NAME to VALUE in the environment and run COMMAND.\n\
118 "), stdout);
120 emit_mandatory_arg_note ();
122 fputs (_("\
123 -i, --ignore-environment start with an empty environment\n\
124 -0, --null end each output line with NUL, not newline\n\
125 -u, --unset=NAME remove variable from the environment\n\
126 "), stdout);
127 fputs (_("\
128 -C, --chdir=DIR change working directory to DIR\n\
129 "), stdout);
130 fputs (_("\
131 -S, --split-string=S process and split S into separate arguments;\n\
132 used to pass multiple arguments on shebang lines\n\
133 "), stdout);
134 fputs (_("\
135 --block-signal[=SIG] block delivery of SIG signal(s) to COMMAND\n\
136 "), stdout);
137 fputs (_("\
138 --default-signal[=SIG] reset handling of SIG signal(s) to the default\n\
139 "), stdout);
140 fputs (_("\
141 --ignore-signal[=SIG] set handling of SIG signal(s) to do nothing\n\
142 "), stdout);
143 fputs (_("\
144 --list-signal-handling list non default signal handling to stderr\n\
145 "), stdout);
146 fputs (_("\
147 -v, --debug print verbose information for each processing step\n\
148 "), stdout);
149 fputs (HELP_OPTION_DESCRIPTION, stdout);
150 fputs (VERSION_OPTION_DESCRIPTION, stdout);
151 fputs (_("\
153 A mere - implies -i. If no COMMAND, print the resulting environment.\n\
154 "), stdout);
155 fputs (_("\
157 SIG may be a signal name like 'PIPE', or a signal number like '13'.\n\
158 Without SIG, all known signals are included. Multiple signals can be\n\
159 comma-separated. An empty SIG argument is a no-op.\n\
160 "), stdout);
161 emit_exec_status (PROGRAM_NAME);
162 emit_ancillary_info (PROGRAM_NAME);
164 exit (status);
167 static void
168 append_unset_var (char const *var)
170 if (usvars_used == usvars_alloc)
171 usvars = x2nrealloc (usvars, &usvars_alloc, sizeof *usvars);
172 usvars[usvars_used++] = var;
175 static void
176 unset_envvars (void)
178 for (idx_t i = 0; i < usvars_used; ++i)
180 devmsg ("unset: %s\n", usvars[i]);
182 if (unsetenv (usvars[i]))
183 error (EXIT_CANCELED, errno, _("cannot unset %s"),
184 quote (usvars[i]));
188 /* Return a pointer to the end of a valid ${VARNAME} string, or nullptr.
189 'str' should point to the '$' character.
190 First letter in VARNAME must be alpha or underscore,
191 rest of letters are alnum or underscore.
192 Any other character is an error. */
193 ATTRIBUTE_PURE
194 static char const *
195 scan_varname (char const *str)
197 if (str[1] == '{' && (c_isalpha (str[2]) || str[2] == '_'))
199 char const *end = str + 3;
200 while (c_isalnum (*end) || *end == '_')
201 ++end;
202 if (*end == '}')
203 return end;
206 return nullptr;
209 /* Return a pointer to a static buffer containing the VARNAME as
210 extracted from a '${VARNAME}' string.
211 The returned string will be NUL terminated.
212 The returned pointer should not be freed.
213 Return nullptr if not a valid ${VARNAME} syntax. */
214 static char *
215 extract_varname (char const *str)
217 idx_t i;
218 char const *p;
220 p = scan_varname (str);
221 if (!p)
222 return nullptr;
224 /* -2 and +2 (below) account for the '${' prefix. */
225 i = p - str - 2;
227 if (i >= vnlen)
229 vnlen = i + 1;
230 varname = xrealloc (varname, vnlen);
233 memcpy (varname, str + 2, i);
234 varname[i] = 0;
236 return varname;
239 /* Temporary buffer used by --split-string processing. */
240 struct splitbuf
242 /* Buffer address, arg count, and half the number of elements in the buffer.
243 ARGC and ARGV are as in 'main', and ARGC + 1 <= HALF_ALLOC so
244 that the upper half of ARGV can be used for string contents.
245 This may waste up to half the space but keeps the code simple,
246 which is better for this rarely-used but security-sensitive code.
248 ARGV[0] is not initialized; that is the caller's responsibility
249 after finalization.
251 During assembly, ARGV[I] (where 0 < I < ARGC) contains the offset
252 of the Ith string (relative to ARGV + HALF_ALLOC), so that
253 reallocating ARGV does not change the validity of its contents.
254 The integer offset is cast to char * during assembly, and is
255 converted to a true char * pointer on finalization.
257 During assembly, ARGV[ARGC] contains the offset of the first
258 unused string byte (relative to ARGV + HALF_ALLOC). */
259 char **argv;
260 int argc;
261 idx_t half_alloc;
263 /* The number of extra argv slots to keep room for. */
264 int extra_argc;
266 /* Whether processing should act as if the most recent character
267 seen was a separator. */
268 bool sep;
271 /* Expand SS so that it has at least one more argv slot and at least
272 one more string byte. */
273 static void
274 splitbuf_grow (struct splitbuf *ss)
276 idx_t old_half_alloc = ss->half_alloc;
277 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
278 ss->argv = xpalloc (ss->argv, &ss->half_alloc, 1,
279 MIN (INT_MAX, IDX_MAX), 2 * sizeof *ss->argv);
280 memmove (ss->argv + ss->half_alloc, ss->argv + old_half_alloc, string_bytes);
283 /* In SS, append C to the last string. */
284 static void
285 splitbuf_append_byte (struct splitbuf *ss, char c)
287 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
288 if (ss->half_alloc * sizeof *ss->argv <= string_bytes)
289 splitbuf_grow (ss);
290 ((char *) (ss->argv + ss->half_alloc))[string_bytes] = c;
291 ss->argv[ss->argc] = (char *) (intptr_t) (string_bytes + 1);
294 /* If SS's most recent character was a separator, finish off its
295 previous argument and start a new one. */
296 static void
297 check_start_new_arg (struct splitbuf *ss)
299 if (ss->sep)
301 splitbuf_append_byte (ss, '\0');
302 int argc = ss->argc;
303 if (ss->half_alloc <= argc + ss->extra_argc + 1)
304 splitbuf_grow (ss);
305 ss->argv[argc + 1] = ss->argv[argc];
306 ss->argc = argc + 1;
307 ss->sep = false;
311 /* All additions to SS have been made. Convert its offsets to pointers,
312 and return the resulting argument vector. */
313 static char **
314 splitbuf_finishup (struct splitbuf *ss)
316 int argc = ss->argc;
317 char **argv = ss->argv;
318 char *stringbase = (char *) (ss->argv + ss->half_alloc);
319 for (int i = 1; i < argc; i++)
320 argv[i] = stringbase + (intptr_t) argv[i];
321 return argv;
324 /* Return a newly-allocated argv-like array,
325 by parsing and splitting the input 'str'.
327 'extra_argc' is the number of additional elements to allocate
328 in the array (on top of the number of args required to split 'str').
330 Store into *argc the number of arguments found (plus 1 for
331 the program name).
333 Example:
334 int argc;
335 char **argv = build_argv ("A=B uname -k', 3, &argc);
336 Results in:
337 argc = 4
338 argv[0] = [not initialized]
339 argv[1] = "A=B"
340 argv[2] = "uname"
341 argv[3] = "-k"
342 argv[4,5,6,7] = [allocated due to extra_argc + 1, but not initialized]
344 To free allocated memory:
345 free (argv);
346 However, 'env' does not free since it's about to exec or exit anyway
347 and the complexity of keeping track of the storage that may have been
348 allocated via multiple calls to build_argv is not worth the hassle. */
349 static char **
350 build_argv (char const *str, int extra_argc, int *argc)
352 bool dq = false, sq = false;
353 struct splitbuf ss;
354 ss.argv = xnmalloc (extra_argc + 2, 2 * sizeof *ss.argv);
355 ss.argc = 1;
356 ss.half_alloc = extra_argc + 2;
357 ss.extra_argc = extra_argc;
358 ss.sep = true;
359 ss.argv[ss.argc] = 0;
361 /* In the following loop,
362 'break' causes the character 'newc' to be added to *dest,
363 'continue' skips the character. */
364 while (*str)
366 char newc = *str; /* Default: add the next character. */
368 switch (*str)
370 case '\'':
371 if (dq)
372 break;
373 sq = !sq;
374 check_start_new_arg (&ss);
375 ++str;
376 continue;
378 case '"':
379 if (sq)
380 break;
381 dq = !dq;
382 check_start_new_arg (&ss);
383 ++str;
384 continue;
386 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
387 /* Start a new argument if outside quotes. */
388 if (sq || dq)
389 break;
390 ss.sep = true;
391 str += strspn (str, C_ISSPACE_CHARS);
392 continue;
394 case '#':
395 if (!ss.sep)
396 break;
397 goto eos; /* '#' as first char terminates the string. */
399 case '\\':
400 /* Backslash inside single-quotes is not special, except \\
401 and \'. */
402 if (sq && str[1] != '\\' && str[1] != '\'')
403 break;
405 /* Skip the backslash and examine the next character. */
406 newc = *++str;
407 switch (newc)
409 case '"': case '#': case '$': case '\'': case '\\':
410 /* Pass escaped character as-is. */
411 break;
413 case '_':
414 if (!dq)
416 ++str; /* '\_' outside double-quotes is arg separator. */
417 ss.sep = true;
418 continue;
420 newc = ' '; /* '\_' inside double-quotes is space. */
421 break;
423 case 'c':
424 if (dq)
425 error (EXIT_CANCELED, 0,
426 _("'\\c' must not appear in double-quoted -S string"));
427 goto eos; /* '\c' terminates the string. */
429 case 'f': newc = '\f'; break;
430 case 'n': newc = '\n'; break;
431 case 'r': newc = '\r'; break;
432 case 't': newc = '\t'; break;
433 case 'v': newc = '\v'; break;
435 case '\0':
436 error (EXIT_CANCELED, 0,
437 _("invalid backslash at end of string in -S"));
439 default:
440 error (EXIT_CANCELED, 0,
441 _("invalid sequence '\\%c' in -S"), newc);
443 break;
445 case '$':
446 /* ${VARNAME} are not expanded inside single-quotes. */
447 if (sq)
448 break;
450 /* Store the ${VARNAME} value. */
452 char *n = extract_varname (str);
453 if (!n)
454 error (EXIT_CANCELED, 0,
455 _("only ${VARNAME} expansion is supported, error at: %s"),
456 str);
458 char *v = getenv (n);
459 if (v)
461 check_start_new_arg (&ss);
462 devmsg ("expanding ${%s} into %s\n", n, quote (v));
463 for (; *v; v++)
464 splitbuf_append_byte (&ss, *v);
466 else
467 devmsg ("replacing ${%s} with null string\n", n);
469 str = strchr (str, '}') + 1;
470 continue;
474 check_start_new_arg (&ss);
475 splitbuf_append_byte (&ss, newc);
476 ++str;
479 if (dq || sq)
480 error (EXIT_CANCELED, 0, _("no terminating quote in -S string"));
482 eos:
483 splitbuf_append_byte (&ss, '\0');
484 *argc = ss.argc;
485 return splitbuf_finishup (&ss);
488 /* Process an "-S" string and create the corresponding argv array.
489 Update the given argc/argv parameters with the new argv.
491 Example: if executed as:
492 $ env -S"-i -C/tmp A=B" foo bar
493 The input argv is:
494 argv[0] = "env"
495 argv[1] = "-S-i -C/tmp A=B"
496 argv[2] = "foo"
497 argv[3] = "bar"
498 argv[4] = nullptr
499 This function will modify argv to be:
500 argv[0] = "env"
501 argv[1] = "-i"
502 argv[2] = "-C/tmp"
503 argv[3] = "A=B"
504 argv[4] = "foo"
505 argv[5] = "bar"
506 argv[6] = nullptr
507 argc will be updated from 4 to 6.
508 optind will be reset to 0 to force getopt_long to rescan all arguments. */
509 static void
510 parse_split_string (char const *str, int *orig_optind,
511 int *orig_argc, char ***orig_argv)
513 int extra_argc = *orig_argc - *orig_optind, newargc;
514 char **newargv = build_argv (str, extra_argc, &newargc);
516 /* Restore argv[0] - the 'env' executable name. */
517 *newargv = (*orig_argv)[0];
519 /* Print parsed arguments. */
520 if (dev_debug && 1 < newargc)
522 devmsg ("split -S: %s\n", quote (str));
523 devmsg (" into: %s\n", quote (newargv[1]));
524 for (int i = 2; i < newargc; i++)
525 devmsg (" & %s\n", quote (newargv[i]));
528 /* Add remaining arguments and terminating null from the original
529 command line. */
530 memcpy (newargv + newargc, *orig_argv + *orig_optind,
531 (extra_argc + 1) * sizeof *newargv);
533 /* Set new values for original getopt variables. */
534 *orig_argc = newargc + extra_argc;
535 *orig_argv = newargv;
536 *orig_optind = 0; /* Tell getopt to restart from first argument. */
539 static void
540 parse_signal_action_params (char const *optarg, bool set_default)
542 char signame[SIG2STR_MAX];
543 char *opt_sig;
544 char *optarg_writable;
546 if (! optarg)
548 /* Without an argument, reset all signals.
549 Some signals cannot be set to ignore or default (e.g., SIGKILL,
550 SIGSTOP on most OSes, and SIGCONT on AIX.) - so ignore errors. */
551 for (int i = 1 ; i <= SIGNUM_BOUND; i++)
552 if (sig2str (i, signame) == 0)
553 signals[i] = set_default ? DEFAULT_NOERR : IGNORE_NOERR;
554 return;
557 optarg_writable = xstrdup (optarg);
559 opt_sig = strtok (optarg_writable, ",");
560 while (opt_sig)
562 int signum = operand2sig (opt_sig, signame);
563 /* operand2sig accepts signal 0 (EXIT) - but we reject it. */
564 if (signum == 0)
565 error (0, 0, _("%s: invalid signal"), quote (opt_sig));
566 if (signum <= 0)
567 usage (exit_failure);
569 signals[signum] = set_default ? DEFAULT : IGNORE;
571 opt_sig = strtok (nullptr, ",");
574 free (optarg_writable);
577 static void
578 reset_signal_handlers (void)
580 for (int i = 1; i <= SIGNUM_BOUND; i++)
582 struct sigaction act;
584 if (signals[i] == UNCHANGED)
585 continue;
587 bool ignore_errors = (signals[i] == DEFAULT_NOERR
588 || signals[i] == IGNORE_NOERR);
590 bool set_to_default = (signals[i] == DEFAULT
591 || signals[i] == DEFAULT_NOERR);
593 int sig_err = sigaction (i, nullptr, &act);
595 if (sig_err && !ignore_errors)
596 error (EXIT_CANCELED, errno,
597 _("failed to get signal action for signal %d"), i);
599 if (! sig_err)
601 act.sa_handler = set_to_default ? SIG_DFL : SIG_IGN;
602 sig_err = sigaction (i, &act, nullptr);
603 if (sig_err && !ignore_errors)
604 error (EXIT_CANCELED, errno,
605 _("failed to set signal action for signal %d"), i);
608 if (dev_debug)
610 char signame[SIG2STR_MAX];
611 sig2str (i, signame);
612 devmsg ("Reset signal %s (%d) to %s%s\n",
613 signame, i,
614 set_to_default ? "DEFAULT" : "IGNORE",
615 sig_err ? " (failure ignored)" : "");
621 static void
622 parse_block_signal_params (char const *optarg, bool block)
624 char signame[SIG2STR_MAX];
625 char *opt_sig;
626 char *optarg_writable;
628 if (! optarg)
630 /* Without an argument, reset all signals. */
631 sigfillset (block ? &block_signals : &unblock_signals);
632 sigemptyset (block ? &unblock_signals : &block_signals);
634 else if (! sig_mask_changed)
636 /* Initialize the sets. */
637 sigemptyset (&block_signals);
638 sigemptyset (&unblock_signals);
641 sig_mask_changed = true;
643 if (! optarg)
644 return;
646 optarg_writable = xstrdup (optarg);
648 opt_sig = strtok (optarg_writable, ",");
649 while (opt_sig)
651 int signum = operand2sig (opt_sig, signame);
652 /* operand2sig accepts signal 0 (EXIT) - but we reject it. */
653 if (signum == 0)
654 error (0, 0, _("%s: invalid signal"), quote (opt_sig));
655 if (signum <= 0)
656 usage (exit_failure);
658 sigaddset (block ? &block_signals : &unblock_signals, signum);
659 sigdelset (block ? &unblock_signals : &block_signals, signum);
661 opt_sig = strtok (nullptr, ",");
664 free (optarg_writable);
667 static void
668 set_signal_proc_mask (void)
670 /* Get the existing signal mask */
671 sigset_t set;
672 char const *debug_act;
674 sigemptyset (&set);
676 if (sigprocmask (0, nullptr, &set))
677 error (EXIT_CANCELED, errno, _("failed to get signal process mask"));
679 for (int i = 1; i <= SIGNUM_BOUND; i++)
681 if (sigismember (&block_signals, i))
683 sigaddset (&set, i);
684 debug_act = "BLOCK";
686 else if (sigismember (&unblock_signals, i))
688 sigdelset (&set, i);
689 debug_act = "UNBLOCK";
691 else
693 debug_act = nullptr;
696 if (dev_debug && debug_act)
698 char signame[SIG2STR_MAX];
699 sig2str (i, signame);
700 devmsg ("signal %s (%d) mask set to %s\n",
701 signame, i, debug_act);
705 if (sigprocmask (SIG_SETMASK, &set, nullptr))
706 error (EXIT_CANCELED, errno, _("failed to set signal process mask"));
709 static void
710 list_signal_handling (void)
712 sigset_t set;
713 char signame[SIG2STR_MAX];
715 sigemptyset (&set);
716 if (sigprocmask (0, nullptr, &set))
717 error (EXIT_CANCELED, errno, _("failed to get signal process mask"));
719 for (int i = 1; i <= SIGNUM_BOUND; i++)
721 struct sigaction act;
722 if (sigaction (i, nullptr, &act))
723 continue;
725 char const *ignored = act.sa_handler == SIG_IGN ? "IGNORE" : "";
726 char const *blocked = sigismember (&set, i) ? "BLOCK" : "";
727 char const *connect = *ignored && *blocked ? "," : "";
729 if (! *ignored && ! *blocked)
730 continue;
732 sig2str (i, signame);
733 fprintf (stderr, "%-10s (%2d): %s%s%s\n", signame, i,
734 blocked, connect, ignored);
738 static void
739 initialize_signals (void)
741 signals = xmalloc ((sizeof *signals) * (SIGNUM_BOUND + 1));
743 for (int i = 0 ; i <= SIGNUM_BOUND; i++)
744 signals[i] = UNCHANGED;
746 return;
750 main (int argc, char **argv)
752 int optc;
753 bool ignore_environment = false;
754 bool opt_nul_terminate_output = false;
755 char const *newdir = nullptr;
757 initialize_main (&argc, &argv);
758 set_program_name (argv[0]);
759 setlocale (LC_ALL, "");
760 bindtextdomain (PACKAGE, LOCALEDIR);
761 textdomain (PACKAGE);
763 initialize_exit_failure (EXIT_CANCELED);
764 atexit (close_stdout);
766 initialize_signals ();
768 while ((optc = getopt_long (argc, argv, shortopts, longopts, nullptr)) != -1)
770 switch (optc)
772 case 'i':
773 ignore_environment = true;
774 break;
775 case 'u':
776 append_unset_var (optarg);
777 break;
778 case 'v':
779 dev_debug = true;
780 break;
781 case '0':
782 opt_nul_terminate_output = true;
783 break;
784 case DEFAULT_SIGNAL_OPTION:
785 parse_signal_action_params (optarg, true);
786 parse_block_signal_params (optarg, false);
787 break;
788 case IGNORE_SIGNAL_OPTION:
789 parse_signal_action_params (optarg, false);
790 break;
791 case BLOCK_SIGNAL_OPTION:
792 parse_block_signal_params (optarg, true);
793 break;
794 case LIST_SIGNAL_HANDLING_OPTION:
795 report_signal_handling = true;
796 break;
797 case 'C':
798 newdir = optarg;
799 break;
800 case 'S':
801 parse_split_string (optarg, &optind, &argc, &argv);
802 break;
803 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
804 /* These are undocumented options. Attempt to detect
805 incorrect shebang usage with extraneous space, e.g.:
806 #!/usr/bin/env -i command
807 In which case argv[1] == "-i command". */
808 error (0, 0, _("invalid option -- '%c'"), optc);
809 error (0, 0, _("use -[v]S to pass options in shebang lines"));
810 usage (EXIT_CANCELED);
812 case_GETOPT_HELP_CHAR;
813 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
814 default:
815 usage (EXIT_CANCELED);
819 if (optind < argc && STREQ (argv[optind], "-"))
821 ignore_environment = true;
822 ++optind;
825 if (ignore_environment)
827 devmsg ("cleaning environ\n");
828 static char *dummy_environ[] = { nullptr };
829 environ = dummy_environ;
831 else
832 unset_envvars ();
834 char *eq;
835 while (optind < argc && (eq = strchr (argv[optind], '=')))
837 devmsg ("setenv: %s\n", argv[optind]);
839 if (putenv (argv[optind]))
841 *eq = '\0';
842 error (EXIT_CANCELED, errno, _("cannot set %s"),
843 quote (argv[optind]));
845 optind++;
848 bool program_specified = optind < argc;
850 if (opt_nul_terminate_output && program_specified)
852 error (0, 0, _("cannot specify --null (-0) with command"));
853 usage (EXIT_CANCELED);
856 if (newdir && ! program_specified)
858 error (0, 0, _("must specify command with --chdir (-C)"));
859 usage (EXIT_CANCELED);
862 if (! program_specified)
864 /* Print the environment and exit. */
865 char *const *e = environ;
866 while (*e)
867 printf ("%s%c", *e++, opt_nul_terminate_output ? '\0' : '\n');
868 return EXIT_SUCCESS;
871 reset_signal_handlers ();
872 if (sig_mask_changed)
873 set_signal_proc_mask ();
875 if (report_signal_handling)
876 list_signal_handling ();
878 if (newdir)
880 devmsg ("chdir: %s\n", quoteaf (newdir));
882 if (chdir (newdir) != 0)
883 error (EXIT_CANCELED, errno, _("cannot change directory to %s"),
884 quoteaf (newdir));
887 if (dev_debug)
889 devmsg ("executing: %s\n", argv[optind]);
890 for (int i=optind; i<argc; ++i)
891 devmsg (" arg[%d]= %s\n", i-optind, quote (argv[i]));
894 execvp (argv[optind], &argv[optind]);
896 int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
897 error (0, errno, "%s", quote (argv[optind]));
899 if (exit_status == EXIT_ENOENT && strpbrk (argv[optind], C_ISSPACE_CHARS))
900 error (0, 0, _("use -[v]S to pass options in shebang lines"));
902 main_exit (exit_status);