dd: synchronize output after write errors
[coreutils.git] / src / env.c
blobabcd0a7897b0ea3b1223860801b3ccf82d82ac39
1 /* env - run a program in a modified environment
2 Copyright (C) 1986-2022 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 "die.h"
28 #include "error.h"
29 #include "idx.h"
30 #include "operand2sig.h"
31 #include "quote.h"
32 #include "sig2str.h"
34 /* The official name of this program (e.g., no 'g' prefix). */
35 #define PROGRAM_NAME "env"
37 #define AUTHORS \
38 proper_name ("Richard Mlynarik"), \
39 proper_name ("David MacKenzie"), \
40 proper_name ("Assaf Gordon")
42 /* Array of envvars to unset. */
43 static char const **usvars;
44 static size_t usvars_alloc;
45 static idx_t usvars_used;
47 /* Annotate the output with extra info to aid the user. */
48 static bool dev_debug;
50 /* Buffer and length of extracted envvars in -S strings. */
51 static char *varname;
52 static idx_t vnlen;
54 /* Possible actions on each signal. */
55 enum SIGNAL_MODE {
56 UNCHANGED = 0,
57 DEFAULT, /* Set to default handler (SIG_DFL). */
58 DEFAULT_NOERR, /* Ditto, but ignore sigaction(2) errors. */
59 IGNORE, /* Set to ignore (SIG_IGN). */
60 IGNORE_NOERR /* Ditto, but ignore sigaction(2) errors. */
62 static enum SIGNAL_MODE *signals;
64 /* Set of signals to block. */
65 static sigset_t block_signals;
67 /* Set of signals to unblock. */
68 static sigset_t unblock_signals;
70 /* Whether signal mask adjustment requested. */
71 static bool sig_mask_changed;
73 /* Whether to list non default handling. */
74 static bool report_signal_handling;
76 /* The isspace characters in the C locale. */
77 #define C_ISSPACE_CHARS " \t\n\v\f\r"
79 static char const shortopts[] = "+C:iS:u:v0" C_ISSPACE_CHARS;
81 /* For long options that have no equivalent short option, use a
82 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
83 enum
85 DEFAULT_SIGNAL_OPTION = CHAR_MAX + 1,
86 IGNORE_SIGNAL_OPTION,
87 BLOCK_SIGNAL_OPTION,
88 LIST_SIGNAL_HANDLING_OPTION,
91 static struct option const longopts[] =
93 {"ignore-environment", no_argument, NULL, 'i'},
94 {"null", no_argument, NULL, '0'},
95 {"unset", required_argument, NULL, 'u'},
96 {"chdir", required_argument, NULL, 'C'},
97 {"default-signal", optional_argument, NULL, DEFAULT_SIGNAL_OPTION},
98 {"ignore-signal", optional_argument, NULL, IGNORE_SIGNAL_OPTION},
99 {"block-signal", optional_argument, NULL, BLOCK_SIGNAL_OPTION},
100 {"list-signal-handling", no_argument, NULL, LIST_SIGNAL_HANDLING_OPTION},
101 {"debug", no_argument, NULL, 'v'},
102 {"split-string", required_argument, NULL, 'S'},
103 {GETOPT_HELP_OPTION_DECL},
104 {GETOPT_VERSION_OPTION_DECL},
105 {NULL, 0, NULL, 0}
108 void
109 usage (int status)
111 if (status != EXIT_SUCCESS)
112 emit_try_help ();
113 else
115 printf (_("\
116 Usage: %s [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]\n"),
117 program_name);
118 fputs (_("\
119 Set each NAME to VALUE in the environment and run COMMAND.\n\
120 "), stdout);
122 emit_mandatory_arg_note ();
124 fputs (_("\
125 -i, --ignore-environment start with an empty environment\n\
126 -0, --null end each output line with NUL, not newline\n\
127 -u, --unset=NAME remove variable from the environment\n\
128 "), stdout);
129 fputs (_("\
130 -C, --chdir=DIR change working directory to DIR\n\
131 "), stdout);
132 fputs (_("\
133 -S, --split-string=S process and split S into separate arguments;\n\
134 used to pass multiple arguments on shebang lines\n\
135 "), stdout);
136 fputs (_("\
137 --block-signal[=SIG] block delivery of SIG signal(s) to COMMAND\n\
138 "), stdout);
139 fputs (_("\
140 --default-signal[=SIG] reset handling of SIG signal(s) to the default\n\
141 "), stdout);
142 fputs (_("\
143 --ignore-signal[=SIG] set handling of SIG signal(s) to do nothing\n\
144 "), stdout);
145 fputs (_("\
146 --list-signal-handling list non default signal handling to stderr\n\
147 "), stdout);
148 fputs (_("\
149 -v, --debug print verbose information for each processing step\n\
150 "), stdout);
151 fputs (HELP_OPTION_DESCRIPTION, stdout);
152 fputs (VERSION_OPTION_DESCRIPTION, stdout);
153 fputs (_("\
155 A mere - implies -i. If no COMMAND, print the resulting environment.\n\
156 "), stdout);
157 fputs (_("\
159 SIG may be a signal name like 'PIPE', or a signal number like '13'.\n\
160 Without SIG, all known signals are included. Multiple signals can be\n\
161 comma-separated.\n\
162 "), stdout);
163 emit_ancillary_info (PROGRAM_NAME);
165 exit (status);
168 static void
169 append_unset_var (char const *var)
171 if (usvars_used == usvars_alloc)
172 usvars = x2nrealloc (usvars, &usvars_alloc, sizeof *usvars);
173 usvars[usvars_used++] = var;
176 static void
177 unset_envvars (void)
179 for (idx_t i = 0; i < usvars_used; ++i)
181 devmsg ("unset: %s\n", usvars[i]);
183 if (unsetenv (usvars[i]))
184 die (EXIT_CANCELED, errno, _("cannot unset %s"),
185 quote (usvars[i]));
188 IF_LINT (free (usvars));
189 IF_LINT (usvars = NULL);
190 IF_LINT (usvars_used = 0);
191 IF_LINT (usvars_alloc = 0);
194 /* Return a pointer to the end of a valid ${VARNAME} string, or NULL.
195 'str' should point to the '$' character.
196 First letter in VARNAME must be alpha or underscore,
197 rest of letters are alnum or underscore.
198 Any other character is an error. */
199 ATTRIBUTE_PURE
200 static char const *
201 scan_varname (char const *str)
203 if (str[1] == '{' && (c_isalpha (str[2]) || str[2] == '_'))
205 char const *end = str + 3;
206 while (c_isalnum (*end) || *end == '_')
207 ++end;
208 if (*end == '}')
209 return end;
212 return NULL;
215 /* Return a pointer to a static buffer containing the VARNAME as
216 extracted from a '${VARNAME}' string.
217 The returned string will be NUL terminated.
218 The returned pointer should not be freed.
219 Return NULL if not a valid ${VARNAME} syntax. */
220 static char *
221 extract_varname (char const *str)
223 idx_t i;
224 char const *p;
226 p = scan_varname (str);
227 if (!p)
228 return NULL;
230 /* -2 and +2 (below) account for the '${' prefix. */
231 i = p - str - 2;
233 if (i >= vnlen)
235 vnlen = i + 1;
236 varname = xrealloc (varname, vnlen);
239 memcpy (varname, str + 2, i);
240 varname[i] = 0;
242 return varname;
245 /* Temporary buffer used by --split-string processing. */
246 struct splitbuf
248 /* Buffer address, arg count, and half the number of elements in the buffer.
249 ARGC and ARGV are as in 'main', and ARGC + 1 <= HALF_ALLOC so
250 that the upper half of ARGV can be used for string contents.
251 This may waste up to half the space but keeps the code simple,
252 which is better for this rarely-used but security-sensitive code.
254 ARGV[0] is not initialized; that is the caller's responsibility
255 after finalization.
257 During assembly, ARGV[I] (where 0 < I < ARGC) contains the offset
258 of the Ith string (relative to ARGV + HALF_ALLOC), so that
259 reallocating ARGV does not change the validity of its contents.
260 The integer offset is cast to char * during assembly, and is
261 converted to a true char * pointer on finalization.
263 During assembly, ARGV[ARGC] contains the offset of the first
264 unused string byte (relative to ARGV + HALF_ALLOC). */
265 char **argv;
266 int argc;
267 idx_t half_alloc;
269 /* The number of extra argv slots to keep room for. */
270 int extra_argc;
272 /* Whether processing should act as if the most recent character
273 seen was a separator. */
274 bool sep;
277 /* Expand SS so that it has at least one more argv slot and at least
278 one more string byte. */
279 static void
280 splitbuf_grow (struct splitbuf *ss)
282 idx_t old_half_alloc = ss->half_alloc;
283 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
284 ss->argv = xpalloc (ss->argv, &ss->half_alloc, 1,
285 MIN (INT_MAX, IDX_MAX), 2 * sizeof *ss->argv);
286 memmove (ss->argv + ss->half_alloc, ss->argv + old_half_alloc, string_bytes);
289 /* In SS, append C to the last string. */
290 static void
291 splitbuf_append_byte (struct splitbuf *ss, char c)
293 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
294 if (ss->half_alloc * sizeof *ss->argv <= string_bytes)
295 splitbuf_grow (ss);
296 ((char *) (ss->argv + ss->half_alloc))[string_bytes] = c;
297 ss->argv[ss->argc] = (char *) (intptr_t) (string_bytes + 1);
300 /* If SS's most recent character was a separator, finish off its
301 previous argument and start a new one. */
302 static void
303 check_start_new_arg (struct splitbuf *ss)
305 if (ss->sep)
307 splitbuf_append_byte (ss, '\0');
308 int argc = ss->argc;
309 if (ss->half_alloc <= argc + ss->extra_argc + 1)
310 splitbuf_grow (ss);
311 ss->argv[argc + 1] = ss->argv[argc];
312 ss->argc = argc + 1;
313 ss->sep = false;
317 /* All additions to SS have been made. Convert its offsets to pointers,
318 and return the resulting argument vector. */
319 static char **
320 splitbuf_finishup (struct splitbuf *ss)
322 int argc = ss->argc;
323 char **argv = ss->argv;
324 char *stringbase = (char *) (ss->argv + ss->half_alloc);
325 for (int i = 1; i < argc; i++)
326 argv[i] = stringbase + (intptr_t) argv[i];
327 return argv;
330 /* Return a newly-allocated argv-like array,
331 by parsing and splitting the input 'str'.
333 'extra_argc' is the number of additional elements to allocate
334 in the array (on top of the number of args required to split 'str').
336 Store into *argc the number of arguments found (plus 1 for
337 the program name).
339 Example:
340 int argc;
341 char **argv = build_argv ("A=B uname -k', 3, &argc);
342 Results in:
343 argc = 4
344 argv[0] = [not initialized]
345 argv[1] = "A=B"
346 argv[2] = "uname"
347 argv[3] = "-k"
348 argv[4,5,6,7] = [allocated due to extra_argc + 1, but not initialized]
350 To free allocated memory:
351 free (argv);
352 However, 'env' does not free since it's about to exec or exit anyway
353 and the complexity of keeping track of the storage that may have been
354 allocated via multiple calls to build_argv is not worth the hassle. */
355 static char **
356 build_argv (char const *str, int extra_argc, int *argc)
358 bool dq = false, sq = false;
359 struct splitbuf ss;
360 ss.argv = xnmalloc (extra_argc + 2, 2 * sizeof *ss.argv);
361 ss.argc = 1;
362 ss.half_alloc = extra_argc + 2;
363 ss.extra_argc = extra_argc;
364 ss.sep = true;
365 ss.argv[ss.argc] = 0;
367 /* In the following loop,
368 'break' causes the character 'newc' to be added to *dest,
369 'continue' skips the character. */
370 while (*str)
372 char newc = *str; /* Default: add the next character. */
374 switch (*str)
376 case '\'':
377 if (dq)
378 break;
379 sq = !sq;
380 check_start_new_arg (&ss);
381 ++str;
382 continue;
384 case '"':
385 if (sq)
386 break;
387 dq = !dq;
388 check_start_new_arg (&ss);
389 ++str;
390 continue;
392 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
393 /* Start a new argument if outside quotes. */
394 if (sq || dq)
395 break;
396 ss.sep = true;
397 str += strspn (str, C_ISSPACE_CHARS);
398 continue;
400 case '#':
401 if (!ss.sep)
402 break;
403 goto eos; /* '#' as first char terminates the string. */
405 case '\\':
406 /* Backslash inside single-quotes is not special, except \\
407 and \'. */
408 if (sq && str[1] != '\\' && str[1] != '\'')
409 break;
411 /* Skip the backslash and examine the next character. */
412 newc = *++str;
413 switch (newc)
415 case '"': case '#': case '$': case '\'': case '\\':
416 /* Pass escaped character as-is. */
417 break;
419 case '_':
420 if (!dq)
422 ++str; /* '\_' outside double-quotes is arg separator. */
423 ss.sep = true;
424 continue;
426 newc = ' '; /* '\_' inside double-quotes is space. */
427 break;
429 case 'c':
430 if (dq)
431 die (EXIT_CANCELED, 0,
432 _("'\\c' must not appear in double-quoted -S string"));
433 goto eos; /* '\c' terminates the string. */
435 case 'f': newc = '\f'; break;
436 case 'n': newc = '\n'; break;
437 case 'r': newc = '\r'; break;
438 case 't': newc = '\t'; break;
439 case 'v': newc = '\v'; break;
441 case '\0':
442 die (EXIT_CANCELED, 0,
443 _("invalid backslash at end of string in -S"));
445 default:
446 die (EXIT_CANCELED, 0, _("invalid sequence '\\%c' in -S"), newc);
448 break;
450 case '$':
451 /* ${VARNAME} are not expanded inside single-quotes. */
452 if (sq)
453 break;
455 /* Store the ${VARNAME} value. */
457 char *n = extract_varname (str);
458 if (!n)
459 die (EXIT_CANCELED, 0,
460 _("only ${VARNAME} expansion is supported, error at: %s"),
461 str);
463 char *v = getenv (n);
464 if (v)
466 check_start_new_arg (&ss);
467 devmsg ("expanding ${%s} into %s\n", n, quote (v));
468 for (; *v; v++)
469 splitbuf_append_byte (&ss, *v);
471 else
472 devmsg ("replacing ${%s} with null string\n", n);
474 str = strchr (str, '}') + 1;
475 continue;
479 check_start_new_arg (&ss);
480 splitbuf_append_byte (&ss, newc);
481 ++str;
484 if (dq || sq)
485 die (EXIT_CANCELED, 0, _("no terminating quote in -S string"));
487 eos:
488 splitbuf_append_byte (&ss, '\0');
489 *argc = ss.argc;
490 return splitbuf_finishup (&ss);
493 /* Process an "-S" string and create the corresponding argv array.
494 Update the given argc/argv parameters with the new argv.
496 Example: if executed as:
497 $ env -S"-i -C/tmp A=B" foo bar
498 The input argv is:
499 argv[0] = "env"
500 argv[1] = "-S-i -C/tmp A=B"
501 argv[2] = "foo"
502 argv[3] = "bar"
503 argv[4] = NULL
504 This function will modify argv to be:
505 argv[0] = "env"
506 argv[1] = "-i"
507 argv[2] = "-C/tmp"
508 argv[3] = "A=B"
509 argv[4] = "foo"
510 argv[5] = "bar"
511 argv[6] = NULL
512 argc will be updated from 4 to 6.
513 optind will be reset to 0 to force getopt_long to rescan all arguments. */
514 static void
515 parse_split_string (char const *str, int *orig_optind,
516 int *orig_argc, char ***orig_argv)
518 int extra_argc = *orig_argc - *orig_optind, newargc;
519 char **newargv = build_argv (str, extra_argc, &newargc);
521 /* Restore argv[0] - the 'env' executable name. */
522 *newargv = (*orig_argv)[0];
524 /* Print parsed arguments. */
525 if (dev_debug && 1 < newargc)
527 devmsg ("split -S: %s\n", quote (str));
528 devmsg (" into: %s\n", quote (newargv[1]));
529 for (int i = 2; i < newargc; i++)
530 devmsg (" & %s\n", quote (newargv[i]));
533 /* Add remaining arguments and terminating null from the original
534 command line. */
535 memcpy (newargv + newargc, *orig_argv + *orig_optind,
536 (extra_argc + 1) * sizeof *newargv);
538 /* Set new values for original getopt variables. */
539 *orig_argc = newargc + extra_argc;
540 *orig_argv = newargv;
541 *orig_optind = 0; /* Tell getopt to restart from first argument. */
544 static void
545 parse_signal_action_params (char const *optarg, bool set_default)
547 char signame[SIG2STR_MAX];
548 char *opt_sig;
549 char *optarg_writable;
551 if (! optarg)
553 /* Without an argument, reset all signals.
554 Some signals cannot be set to ignore or default (e.g., SIGKILL,
555 SIGSTOP on most OSes, and SIGCONT on AIX.) - so ignore errors. */
556 for (int i = 1 ; i <= SIGNUM_BOUND; i++)
557 if (sig2str (i, signame) == 0)
558 signals[i] = set_default ? DEFAULT_NOERR : IGNORE_NOERR;
559 return;
562 optarg_writable = xstrdup (optarg);
564 opt_sig = strtok (optarg_writable, ",");
565 while (opt_sig)
567 int signum = operand2sig (opt_sig, signame);
568 /* operand2sig accepts signal 0 (EXIT) - but we reject it. */
569 if (signum == 0)
570 error (0, 0, _("%s: invalid signal"), quote (opt_sig));
571 if (signum <= 0)
572 usage (exit_failure);
574 signals[signum] = set_default ? DEFAULT : IGNORE;
576 opt_sig = strtok (NULL, ",");
579 free (optarg_writable);
582 static void
583 reset_signal_handlers (void)
585 for (int i = 1; i <= SIGNUM_BOUND; i++)
587 struct sigaction act;
589 if (signals[i] == UNCHANGED)
590 continue;
592 bool ignore_errors = (signals[i] == DEFAULT_NOERR
593 || signals[i] == IGNORE_NOERR);
595 bool set_to_default = (signals[i] == DEFAULT
596 || signals[i] == DEFAULT_NOERR);
598 int sig_err = sigaction (i, NULL, &act);
600 if (sig_err && !ignore_errors)
601 die (EXIT_CANCELED, errno,
602 _("failed to get signal action for signal %d"), i);
604 if (! sig_err)
606 act.sa_handler = set_to_default ? SIG_DFL : SIG_IGN;
607 sig_err = sigaction (i, &act, NULL);
608 if (sig_err && !ignore_errors)
609 die (EXIT_CANCELED, errno,
610 _("failed to set signal action for signal %d"), i);
613 if (dev_debug)
615 char signame[SIG2STR_MAX];
616 sig2str (i, signame);
617 devmsg ("Reset signal %s (%d) to %s%s\n",
618 signame, i,
619 set_to_default ? "DEFAULT" : "IGNORE",
620 sig_err ? " (failure ignored)" : "");
626 static void
627 parse_block_signal_params (char const *optarg, bool block)
629 char signame[SIG2STR_MAX];
630 char *opt_sig;
631 char *optarg_writable;
633 if (! optarg)
635 /* Without an argument, reset all signals. */
636 sigfillset (block ? &block_signals : &unblock_signals);
637 sigemptyset (block ? &unblock_signals : &block_signals);
639 else if (! sig_mask_changed)
641 /* Initialize the sets. */
642 sigemptyset (&block_signals);
643 sigemptyset (&unblock_signals);
646 sig_mask_changed = true;
648 if (! optarg)
649 return;
651 optarg_writable = xstrdup (optarg);
653 opt_sig = strtok (optarg_writable, ",");
654 while (opt_sig)
656 int signum = operand2sig (opt_sig, signame);
657 /* operand2sig accepts signal 0 (EXIT) - but we reject it. */
658 if (signum == 0)
659 error (0, 0, _("%s: invalid signal"), quote (opt_sig));
660 if (signum <= 0)
661 usage (exit_failure);
663 sigaddset (block ? &block_signals : &unblock_signals, signum);
664 sigdelset (block ? &unblock_signals : &block_signals, signum);
666 opt_sig = strtok (NULL, ",");
669 free (optarg_writable);
672 static void
673 set_signal_proc_mask (void)
675 /* Get the existing signal mask */
676 sigset_t set;
677 char const *debug_act;
679 sigemptyset (&set);
681 if (sigprocmask (0, NULL, &set))
682 die (EXIT_CANCELED, errno, _("failed to get signal process mask"));
684 for (int i = 1; i <= SIGNUM_BOUND; i++)
686 if (sigismember (&block_signals, i))
688 sigaddset (&set, i);
689 debug_act = "BLOCK";
691 else if (sigismember (&unblock_signals, i))
693 sigdelset (&set, i);
694 debug_act = "UNBLOCK";
696 else
698 debug_act = NULL;
701 if (dev_debug && debug_act)
703 char signame[SIG2STR_MAX];
704 sig2str (i, signame);
705 devmsg ("signal %s (%d) mask set to %s\n",
706 signame, i, debug_act);
710 if (sigprocmask (SIG_SETMASK, &set, NULL))
711 die (EXIT_CANCELED, errno, _("failed to set signal process mask"));
714 static void
715 list_signal_handling (void)
717 sigset_t set;
718 char signame[SIG2STR_MAX];
720 sigemptyset (&set);
721 if (sigprocmask (0, NULL, &set))
722 die (EXIT_CANCELED, errno, _("failed to get signal process mask"));
724 for (int i = 1; i <= SIGNUM_BOUND; i++)
726 struct sigaction act;
727 if (sigaction (i, NULL, &act))
728 continue;
730 char const *ignored = act.sa_handler == SIG_IGN ? "IGNORE" : "";
731 char const *blocked = sigismember (&set, i) ? "BLOCK" : "";
732 char const *connect = *ignored && *blocked ? "," : "";
734 if (! *ignored && ! *blocked)
735 continue;
737 sig2str (i, signame);
738 fprintf (stderr, "%-10s (%2d): %s%s%s\n", signame, i,
739 blocked, connect, ignored);
743 static void
744 initialize_signals (void)
746 signals = xmalloc ((sizeof *signals) * (SIGNUM_BOUND + 1));
748 for (int i = 0 ; i <= SIGNUM_BOUND; i++)
749 signals[i] = UNCHANGED;
751 return;
755 main (int argc, char **argv)
757 int optc;
758 bool ignore_environment = false;
759 bool opt_nul_terminate_output = false;
760 char const *newdir = NULL;
762 initialize_main (&argc, &argv);
763 set_program_name (argv[0]);
764 setlocale (LC_ALL, "");
765 bindtextdomain (PACKAGE, LOCALEDIR);
766 textdomain (PACKAGE);
768 initialize_exit_failure (EXIT_CANCELED);
769 atexit (close_stdout);
771 initialize_signals ();
773 while ((optc = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
775 switch (optc)
777 case 'i':
778 ignore_environment = true;
779 break;
780 case 'u':
781 append_unset_var (optarg);
782 break;
783 case 'v':
784 dev_debug = true;
785 break;
786 case '0':
787 opt_nul_terminate_output = true;
788 break;
789 case DEFAULT_SIGNAL_OPTION:
790 parse_signal_action_params (optarg, true);
791 parse_block_signal_params (optarg, false);
792 break;
793 case IGNORE_SIGNAL_OPTION:
794 parse_signal_action_params (optarg, false);
795 break;
796 case BLOCK_SIGNAL_OPTION:
797 parse_block_signal_params (optarg, true);
798 break;
799 case LIST_SIGNAL_HANDLING_OPTION:
800 report_signal_handling = true;
801 break;
802 case 'C':
803 newdir = optarg;
804 break;
805 case 'S':
806 parse_split_string (optarg, &optind, &argc, &argv);
807 break;
808 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
809 /* These are undocumented options. Attempt to detect
810 incorrect shebang usage with extraneous space, e.g.:
811 #!/usr/bin/env -i command
812 In which case argv[1] == "-i command". */
813 error (0, 0, _("invalid option -- '%c'"), optc);
814 error (0, 0, _("use -[v]S to pass options in shebang lines"));
815 usage (EXIT_CANCELED);
817 case_GETOPT_HELP_CHAR;
818 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
819 default:
820 usage (EXIT_CANCELED);
824 if (optind < argc && STREQ (argv[optind], "-"))
826 ignore_environment = true;
827 ++optind;
830 if (ignore_environment)
832 devmsg ("cleaning environ\n");
833 static char *dummy_environ[] = { NULL };
834 environ = dummy_environ;
836 else
837 unset_envvars ();
839 char *eq;
840 while (optind < argc && (eq = strchr (argv[optind], '=')))
842 devmsg ("setenv: %s\n", argv[optind]);
844 if (putenv (argv[optind]))
846 *eq = '\0';
847 die (EXIT_CANCELED, errno, _("cannot set %s"),
848 quote (argv[optind]));
850 optind++;
853 bool program_specified = optind < argc;
855 if (opt_nul_terminate_output && program_specified)
857 error (0, 0, _("cannot specify --null (-0) with command"));
858 usage (EXIT_CANCELED);
861 if (newdir && ! program_specified)
863 error (0, 0, _("must specify command with --chdir (-C)"));
864 usage (EXIT_CANCELED);
867 if (! program_specified)
869 /* Print the environment and exit. */
870 char *const *e = environ;
871 while (*e)
872 printf ("%s%c", *e++, opt_nul_terminate_output ? '\0' : '\n');
873 return EXIT_SUCCESS;
876 reset_signal_handlers ();
877 if (sig_mask_changed)
878 set_signal_proc_mask ();
880 if (report_signal_handling)
881 list_signal_handling ();
883 if (newdir)
885 devmsg ("chdir: %s\n", quoteaf (newdir));
887 if (chdir (newdir) != 0)
888 die (EXIT_CANCELED, errno, _("cannot change directory to %s"),
889 quoteaf (newdir));
892 if (dev_debug)
894 devmsg ("executing: %s\n", argv[optind]);
895 for (int i=optind; i<argc; ++i)
896 devmsg (" arg[%d]= %s\n", i-optind, quote (argv[i]));
899 execvp (argv[optind], &argv[optind]);
901 int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
902 error (0, errno, "%s", quote (argv[optind]));
904 if (exit_status == EXIT_ENOENT && strpbrk (argv[optind], C_ISSPACE_CHARS))
905 error (0, 0, _("use -[v]S to pass options in shebang lines"));
907 return exit_status;