ls: fix spurious output with -Z
[coreutils.git] / src / env.c
blobaf876fa08b4ba01de30919b3115d9f3d222201ef
1 /* env - run a program in a modified environment
2 Copyright (C) 1986-2024 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 "operand2sig.h"
28 #include "quote.h"
29 #include "sig2str.h"
31 /* The official name of this program (e.g., no 'g' prefix). */
32 #define PROGRAM_NAME "env"
34 #define AUTHORS \
35 proper_name ("Richard Mlynarik"), \
36 proper_name ("David MacKenzie"), \
37 proper_name ("Assaf Gordon")
39 /* Array of envvars to unset. */
40 static char const **usvars;
41 static size_t usvars_alloc;
42 static idx_t usvars_used;
44 /* Annotate the output with extra info to aid the user. */
45 static bool dev_debug;
47 /* Buffer and length of extracted envvars in -S strings. */
48 static char *varname;
49 static idx_t vnlen;
51 /* Possible actions on each signal. */
52 enum SIGNAL_MODE {
53 UNCHANGED = 0,
54 DEFAULT, /* Set to default handler (SIG_DFL). */
55 DEFAULT_NOERR, /* Ditto, but ignore sigaction(2) errors. */
56 IGNORE, /* Set to ignore (SIG_IGN). */
57 IGNORE_NOERR /* Ditto, but ignore sigaction(2) errors. */
59 static enum SIGNAL_MODE *signals;
61 /* Set of signals to block. */
62 static sigset_t block_signals;
64 /* Set of signals to unblock. */
65 static sigset_t unblock_signals;
67 /* Whether signal mask adjustment requested. */
68 static bool sig_mask_changed;
70 /* Whether to list non default handling. */
71 static bool report_signal_handling;
73 /* The isspace characters in the C locale. */
74 #define C_ISSPACE_CHARS " \t\n\v\f\r"
76 static char const shortopts[] = "+a:C:iS:u:v0" C_ISSPACE_CHARS;
78 /* For long options that have no equivalent short option, use a
79 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
80 enum
82 DEFAULT_SIGNAL_OPTION = CHAR_MAX + 1,
83 IGNORE_SIGNAL_OPTION,
84 BLOCK_SIGNAL_OPTION,
85 LIST_SIGNAL_HANDLING_OPTION,
88 static struct option const longopts[] =
90 {"argv0", required_argument, nullptr, 'a'},
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 -a, --argv0=ARG pass ARG as the zeroth argument of COMMAND\n\
124 "), stdout);
125 fputs (_("\
126 -i, --ignore-environment start with an empty environment\n\
127 -0, --null end each output line with NUL, not newline\n\
128 -u, --unset=NAME remove variable from the environment\n\
129 "), stdout);
130 fputs (_("\
131 -C, --chdir=DIR change working directory to DIR\n\
132 "), stdout);
133 fputs (_("\
134 -S, --split-string=S process and split S into separate arguments;\n\
135 used to pass multiple arguments on shebang lines\n\
136 "), stdout);
137 fputs (_("\
138 --block-signal[=SIG] block delivery of SIG signal(s) to COMMAND\n\
139 "), stdout);
140 fputs (_("\
141 --default-signal[=SIG] reset handling of SIG signal(s) to the default\n\
142 "), stdout);
143 fputs (_("\
144 --ignore-signal[=SIG] set handling of SIG signal(s) to do nothing\n\
145 "), stdout);
146 fputs (_("\
147 --list-signal-handling list non default signal handling to stderr\n\
148 "), stdout);
149 fputs (_("\
150 -v, --debug print verbose information for each processing step\n\
151 "), stdout);
152 fputs (HELP_OPTION_DESCRIPTION, stdout);
153 fputs (VERSION_OPTION_DESCRIPTION, stdout);
154 fputs (_("\
156 A mere - implies -i. If no COMMAND, print the resulting environment.\n\
157 "), stdout);
158 fputs (_("\
160 SIG may be a signal name like 'PIPE', or a signal number like '13'.\n\
161 Without SIG, all known signals are included. Multiple signals can be\n\
162 comma-separated. An empty SIG argument is a no-op.\n\
163 "), stdout);
164 emit_exec_status (PROGRAM_NAME);
165 emit_ancillary_info (PROGRAM_NAME);
167 exit (status);
170 static void
171 append_unset_var (char const *var)
173 if (usvars_used == usvars_alloc)
174 usvars = x2nrealloc (usvars, &usvars_alloc, sizeof *usvars);
175 usvars[usvars_used++] = var;
178 static void
179 unset_envvars (void)
181 for (idx_t i = 0; i < usvars_used; ++i)
183 devmsg ("unset: %s\n", usvars[i]);
185 if (unsetenv (usvars[i]))
186 error (EXIT_CANCELED, errno, _("cannot unset %s"),
187 quote (usvars[i]));
191 /* Return a pointer to the end of a valid ${VARNAME} string, or nullptr.
192 'str' should point to the '$' character.
193 First letter in VARNAME must be alpha or underscore,
194 rest of letters are alnum or underscore.
195 Any other character is an error. */
196 ATTRIBUTE_PURE
197 static char const *
198 scan_varname (char const *str)
200 if (str[1] == '{' && (c_isalpha (str[2]) || str[2] == '_'))
202 char const *end = str + 3;
203 while (c_isalnum (*end) || *end == '_')
204 ++end;
205 if (*end == '}')
206 return end;
209 return nullptr;
212 /* Return a pointer to a static buffer containing the VARNAME as
213 extracted from a '${VARNAME}' string.
214 The returned string will be NUL terminated.
215 The returned pointer should not be freed.
216 Return nullptr if not a valid ${VARNAME} syntax. */
217 static char *
218 extract_varname (char const *str)
220 idx_t i;
221 char const *p;
223 p = scan_varname (str);
224 if (!p)
225 return nullptr;
227 /* -2 and +2 (below) account for the '${' prefix. */
228 i = p - str - 2;
230 if (i >= vnlen)
232 vnlen = i + 1;
233 varname = xrealloc (varname, vnlen);
236 memcpy (varname, str + 2, i);
237 varname[i] = 0;
239 return varname;
242 /* Temporary buffer used by --split-string processing. */
243 struct splitbuf
245 /* Buffer address, arg count, and half the number of elements in the buffer.
246 ARGC and ARGV are as in 'main', and ARGC + 1 <= HALF_ALLOC so
247 that the upper half of ARGV can be used for string contents.
248 This may waste up to half the space but keeps the code simple,
249 which is better for this rarely-used but security-sensitive code.
251 ARGV[0] is not initialized; that is the caller's responsibility
252 after finalization.
254 During assembly, ARGV[I] (where 0 < I < ARGC) contains the offset
255 of the Ith string (relative to ARGV + HALF_ALLOC), so that
256 reallocating ARGV does not change the validity of its contents.
257 The integer offset is cast to char * during assembly, and is
258 converted to a true char * pointer on finalization.
260 During assembly, ARGV[ARGC] contains the offset of the first
261 unused string byte (relative to ARGV + HALF_ALLOC). */
262 char **argv;
263 int argc;
264 idx_t half_alloc;
266 /* The number of extra argv slots to keep room for. */
267 int extra_argc;
269 /* Whether processing should act as if the most recent character
270 seen was a separator. */
271 bool sep;
274 /* Expand SS so that it has at least one more argv slot and at least
275 one more string byte. */
276 static void
277 splitbuf_grow (struct splitbuf *ss)
279 idx_t old_half_alloc = ss->half_alloc;
280 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
281 ss->argv = xpalloc (ss->argv, &ss->half_alloc, 1,
282 MIN (INT_MAX, IDX_MAX), 2 * sizeof *ss->argv);
283 memmove (ss->argv + ss->half_alloc, ss->argv + old_half_alloc, string_bytes);
286 /* In SS, append C to the last string. */
287 static void
288 splitbuf_append_byte (struct splitbuf *ss, char c)
290 idx_t string_bytes = (intptr_t) ss->argv[ss->argc];
291 if (ss->half_alloc * sizeof *ss->argv <= string_bytes)
292 splitbuf_grow (ss);
293 ((char *) (ss->argv + ss->half_alloc))[string_bytes] = c;
294 ss->argv[ss->argc] = (char *) (intptr_t) (string_bytes + 1);
297 /* If SS's most recent character was a separator, finish off its
298 previous argument and start a new one. */
299 static void
300 check_start_new_arg (struct splitbuf *ss)
302 if (ss->sep)
304 splitbuf_append_byte (ss, '\0');
305 int argc = ss->argc;
306 if (ss->half_alloc <= argc + ss->extra_argc + 1)
307 splitbuf_grow (ss);
308 ss->argv[argc + 1] = ss->argv[argc];
309 ss->argc = argc + 1;
310 ss->sep = false;
314 /* All additions to SS have been made. Convert its offsets to pointers,
315 and return the resulting argument vector. */
316 static char **
317 splitbuf_finishup (struct splitbuf *ss)
319 int argc = ss->argc;
320 char **argv = ss->argv;
321 char *stringbase = (char *) (ss->argv + ss->half_alloc);
322 for (int i = 1; i < argc; i++)
323 argv[i] = stringbase + (intptr_t) argv[i];
324 return argv;
327 /* Return a newly-allocated argv-like array,
328 by parsing and splitting the input 'str'.
330 'extra_argc' is the number of additional elements to allocate
331 in the array (on top of the number of args required to split 'str').
333 Store into *argc the number of arguments found (plus 1 for
334 the program name).
336 Example:
337 int argc;
338 char **argv = build_argv ("A=B uname -k', 3, &argc);
339 Results in:
340 argc = 4
341 argv[0] = [not initialized]
342 argv[1] = "A=B"
343 argv[2] = "uname"
344 argv[3] = "-k"
345 argv[4,5,6,7] = [allocated due to extra_argc + 1, but not initialized]
347 To free allocated memory:
348 free (argv);
349 However, 'env' does not free since it's about to exec or exit anyway
350 and the complexity of keeping track of the storage that may have been
351 allocated via multiple calls to build_argv is not worth the hassle. */
352 static char **
353 build_argv (char const *str, int extra_argc, int *argc)
355 bool dq = false, sq = false;
356 struct splitbuf ss;
357 ss.argv = xnmalloc (extra_argc + 2, 2 * sizeof *ss.argv);
358 ss.argc = 1;
359 ss.half_alloc = extra_argc + 2;
360 ss.extra_argc = extra_argc;
361 ss.sep = true;
362 ss.argv[ss.argc] = 0;
364 /* In the following loop,
365 'break' causes the character 'newc' to be added to *dest,
366 'continue' skips the character. */
367 while (*str)
369 char newc = *str; /* Default: add the next character. */
371 switch (*str)
373 case '\'':
374 if (dq)
375 break;
376 sq = !sq;
377 check_start_new_arg (&ss);
378 ++str;
379 continue;
381 case '"':
382 if (sq)
383 break;
384 dq = !dq;
385 check_start_new_arg (&ss);
386 ++str;
387 continue;
389 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
390 /* Start a new argument if outside quotes. */
391 if (sq || dq)
392 break;
393 ss.sep = true;
394 str += strspn (str, C_ISSPACE_CHARS);
395 continue;
397 case '#':
398 if (!ss.sep)
399 break;
400 goto eos; /* '#' as first char terminates the string. */
402 case '\\':
403 /* Backslash inside single-quotes is not special, except \\
404 and \'. */
405 if (sq && str[1] != '\\' && str[1] != '\'')
406 break;
408 /* Skip the backslash and examine the next character. */
409 newc = *++str;
410 switch (newc)
412 case '"': case '#': case '$': case '\'': case '\\':
413 /* Pass escaped character as-is. */
414 break;
416 case '_':
417 if (!dq)
419 ++str; /* '\_' outside double-quotes is arg separator. */
420 ss.sep = true;
421 continue;
423 newc = ' '; /* '\_' inside double-quotes is space. */
424 break;
426 case 'c':
427 if (dq)
428 error (EXIT_CANCELED, 0,
429 _("'\\c' must not appear in double-quoted -S string"));
430 goto eos; /* '\c' terminates the string. */
432 case 'f': newc = '\f'; break;
433 case 'n': newc = '\n'; break;
434 case 'r': newc = '\r'; break;
435 case 't': newc = '\t'; break;
436 case 'v': newc = '\v'; break;
438 case '\0':
439 error (EXIT_CANCELED, 0,
440 _("invalid backslash at end of string in -S"));
442 default:
443 error (EXIT_CANCELED, 0,
444 _("invalid sequence '\\%c' in -S"), newc);
446 break;
448 case '$':
449 /* ${VARNAME} are not expanded inside single-quotes. */
450 if (sq)
451 break;
453 /* Store the ${VARNAME} value. */
455 char *n = extract_varname (str);
456 if (!n)
457 error (EXIT_CANCELED, 0,
458 _("only ${VARNAME} expansion is supported, error at: %s"),
459 str);
461 char *v = getenv (n);
462 if (v)
464 check_start_new_arg (&ss);
465 devmsg ("expanding ${%s} into %s\n", n, quote (v));
466 for (; *v; v++)
467 splitbuf_append_byte (&ss, *v);
469 else
470 devmsg ("replacing ${%s} with null string\n", n);
472 str = strchr (str, '}') + 1;
473 continue;
477 check_start_new_arg (&ss);
478 splitbuf_append_byte (&ss, newc);
479 ++str;
482 if (dq || sq)
483 error (EXIT_CANCELED, 0, _("no terminating quote in -S string"));
485 eos:
486 splitbuf_append_byte (&ss, '\0');
487 *argc = ss.argc;
488 return splitbuf_finishup (&ss);
491 /* Process an "-S" string and create the corresponding argv array.
492 Update the given argc/argv parameters with the new argv.
494 Example: if executed as:
495 $ env -S"-i -C/tmp A=B" foo bar
496 The input argv is:
497 argv[0] = "env"
498 argv[1] = "-S-i -C/tmp A=B"
499 argv[2] = "foo"
500 argv[3] = "bar"
501 argv[4] = nullptr
502 This function will modify argv to be:
503 argv[0] = "env"
504 argv[1] = "-i"
505 argv[2] = "-C/tmp"
506 argv[3] = "A=B"
507 argv[4] = "foo"
508 argv[5] = "bar"
509 argv[6] = nullptr
510 argc will be updated from 4 to 6.
511 optind will be reset to 0 to force getopt_long to rescan all arguments. */
512 static void
513 parse_split_string (char const *str, int *orig_optind,
514 int *orig_argc, char ***orig_argv)
516 int extra_argc = *orig_argc - *orig_optind, newargc;
517 char **newargv = build_argv (str, extra_argc, &newargc);
519 /* Restore argv[0] - the 'env' executable name. */
520 *newargv = (*orig_argv)[0];
522 /* Print parsed arguments. */
523 if (dev_debug && 1 < newargc)
525 devmsg ("split -S: %s\n", quote (str));
526 devmsg (" into: %s\n", quote (newargv[1]));
527 for (int i = 2; i < newargc; i++)
528 devmsg (" & %s\n", quote (newargv[i]));
531 /* Add remaining arguments and terminating null from the original
532 command line. */
533 memcpy (newargv + newargc, *orig_argv + *orig_optind,
534 (extra_argc + 1) * sizeof *newargv);
536 /* Set new values for original getopt variables. */
537 *orig_argc = newargc + extra_argc;
538 *orig_argv = newargv;
539 *orig_optind = 0; /* Tell getopt to restart from first argument. */
542 static void
543 parse_signal_action_params (char const *arg, bool set_default)
545 char *opt_sig;
546 char *optarg_writable;
548 if (! arg)
550 /* Without an argument, reset all signals.
551 Some signals cannot be set to ignore or default (e.g., SIGKILL,
552 SIGSTOP on most OSes, and SIGCONT on AIX.) - so ignore errors. */
553 for (int i = 1 ; i <= SIGNUM_BOUND; i++)
554 signals[i] = set_default ? DEFAULT_NOERR : IGNORE_NOERR;
555 return;
558 optarg_writable = xstrdup (arg);
560 opt_sig = strtok (optarg_writable, ",");
561 while (opt_sig)
563 int signum = operand2sig (opt_sig);
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 (nullptr, ",");
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, nullptr, &act);
596 if (sig_err && !ignore_errors)
597 error (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, nullptr);
604 if (sig_err && !ignore_errors)
605 error (EXIT_CANCELED, errno,
606 _("failed to set signal action for signal %d"), i);
609 if (dev_debug)
611 char signame[SIG2STR_MAX];
612 if (sig2str (i, signame) != 0)
613 snprintf (signame, sizeof signame, "SIG%d", i);
614 devmsg ("Reset signal %s (%d) to %s%s\n",
615 signame, i,
616 set_to_default ? "DEFAULT" : "IGNORE",
617 sig_err ? " (failure ignored)" : "");
623 static void
624 parse_block_signal_params (char const *arg, bool block)
626 char *opt_sig;
627 char *optarg_writable;
629 if (! arg)
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 (! arg)
645 return;
647 optarg_writable = xstrdup (arg);
649 opt_sig = strtok (optarg_writable, ",");
650 while (opt_sig)
652 int signum = operand2sig (opt_sig);
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 if (sigaddset (block ? &block_signals : &unblock_signals, signum) == -1)
661 if (block)
662 error (EXIT_CANCELED, errno,
663 _("failed to block signal %d"), signum);
664 /* else diagnosed in parse_signal_action_params(). */
666 else
667 sigdelset (block ? &unblock_signals : &block_signals, signum);
669 opt_sig = strtok (nullptr, ",");
672 free (optarg_writable);
675 static void
676 set_signal_proc_mask (void)
678 /* Get the existing signal mask */
679 sigset_t set;
680 char const *debug_act;
682 sigemptyset (&set);
684 if (sigprocmask (0, nullptr, &set))
685 error (EXIT_CANCELED, errno, _("failed to get signal process mask"));
687 for (int i = 1; i <= SIGNUM_BOUND; i++)
689 if (sigismember (&block_signals, i))
691 sigaddset (&set, i);
692 debug_act = "BLOCK";
694 else if (sigismember (&unblock_signals, i))
696 sigdelset (&set, i);
697 debug_act = "UNBLOCK";
699 else
701 debug_act = nullptr;
704 if (dev_debug && debug_act)
706 char signame[SIG2STR_MAX];
707 if (sig2str (i, signame) != 0)
708 snprintf (signame, sizeof signame, "SIG%d", i);
709 devmsg ("signal %s (%d) mask set to %s\n",
710 signame, i, debug_act);
714 if (sigprocmask (SIG_SETMASK, &set, nullptr))
715 error (EXIT_CANCELED, errno, _("failed to set signal process mask"));
718 static void
719 list_signal_handling (void)
721 sigset_t set;
722 char signame[SIG2STR_MAX];
724 sigemptyset (&set);
725 if (sigprocmask (0, nullptr, &set))
726 error (EXIT_CANCELED, errno, _("failed to get signal process mask"));
728 for (int i = 1; i <= SIGNUM_BOUND; i++)
730 struct sigaction act;
731 if (sigaction (i, nullptr, &act))
732 continue;
734 char const *ignored = act.sa_handler == SIG_IGN ? "IGNORE" : "";
735 char const *blocked = sigismember (&set, i) ? "BLOCK" : "";
736 char const *connect = *ignored && *blocked ? "," : "";
738 if (! *ignored && ! *blocked)
739 continue;
741 if (sig2str (i, signame) != 0)
742 snprintf (signame, sizeof signame, "SIG%d", i);
743 fprintf (stderr, "%-10s (%2d): %s%s%s\n", signame, i,
744 blocked, connect, ignored);
748 static void
749 initialize_signals (void)
751 signals = xmalloc ((sizeof *signals) * (SIGNUM_BOUND + 1));
753 for (int i = 0 ; i <= SIGNUM_BOUND; i++)
754 signals[i] = UNCHANGED;
756 return;
760 main (int argc, char **argv)
762 int optc;
763 bool ignore_environment = false;
764 bool opt_nul_terminate_output = false;
765 char const *newdir = nullptr;
766 char *argv0 = nullptr;
768 initialize_main (&argc, &argv);
769 set_program_name (argv[0]);
770 setlocale (LC_ALL, "");
771 bindtextdomain (PACKAGE, LOCALEDIR);
772 textdomain (PACKAGE);
774 initialize_exit_failure (EXIT_CANCELED);
775 atexit (close_stdout);
777 initialize_signals ();
779 while ((optc = getopt_long (argc, argv, shortopts, longopts, nullptr)) != -1)
781 switch (optc)
783 case 'a':
784 argv0 = optarg;
785 break;
786 case 'i':
787 ignore_environment = true;
788 break;
789 case 'u':
790 append_unset_var (optarg);
791 break;
792 case 'v':
793 dev_debug = true;
794 break;
795 case '0':
796 opt_nul_terminate_output = true;
797 break;
798 case DEFAULT_SIGNAL_OPTION:
799 parse_signal_action_params (optarg, true);
800 parse_block_signal_params (optarg, false);
801 break;
802 case IGNORE_SIGNAL_OPTION:
803 parse_signal_action_params (optarg, false);
804 break;
805 case BLOCK_SIGNAL_OPTION:
806 parse_block_signal_params (optarg, true);
807 break;
808 case LIST_SIGNAL_HANDLING_OPTION:
809 report_signal_handling = true;
810 break;
811 case 'C':
812 newdir = optarg;
813 break;
814 case 'S':
815 parse_split_string (optarg, &optind, &argc, &argv);
816 break;
817 case ' ': case '\t': case '\n': case '\v': case '\f': case '\r':
818 /* These are undocumented options. Attempt to detect
819 incorrect shebang usage with extraneous space, e.g.:
820 #!/usr/bin/env -i command
821 In which case argv[1] == "-i command". */
822 error (0, 0, _("invalid option -- '%c'"), optc);
823 error (0, 0, _("use -[v]S to pass options in shebang lines"));
824 usage (EXIT_CANCELED);
826 case_GETOPT_HELP_CHAR;
827 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
828 default:
829 usage (EXIT_CANCELED);
833 if (optind < argc && STREQ (argv[optind], "-"))
835 ignore_environment = true;
836 ++optind;
839 if (ignore_environment)
841 devmsg ("cleaning environ\n");
842 static char *dummy_environ[] = { nullptr };
843 environ = dummy_environ;
845 else
846 unset_envvars ();
848 char *eq;
849 while (optind < argc && (eq = strchr (argv[optind], '=')))
851 devmsg ("setenv: %s\n", argv[optind]);
853 if (putenv (argv[optind]))
855 *eq = '\0';
856 error (EXIT_CANCELED, errno, _("cannot set %s"),
857 quote (argv[optind]));
859 optind++;
862 bool program_specified = optind < argc;
864 if (opt_nul_terminate_output && program_specified)
866 error (0, 0, _("cannot specify --null (-0) with command"));
867 usage (EXIT_CANCELED);
870 if (newdir && ! program_specified)
872 error (0, 0, _("must specify command with --chdir (-C)"));
873 usage (EXIT_CANCELED);
876 if (argv0 && ! program_specified)
878 error (0, 0, _("must specify command with --argv0 (-a)"));
879 usage (EXIT_CANCELED);
882 if (! program_specified)
884 /* Print the environment and exit. */
885 char *const *e = environ;
886 while (*e)
887 printf ("%s%c", *e++, opt_nul_terminate_output ? '\0' : '\n');
888 return EXIT_SUCCESS;
891 reset_signal_handlers ();
892 if (sig_mask_changed)
893 set_signal_proc_mask ();
895 if (report_signal_handling)
896 list_signal_handling ();
898 if (newdir)
900 devmsg ("chdir: %s\n", quoteaf (newdir));
902 if (chdir (newdir) != 0)
903 error (EXIT_CANCELED, errno, _("cannot change directory to %s"),
904 quoteaf (newdir));
907 char *program = argv[optind];
908 if (argv0)
910 devmsg ("argv0: %s\n", quoteaf (argv0));
911 argv[optind] = argv0;
914 if (dev_debug)
916 devmsg ("executing: %s\n", program);
917 for (int i=optind; i<argc; ++i)
918 devmsg (" arg[%d]= %s\n", i-optind, quote (argv[i]));
921 execvp (program, &argv[optind]);
923 int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
924 error (0, errno, "%s", quote (program));
926 if (exit_status == EXIT_ENOENT && strpbrk (program, C_ISSPACE_CHARS))
927 error (0, 0, _("use -[v]S to pass options in shebang lines"));
929 main_exit (exit_status);