du: --apparent counts only symlinks and regular
[coreutils.git] / src / env.c
blob2f38d7538f975445ab382fe7b713e7a485a15a42
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 "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. An empty SIG argument is a no-op.\n\
162 "), stdout);
163 emit_exec_status (PROGRAM_NAME);
164 emit_ancillary_info (PROGRAM_NAME);
166 exit (status);
169 static void
170 append_unset_var (char const *var)
172 if (usvars_used == usvars_alloc)
173 usvars = x2nrealloc (usvars, &usvars_alloc, sizeof *usvars);
174 usvars[usvars_used++] = var;
177 static void
178 unset_envvars (void)
180 for (idx_t i = 0; i < usvars_used; ++i)
182 devmsg ("unset: %s\n", usvars[i]);
184 if (unsetenv (usvars[i]))
185 die (EXIT_CANCELED, errno, _("cannot unset %s"),
186 quote (usvars[i]));
190 /* Return a pointer to the end of a valid ${VARNAME} string, or NULL.
191 'str' should point to the '$' character.
192 First letter in VARNAME must be alpha or underscore,
193 rest of letters are alnum or underscore.
194 Any other character is an error. */
195 ATTRIBUTE_PURE
196 static char const *
197 scan_varname (char const *str)
199 if (str[1] == '{' && (c_isalpha (str[2]) || str[2] == '_'))
201 char const *end = str + 3;
202 while (c_isalnum (*end) || *end == '_')
203 ++end;
204 if (*end == '}')
205 return end;
208 return NULL;
211 /* Return a pointer to a static buffer containing the VARNAME as
212 extracted from a '${VARNAME}' string.
213 The returned string will be NUL terminated.
214 The returned pointer should not be freed.
215 Return NULL if not a valid ${VARNAME} syntax. */
216 static char *
217 extract_varname (char const *str)
219 idx_t i;
220 char const *p;
222 p = scan_varname (str);
223 if (!p)
224 return NULL;
226 /* -2 and +2 (below) account for the '${' prefix. */
227 i = p - str - 2;
229 if (i >= vnlen)
231 vnlen = i + 1;
232 varname = xrealloc (varname, vnlen);
235 memcpy (varname, str + 2, i);
236 varname[i] = 0;
238 return varname;
241 /* Temporary buffer used by --split-string processing. */
242 struct splitbuf
244 /* Buffer address, arg count, and half the number of elements in the buffer.
245 ARGC and ARGV are as in 'main', and ARGC + 1 <= HALF_ALLOC so
246 that the upper half of ARGV can be used for string contents.
247 This may waste up to half the space but keeps the code simple,
248 which is better for this rarely-used but security-sensitive code.
250 ARGV[0] is not initialized; that is the caller's responsibility
251 after finalization.
253 During assembly, ARGV[I] (where 0 < I < ARGC) contains the offset
254 of the Ith string (relative to ARGV + HALF_ALLOC), so that
255 reallocating ARGV does not change the validity of its contents.
256 The integer offset is cast to char * during assembly, and is
257 converted to a true char * pointer on finalization.
259 During assembly, ARGV[ARGC] contains the offset of the first
260 unused string byte (relative to ARGV + HALF_ALLOC). */
261 char **argv;
262 int argc;
263 idx_t half_alloc;
265 /* The number of extra argv slots to keep room for. */
266 int extra_argc;
268 /* Whether processing should act as if the most recent character
269 seen was a separator. */
270 bool sep;
273 /* Expand SS so that it has at least one more argv slot and at least
274 one more string byte. */
275 static void
276 splitbuf_grow (struct splitbuf *ss)
278 idx_t old_half_alloc = ss->half_alloc;
279 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
280 ss->argv = xpalloc (ss->argv, &ss->half_alloc, 1,
281 MIN (INT_MAX, IDX_MAX), 2 * sizeof *ss->argv);
282 memmove (ss->argv + ss->half_alloc, ss->argv + old_half_alloc, string_bytes);
285 /* In SS, append C to the last string. */
286 static void
287 splitbuf_append_byte (struct splitbuf *ss, char c)
289 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
290 if (ss->half_alloc * sizeof *ss->argv <= string_bytes)
291 splitbuf_grow (ss);
292 ((char *) (ss->argv + ss->half_alloc))[string_bytes] = c;
293 ss->argv[ss->argc] = (char *) (intptr_t) (string_bytes + 1);
296 /* If SS's most recent character was a separator, finish off its
297 previous argument and start a new one. */
298 static void
299 check_start_new_arg (struct splitbuf *ss)
301 if (ss->sep)
303 splitbuf_append_byte (ss, '\0');
304 int argc = ss->argc;
305 if (ss->half_alloc <= argc + ss->extra_argc + 1)
306 splitbuf_grow (ss);
307 ss->argv[argc + 1] = ss->argv[argc];
308 ss->argc = argc + 1;
309 ss->sep = false;
313 /* All additions to SS have been made. Convert its offsets to pointers,
314 and return the resulting argument vector. */
315 static char **
316 splitbuf_finishup (struct splitbuf *ss)
318 int argc = ss->argc;
319 char **argv = ss->argv;
320 char *stringbase = (char *) (ss->argv + ss->half_alloc);
321 for (int i = 1; i < argc; i++)
322 argv[i] = stringbase + (intptr_t) argv[i];
323 return argv;
326 /* Return a newly-allocated argv-like array,
327 by parsing and splitting the input 'str'.
329 'extra_argc' is the number of additional elements to allocate
330 in the array (on top of the number of args required to split 'str').
332 Store into *argc the number of arguments found (plus 1 for
333 the program name).
335 Example:
336 int argc;
337 char **argv = build_argv ("A=B uname -k', 3, &argc);
338 Results in:
339 argc = 4
340 argv[0] = [not initialized]
341 argv[1] = "A=B"
342 argv[2] = "uname"
343 argv[3] = "-k"
344 argv[4,5,6,7] = [allocated due to extra_argc + 1, but not initialized]
346 To free allocated memory:
347 free (argv);
348 However, 'env' does not free since it's about to exec or exit anyway
349 and the complexity of keeping track of the storage that may have been
350 allocated via multiple calls to build_argv is not worth the hassle. */
351 static char **
352 build_argv (char const *str, int extra_argc, int *argc)
354 bool dq = false, sq = false;
355 struct splitbuf ss;
356 ss.argv = xnmalloc (extra_argc + 2, 2 * sizeof *ss.argv);
357 ss.argc = 1;
358 ss.half_alloc = extra_argc + 2;
359 ss.extra_argc = extra_argc;
360 ss.sep = true;
361 ss.argv[ss.argc] = 0;
363 /* In the following loop,
364 'break' causes the character 'newc' to be added to *dest,
365 'continue' skips the character. */
366 while (*str)
368 char newc = *str; /* Default: add the next character. */
370 switch (*str)
372 case '\'':
373 if (dq)
374 break;
375 sq = !sq;
376 check_start_new_arg (&ss);
377 ++str;
378 continue;
380 case '"':
381 if (sq)
382 break;
383 dq = !dq;
384 check_start_new_arg (&ss);
385 ++str;
386 continue;
388 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
389 /* Start a new argument if outside quotes. */
390 if (sq || dq)
391 break;
392 ss.sep = true;
393 str += strspn (str, C_ISSPACE_CHARS);
394 continue;
396 case '#':
397 if (!ss.sep)
398 break;
399 goto eos; /* '#' as first char terminates the string. */
401 case '\\':
402 /* Backslash inside single-quotes is not special, except \\
403 and \'. */
404 if (sq && str[1] != '\\' && str[1] != '\'')
405 break;
407 /* Skip the backslash and examine the next character. */
408 newc = *++str;
409 switch (newc)
411 case '"': case '#': case '$': case '\'': case '\\':
412 /* Pass escaped character as-is. */
413 break;
415 case '_':
416 if (!dq)
418 ++str; /* '\_' outside double-quotes is arg separator. */
419 ss.sep = true;
420 continue;
422 newc = ' '; /* '\_' inside double-quotes is space. */
423 break;
425 case 'c':
426 if (dq)
427 die (EXIT_CANCELED, 0,
428 _("'\\c' must not appear in double-quoted -S string"));
429 goto eos; /* '\c' terminates the string. */
431 case 'f': newc = '\f'; break;
432 case 'n': newc = '\n'; break;
433 case 'r': newc = '\r'; break;
434 case 't': newc = '\t'; break;
435 case 'v': newc = '\v'; break;
437 case '\0':
438 die (EXIT_CANCELED, 0,
439 _("invalid backslash at end of string in -S"));
441 default:
442 die (EXIT_CANCELED, 0, _("invalid sequence '\\%c' in -S"), newc);
444 break;
446 case '$':
447 /* ${VARNAME} are not expanded inside single-quotes. */
448 if (sq)
449 break;
451 /* Store the ${VARNAME} value. */
453 char *n = extract_varname (str);
454 if (!n)
455 die (EXIT_CANCELED, 0,
456 _("only ${VARNAME} expansion is supported, error at: %s"),
457 str);
459 char *v = getenv (n);
460 if (v)
462 check_start_new_arg (&ss);
463 devmsg ("expanding ${%s} into %s\n", n, quote (v));
464 for (; *v; v++)
465 splitbuf_append_byte (&ss, *v);
467 else
468 devmsg ("replacing ${%s} with null string\n", n);
470 str = strchr (str, '}') + 1;
471 continue;
475 check_start_new_arg (&ss);
476 splitbuf_append_byte (&ss, newc);
477 ++str;
480 if (dq || sq)
481 die (EXIT_CANCELED, 0, _("no terminating quote in -S string"));
483 eos:
484 splitbuf_append_byte (&ss, '\0');
485 *argc = ss.argc;
486 return splitbuf_finishup (&ss);
489 /* Process an "-S" string and create the corresponding argv array.
490 Update the given argc/argv parameters with the new argv.
492 Example: if executed as:
493 $ env -S"-i -C/tmp A=B" foo bar
494 The input argv is:
495 argv[0] = "env"
496 argv[1] = "-S-i -C/tmp A=B"
497 argv[2] = "foo"
498 argv[3] = "bar"
499 argv[4] = NULL
500 This function will modify argv to be:
501 argv[0] = "env"
502 argv[1] = "-i"
503 argv[2] = "-C/tmp"
504 argv[3] = "A=B"
505 argv[4] = "foo"
506 argv[5] = "bar"
507 argv[6] = NULL
508 argc will be updated from 4 to 6.
509 optind will be reset to 0 to force getopt_long to rescan all arguments. */
510 static void
511 parse_split_string (char const *str, int *orig_optind,
512 int *orig_argc, char ***orig_argv)
514 int extra_argc = *orig_argc - *orig_optind, newargc;
515 char **newargv = build_argv (str, extra_argc, &newargc);
517 /* Restore argv[0] - the 'env' executable name. */
518 *newargv = (*orig_argv)[0];
520 /* Print parsed arguments. */
521 if (dev_debug && 1 < newargc)
523 devmsg ("split -S: %s\n", quote (str));
524 devmsg (" into: %s\n", quote (newargv[1]));
525 for (int i = 2; i < newargc; i++)
526 devmsg (" & %s\n", quote (newargv[i]));
529 /* Add remaining arguments and terminating null from the original
530 command line. */
531 memcpy (newargv + newargc, *orig_argv + *orig_optind,
532 (extra_argc + 1) * sizeof *newargv);
534 /* Set new values for original getopt variables. */
535 *orig_argc = newargc + extra_argc;
536 *orig_argv = newargv;
537 *orig_optind = 0; /* Tell getopt to restart from first argument. */
540 static void
541 parse_signal_action_params (char const *optarg, bool set_default)
543 char signame[SIG2STR_MAX];
544 char *opt_sig;
545 char *optarg_writable;
547 if (! optarg)
549 /* Without an argument, reset all signals.
550 Some signals cannot be set to ignore or default (e.g., SIGKILL,
551 SIGSTOP on most OSes, and SIGCONT on AIX.) - so ignore errors. */
552 for (int i = 1 ; i <= SIGNUM_BOUND; i++)
553 if (sig2str (i, signame) == 0)
554 signals[i] = set_default ? DEFAULT_NOERR : IGNORE_NOERR;
555 return;
558 optarg_writable = xstrdup (optarg);
560 opt_sig = strtok (optarg_writable, ",");
561 while (opt_sig)
563 int signum = operand2sig (opt_sig, signame);
564 /* operand2sig accepts signal 0 (EXIT) - but we reject it. */
565 if (signum == 0)
566 error (0, 0, _("%s: invalid signal"), quote (opt_sig));
567 if (signum <= 0)
568 usage (exit_failure);
570 signals[signum] = set_default ? DEFAULT : IGNORE;
572 opt_sig = strtok (NULL, ",");
575 free (optarg_writable);
578 static void
579 reset_signal_handlers (void)
581 for (int i = 1; i <= SIGNUM_BOUND; i++)
583 struct sigaction act;
585 if (signals[i] == UNCHANGED)
586 continue;
588 bool ignore_errors = (signals[i] == DEFAULT_NOERR
589 || signals[i] == IGNORE_NOERR);
591 bool set_to_default = (signals[i] == DEFAULT
592 || signals[i] == DEFAULT_NOERR);
594 int sig_err = sigaction (i, NULL, &act);
596 if (sig_err && !ignore_errors)
597 die (EXIT_CANCELED, errno,
598 _("failed to get signal action for signal %d"), i);
600 if (! sig_err)
602 act.sa_handler = set_to_default ? SIG_DFL : SIG_IGN;
603 sig_err = sigaction (i, &act, NULL);
604 if (sig_err && !ignore_errors)
605 die (EXIT_CANCELED, errno,
606 _("failed to set signal action for signal %d"), i);
609 if (dev_debug)
611 char signame[SIG2STR_MAX];
612 sig2str (i, signame);
613 devmsg ("Reset signal %s (%d) to %s%s\n",
614 signame, i,
615 set_to_default ? "DEFAULT" : "IGNORE",
616 sig_err ? " (failure ignored)" : "");
622 static void
623 parse_block_signal_params (char const *optarg, bool block)
625 char signame[SIG2STR_MAX];
626 char *opt_sig;
627 char *optarg_writable;
629 if (! optarg)
631 /* Without an argument, reset all signals. */
632 sigfillset (block ? &block_signals : &unblock_signals);
633 sigemptyset (block ? &unblock_signals : &block_signals);
635 else if (! sig_mask_changed)
637 /* Initialize the sets. */
638 sigemptyset (&block_signals);
639 sigemptyset (&unblock_signals);
642 sig_mask_changed = true;
644 if (! optarg)
645 return;
647 optarg_writable = xstrdup (optarg);
649 opt_sig = strtok (optarg_writable, ",");
650 while (opt_sig)
652 int signum = operand2sig (opt_sig, signame);
653 /* operand2sig accepts signal 0 (EXIT) - but we reject it. */
654 if (signum == 0)
655 error (0, 0, _("%s: invalid signal"), quote (opt_sig));
656 if (signum <= 0)
657 usage (exit_failure);
659 sigaddset (block ? &block_signals : &unblock_signals, signum);
660 sigdelset (block ? &unblock_signals : &block_signals, signum);
662 opt_sig = strtok (NULL, ",");
665 free (optarg_writable);
668 static void
669 set_signal_proc_mask (void)
671 /* Get the existing signal mask */
672 sigset_t set;
673 char const *debug_act;
675 sigemptyset (&set);
677 if (sigprocmask (0, NULL, &set))
678 die (EXIT_CANCELED, errno, _("failed to get signal process mask"));
680 for (int i = 1; i <= SIGNUM_BOUND; i++)
682 if (sigismember (&block_signals, i))
684 sigaddset (&set, i);
685 debug_act = "BLOCK";
687 else if (sigismember (&unblock_signals, i))
689 sigdelset (&set, i);
690 debug_act = "UNBLOCK";
692 else
694 debug_act = NULL;
697 if (dev_debug && debug_act)
699 char signame[SIG2STR_MAX];
700 sig2str (i, signame);
701 devmsg ("signal %s (%d) mask set to %s\n",
702 signame, i, debug_act);
706 if (sigprocmask (SIG_SETMASK, &set, NULL))
707 die (EXIT_CANCELED, errno, _("failed to set signal process mask"));
710 static void
711 list_signal_handling (void)
713 sigset_t set;
714 char signame[SIG2STR_MAX];
716 sigemptyset (&set);
717 if (sigprocmask (0, NULL, &set))
718 die (EXIT_CANCELED, errno, _("failed to get signal process mask"));
720 for (int i = 1; i <= SIGNUM_BOUND; i++)
722 struct sigaction act;
723 if (sigaction (i, NULL, &act))
724 continue;
726 char const *ignored = act.sa_handler == SIG_IGN ? "IGNORE" : "";
727 char const *blocked = sigismember (&set, i) ? "BLOCK" : "";
728 char const *connect = *ignored && *blocked ? "," : "";
730 if (! *ignored && ! *blocked)
731 continue;
733 sig2str (i, signame);
734 fprintf (stderr, "%-10s (%2d): %s%s%s\n", signame, i,
735 blocked, connect, ignored);
739 static void
740 initialize_signals (void)
742 signals = xmalloc ((sizeof *signals) * (SIGNUM_BOUND + 1));
744 for (int i = 0 ; i <= SIGNUM_BOUND; i++)
745 signals[i] = UNCHANGED;
747 return;
751 main (int argc, char **argv)
753 int optc;
754 bool ignore_environment = false;
755 bool opt_nul_terminate_output = false;
756 char const *newdir = NULL;
758 initialize_main (&argc, &argv);
759 set_program_name (argv[0]);
760 setlocale (LC_ALL, "");
761 bindtextdomain (PACKAGE, LOCALEDIR);
762 textdomain (PACKAGE);
764 initialize_exit_failure (EXIT_CANCELED);
765 atexit (close_stdout);
767 initialize_signals ();
769 while ((optc = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)
771 switch (optc)
773 case 'i':
774 ignore_environment = true;
775 break;
776 case 'u':
777 append_unset_var (optarg);
778 break;
779 case 'v':
780 dev_debug = true;
781 break;
782 case '0':
783 opt_nul_terminate_output = true;
784 break;
785 case DEFAULT_SIGNAL_OPTION:
786 parse_signal_action_params (optarg, true);
787 parse_block_signal_params (optarg, false);
788 break;
789 case IGNORE_SIGNAL_OPTION:
790 parse_signal_action_params (optarg, false);
791 break;
792 case BLOCK_SIGNAL_OPTION:
793 parse_block_signal_params (optarg, true);
794 break;
795 case LIST_SIGNAL_HANDLING_OPTION:
796 report_signal_handling = true;
797 break;
798 case 'C':
799 newdir = optarg;
800 break;
801 case 'S':
802 parse_split_string (optarg, &optind, &argc, &argv);
803 break;
804 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
805 /* These are undocumented options. Attempt to detect
806 incorrect shebang usage with extraneous space, e.g.:
807 #!/usr/bin/env -i command
808 In which case argv[1] == "-i command". */
809 error (0, 0, _("invalid option -- '%c'"), optc);
810 error (0, 0, _("use -[v]S to pass options in shebang lines"));
811 usage (EXIT_CANCELED);
813 case_GETOPT_HELP_CHAR;
814 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
815 default:
816 usage (EXIT_CANCELED);
820 if (optind < argc && STREQ (argv[optind], "-"))
822 ignore_environment = true;
823 ++optind;
826 if (ignore_environment)
828 devmsg ("cleaning environ\n");
829 static char *dummy_environ[] = { NULL };
830 environ = dummy_environ;
832 else
833 unset_envvars ();
835 char *eq;
836 while (optind < argc && (eq = strchr (argv[optind], '=')))
838 devmsg ("setenv: %s\n", argv[optind]);
840 if (putenv (argv[optind]))
842 *eq = '\0';
843 die (EXIT_CANCELED, errno, _("cannot set %s"),
844 quote (argv[optind]));
846 optind++;
849 bool program_specified = optind < argc;
851 if (opt_nul_terminate_output && program_specified)
853 error (0, 0, _("cannot specify --null (-0) with command"));
854 usage (EXIT_CANCELED);
857 if (newdir && ! program_specified)
859 error (0, 0, _("must specify command with --chdir (-C)"));
860 usage (EXIT_CANCELED);
863 if (! program_specified)
865 /* Print the environment and exit. */
866 char *const *e = environ;
867 while (*e)
868 printf ("%s%c", *e++, opt_nul_terminate_output ? '\0' : '\n');
869 return EXIT_SUCCESS;
872 reset_signal_handlers ();
873 if (sig_mask_changed)
874 set_signal_proc_mask ();
876 if (report_signal_handling)
877 list_signal_handling ();
879 if (newdir)
881 devmsg ("chdir: %s\n", quoteaf (newdir));
883 if (chdir (newdir) != 0)
884 die (EXIT_CANCELED, errno, _("cannot change directory to %s"),
885 quoteaf (newdir));
888 if (dev_debug)
890 devmsg ("executing: %s\n", argv[optind]);
891 for (int i=optind; i<argc; ++i)
892 devmsg (" arg[%d]= %s\n", i-optind, quote (argv[i]));
895 execvp (argv[optind], &argv[optind]);
897 int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
898 error (0, errno, "%s", quote (argv[optind]));
900 if (exit_status == EXIT_ENOENT && strpbrk (argv[optind], C_ISSPACE_CHARS))
901 error (0, 0, _("use -[v]S to pass options in shebang lines"));
903 main_exit (exit_status);