/* env - run a program in a modified environment                                This is the env utility
   Copyright (C) 1986-2018 Free Software Foundation, Inc.                       
                                                                                
   This program is free software: you can redistribute it and/or modify         
   it under the terms of the GNU General Public License as published by         
   the Free Software Foundation, either version 3 of the License, or            
   (at your option) any later version.                                          
                                                                                
   This program is distributed in the hope that it will be useful,              
   but WITHOUT ANY WARRANTY; without even the implied warranty of               
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                
   GNU General Public License for more details.                                 
                                                                                
   You should have received a copy of the GNU General Public License            
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */   The GNUv3 license
                                                                                
/* Richard Mlynarik and David MacKenzie */                                      
                                                                                
#include <config.h>                                                             Provides system specific information
#include <stdio.h>                                                              Provides standard I/O capability
#include <sys/types.h>                                                          Provides system data types
#include <getopt.h>                                                             ...!includes auto-comment...
#include <c-ctype.h>                                                            ...!includes auto-comment...
                                                                                
#include <assert.h>                                                             ...!includes auto-comment...
#include "system.h"                                                             ...!includes auto-comment...
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "quote.h"                                                              ...!includes auto-comment...
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "env"                                                      Line 32
                                                                                
#define AUTHORS \                                                               Line 34
  proper_name ("Richard Mlynarik"), \                                           Line 35
  proper_name ("David MacKenzie"), \                                            Line 36
  proper_name ("Assaf Gordon")                                                  Line 37
                                                                                
/* array of envvars to unset. */                                                
static const char** usvars;                                                     Line 40
static size_t usvars_alloc;                                                     Line 41
static size_t usvars_used;                                                      Line 42
                                                                                
/* Annotate the output with extra info to aid the user.  */                     
static bool dev_debug;                                                          Line 45
                                                                                
/* buffer and length of extracted envvars in -S strings. */                     
static char *varname;                                                           Line 48
static size_t vnlen;                                                            Line 49
                                                                                
static char const shortopts[] = "+C:iS:u:v0 \t";                                Line 51
                                                                                
static struct option const longopts[] =                                         Line 53
{                                                                               
  {"ignore-environment", no_argument, NULL, 'i'},                               Line 55
  {"null", no_argument, NULL, '0'},                                             Line 56
  {"unset", required_argument, NULL, 'u'},                                      Line 57
  {"chdir", required_argument, NULL, 'C'},                                      Line 58
  {"debug", no_argument, NULL, 'v'},                                            Line 59
  {"split-string", required_argument, NULL, 'S'},                               Line 60
  {GETOPT_HELP_OPTION_DECL},                                                    Line 61
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 62
  {NULL, 0, NULL, 0}                                                            Line 63
};                                                                              Block 1
                                                                                
void                                                                            Line 66
usage (int status)                                                              Line 67
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 69
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 71
    {                                                                           
      printf (_("\                                                              Line 73
Usage: %s [OPTION]... [-] [NAME=VALUE]... [COMMAND [ARG]...]\n"),               Line 74
              program_name);                                                    Line 75
      fputs (_("\                                                               Line 76
Set each NAME to VALUE in the environment and run COMMAND.\n\                   Line 77
"), stdout);                                                                    Line 78
                                                                                
      emit_mandatory_arg_note ();                                               ...!common auto-comment...
                                                                                
      fputs (_("\                                                               Line 82
  -i, --ignore-environment  start with an empty environment\n\                  Line 83
  -0, --null           end each output line with NUL, not newline\n\            Line 84
  -u, --unset=NAME     remove variable from the environment\n\                  Line 85
"), stdout);                                                                    Line 86
      fputs (_("\                                                               Line 87
  -C, --chdir=DIR      change working directory to DIR\n\                       Line 88
"), stdout);                                                                    Line 89
      fputs (_("\                                                               Line 90
  -S, --split-string=S  process and split S into separate arguments;\n\         Line 91
                        used to pass multiple arguments on shebang lines\n\     Line 92
  -v, --debug          print verbose information for each processing step\n\    Line 93
"), stdout);                                                                    Line 94
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 95
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 96
      fputs (_("\                                                               Line 97
\n\                                                                             
A mere - implies -i.  If no COMMAND, print the resulting environment.\n\        Line 99
"), stdout);                                                                    Line 100
      emit_ancillary_info (PROGRAM_NAME);                                       Line 101
    }                                                                           
  exit (status);                                                                Line 103
}                                                                               Block 2
                                                                                
static void                                                                     Line 106
append_unset_var (const char *var)                                              Line 107
{                                                                               
  if (usvars_used == usvars_alloc)                                              Line 109
    usvars = x2nrealloc (usvars, &usvars_alloc, sizeof *usvars);                Line 110
  usvars[usvars_used++] = var;                                                  Line 111
}                                                                               Block 3
                                                                                
static void                                                                     Line 114
unset_envvars (void)                                                            Line 115
{                                                                               
  for (size_t i = 0; i < usvars_used; ++i)                                      Line 117
    {                                                                           
      devmsg ("unset:    %s\n", usvars[i]);                                     Line 119
                                                                                
      if (unsetenv (usvars[i]))                                                 Line 121
        die (EXIT_CANCELED, errno, _("cannot unset %s"),                        Line 122
             quote (usvars[i]));                                                Line 123
    }                                                                           
                                                                                
  IF_LINT (free (usvars));                                                      Line 126
  IF_LINT (usvars = NULL);                                                      Line 127
  IF_LINT (usvars_used = 0);                                                    Line 128
  IF_LINT (usvars_alloc = 0);                                                   Line 129
}                                                                               Block 4
                                                                                
static bool _GL_ATTRIBUTE_PURE                                                  Line 132
valid_escape_sequence (const char c)                                            Line 133
{                                                                               
  return (c == 'c' || c == 'f' || c == 'n' || c == 'r' || c == 't' || c == 'v' \Line 135
          || c == '#' || c == '$' || c == '_' || c == '"' || c == '\'' \        Line 136
          || c == '\\');                                                        Line 137
}                                                                               Block 5
                                                                                
static char _GL_ATTRIBUTE_PURE                                                  Line 140
escape_char (const char c)                                                      Line 141
{                                                                               
  switch (c)                                                                    Line 143
    {                                                                           
    /* \a,\b not supported by FreeBSD's env. */                                 
    case 'f': return '\f';                                                      Line 146
    case 'n': return '\n';                                                      Line 147
    case 'r': return '\r';                                                      Line 148
    case 't': return '\t';                                                      Line 149
    case 'v': return '\v';                                                      Line 150
    default:  assert (0);                           /* LCOV_EXCL_LINE */        Line 151
    }                                                                           
}                                                                               Block 6
                                                                                
/* Return a pointer to the end of a valid ${VARNAME} string, or NULL.           
   'str' should point to the '$' character.                                     
   First letter in VARNAME must be alpha or underscore,                         
   rest of letters are alnum or underscore. Any other character is an error. */ 
static const char* _GL_ATTRIBUTE_PURE                                           Line 159
scan_varname (const char* str)                                                  Line 160
{                                                                               
  assert (str && *str == '$');                      /* LCOV_EXCL_LINE */        Line 162
  if ( *(str+1) == '{' && (c_isalpha (*(str+2)) || *(str+2) == '_'))            Line 163
    {                                                                           
      const char* end = str+3;                                                  Line 165
      while (c_isalnum (*end) || *end == '_')                                   Line 166
        ++end;                                                                  Line 167
      if (*end == '}')                                                          Line 168
        return end;                                                             Line 169
    }                                                                           
                                                                                
  return NULL;                                                                  Line 172
}                                                                               Block 7
                                                                                
/* Return a pointer to a static buffer containing the VARNAME as                
   extracted from a '${VARNAME}' string.                                        
   The returned string will be NUL terminated.                                  
   The returned pointer should not be freed.                                    
   Return NULL if not a valid ${VARNAME} syntax. */                             
static char*                                                                    Line 180
extract_varname (const char* str)                                               Line 181
{                                                                               
  ptrdiff_t i;                                                                  Line 183
  const char* p;                                                                Line 184
                                                                                
  p = scan_varname (str);                                                       Line 186
  if (!p)                                                                       Line 187
    return NULL;                                                                Line 188
                                                                                
  /* -2 and +2 (below) account for the '${' prefix. */                          
  i = p - str - 2;                                                              Line 191
                                                                                
  if (i >= vnlen)                                                               Line 193
    {                                                                           
      vnlen = i + 1;                                                            Line 195
      varname = xrealloc (varname, vnlen);                                      Line 196
    }                                                                           
                                                                                
  memcpy (varname, str+2, i);                                                   Line 199
  varname[i]=0;                                                                 Line 200
                                                                                
  return varname;                                                               Line 202
}                                                                               Block 8
                                                                                
/* Validate the "-S" parameter, according to the syntax defined by FreeBSD's    
   env(1). Terminate with an error message if not valid.                        
                                                                                
   Calculate and set two values:                                                
   bufsize - the size (in bytes) required to hold the resulting string          
             after ENVVAR expansion (the value is overestimated).               
   maxargc - the maximum number of arguments (the size of the new argv). */     
static void                                                                     Line 212
validate_split_str (const char* str, size_t* /*out*/ bufsize,                   Line 213
                    int* /*out*/ maxargc)                                       Line 214
{                                                                               
  bool dq, sq, sp;                                                              Line 216
  const char *pch;                                                              Line 217
  size_t buflen;                                                                Line 218
  int cnt = 1;                                                                  Line 219
                                                                                
  assert (str && str[0] && !isspace (str[0]));     /* LCOV_EXCL_LINE */         Line 221
                                                                                
  dq = sq = sp = false;                                                         Line 223
  buflen = strlen (str)+1;                                                      Line 224
                                                                                
  while (*str)                                                                  Line 226
    {                                                                           
      const char next = *(str+1);                                               Line 228
                                                                                
      if (isspace (*str) && !dq && !sq)                                         Line 230
        {                                                                       
          sp = true;                                                            Line 232
        }                                                                       
      else                                                                      Line 234
        {                                                                       
          if (sp)                                                               Line 236
            ++cnt;                                                              Line 237
          sp = false;                                                           Line 238
        }                                                                       
                                                                                
      switch (*str)                                                             Line 241
        {                                                                       
        case '\'':                                                              Line 243
          assert (!(sq && dq));                           /* LCOV_EXCL_LINE */  Line 244
          sq = !sq && !dq;                                                      Line 245
          break;                                                                Line 246
                                                                                
        case '"':                                                               Line 248
          assert (!(sq && dq));                           /* LCOV_EXCL_LINE */  Line 249
          dq = !sq && !dq;                                                      Line 250
          break;                                                                Line 251
                                                                                
        case '\\':                                                              Line 253
          if (dq && next == 'c')                                                Line 254
            die (EXIT_CANCELED, 0,                                              Line 255
                 _("'\\c' must not appear in double-quoted -S string"));        Line 256
                                                                                
          if (next == '\0')                                                     Line 258
            die (EXIT_CANCELED, 0,                                              Line 259
                 _("invalid backslash at end of string in -S"));                Line 260
                                                                                
          if (!valid_escape_sequence (next))                                    Line 262
            die (EXIT_CANCELED, 0, _("invalid sequence '\\%c' in -S"), next);   Line 263
                                                                                
          if (next == '_')                                                      Line 265
            ++cnt;                                                              Line 266
                                                                                
          ++str;                                                                Line 268
          break;                                                                Line 269
                                                                                
                                                                                
        case '$':                                                               Line 272
          if (sq)                                                               Line 273
            break;                                                              Line 274
                                                                                
          if (!(pch = extract_varname (str)))                                   Line 276
            die (EXIT_CANCELED, 0, _("only ${VARNAME} expansion is supported,"\ Line 277
                                     " error at: %s"), str);                    Line 278
                                                                                
          if ((pch = getenv (pch)))                                             Line 280
            buflen += strlen (pch);                                             Line 281
          break;                                                                Line 282
        }                                                                       
      ++str;                                                                    Line 284
    }                                                                           
                                                                                
  if (dq || sq)                                                                 Line 287
    die (EXIT_CANCELED, 0, _("no terminating quote in -S string"));             Line 288
                                                                                
  *maxargc = cnt;                                                               Line 290
  *bufsize = buflen;                                                            Line 291
}                                                                               Block 9
                                                                                
/* Return a newly-allocated *arg[]-like array,                                  
   by parsing and splitting the input 'str'.                                    
   'extra_argc' is the number of additional elements to allocate                
   in the array (on top of the number of args required to split 'str').         
                                                                                
   Example:                                                                     
     char **argv = build_argv ("A=B uname -k', 3)                               
   Results in:                                                                  
     argv[0] = "DUMMY" - dummy executable name, can be replaced later.          
     argv[1] = "A=B"                                                            
     argv[2] = "uname"                                                          
     argv[3] = "-k"                                                             
     argv[4] = NULL                                                             
     argv[5,6,7] = [allocated due to extra_argc, but not initialized]           
                                                                                
   The strings are stored in an allocated buffer, pointed by argv[0].           
   To free allocated memory:                                                    
     free (argv[0]);                                                            
     free (argv); */                                                            
static char**                                                                   Line 313
build_argv (const char* str, int extra_argc)                                    Line 314
{                                                                               
  bool dq = false, sq = false, sep = true;                                      Line 316
  char *dest;    /* buffer to hold the new argv values. allocated as one buffer,Line 317
                    but will contain multiple NUL-terminate strings. */         
  char **newargv, **nextargv;                                                   Line 319
  int newargc = 0;                                                              Line 320
  size_t buflen = 0;                                                            Line 321
                                                                                
  /* This macro is called before inserting any characters to the output         
     buffer. It checks if the previous character was a separator                
     and if so starts a new argv element. */                                    
#define CHECK_START_NEW_ARG                     \                               Line 326
  do {                                          \                               Line 327
    if (sep)                                    \                               Line 328
      {                                         \                               Line 329
        *dest++ = '\0';                         \                               Line 330
        *nextargv++ = dest;                     \                               Line 331
        sep = false;                            \                               Line 332
      }                                         \                               Line 333
  } while (0)                                                                   Line 334
                                                                                
  assert (str && str[0] && !isspace (str[0]));             /* LCOV_EXCL_LINE */ Line 336
                                                                                
  validate_split_str (str, &buflen, &newargc);                                  Line 338
                                                                                
  /* allocate buffer. +6 for the "DUMMY\0" executable name, +1 for NUL. */      
  dest = xmalloc (buflen + 6 + 1);                                              Line 341
                                                                                
  /* allocate the argv array.                                                   
     +2 for the program name (argv[0]) and the last NULL pointer. */            
  nextargv = newargv = xmalloc ((newargc + extra_argc + 2) * sizeof (char *));  Line 345
                                                                                
  /* argv[0] = executable's name  - will be replaced later. */                  
  strcpy (dest, "DUMMY");                                                       Line 348
  *nextargv++ = dest;                                                           Line 349
  dest += 6;                                                                    Line 350
                                                                                
  /* In the following loop,                                                     
     'break' causes the character 'newc' to be added to *dest,                  
     'continue' skips the character. */                                         
  while (*str)                                                                  Line 355
    {                                                                           
      char newc = *str; /* default: add the next character. */                  Line 357
                                                                                
      switch (*str)                                                             Line 359
        {                                                                       
        case '\'':                                                              Line 361
          if (dq)                                                               Line 362
            break;                                                              Line 363
          sq = !sq;                                                             Line 364
          CHECK_START_NEW_ARG;                                                  Line 365
          ++str;                                                                Line 366
          continue;                                                             Line 367
                                                                                
        case '"':                                                               Line 369
          if (sq)                                                               Line 370
            break;                                                              Line 371
          dq = !dq;                                                             Line 372
          CHECK_START_NEW_ARG;                                                  Line 373
          ++str;                                                                Line 374
          continue;                                                             Line 375
                                                                                
        case ' ':                                                               Line 377
        case '\t':                                                              Line 378
          /* space/tab outside quotes starts a new argument. */                 
          if (sq || dq)                                                         Line 380
            break;                                                              Line 381
          sep = true;                                                           Line 382
          str += strspn (str, " \t"); /* skip whitespace. */                    Line 383
          continue;                                                             Line 384
                                                                                
        case '#':                                                               Line 386
          if (!sep)                                                             Line 387
            break;                                                              Line 388
          goto eos; /* '#' as first char terminates the string. */              Line 389
                                                                                
        case '\\':                                                              Line 391
          /* backslash inside single-quotes is not special, except \\ and \'. */
          if (sq && *(str+1) != '\\' && *(str+1) != '\'')                       Line 393
            break;                                                              Line 394
                                                                                
          /* skip the backslash and examine the next character. */              
          newc = *(++str);                                                      Line 397
          if ((newc == '\\' || newc == '\'')                                    Line 398
              || (!sq && (newc == '#' || newc == '$' || newc == '"')))          Line 399
            {                                                                   
              /* Pass escaped character as-is. */                               
            }                                                                   
          else if (newc == '_')                                                 Line 403
            {                                                                   
              if (!dq)                                                          Line 405
                {                                                               
                  ++str;  /* '\_' outside double-quotes is arg separator. */    Line 407
                  sep = true;                                                   Line 408
                  continue;                                                     Line 409
                }                                                               
              else                                                              Line 411
                  newc = ' ';  /* '\_' inside double-quotes is space. */        Line 412
            }                                                                   
          else if (newc == 'c')                                                 Line 414
              goto eos; /* '\c' terminates the string. */                       Line 415
          else                                                                  Line 416
              newc = escape_char (newc); /* other characters (e.g. '\n'). */    Line 417
          break;                                                                Line 418
                                                                                
        case '$':                                                               Line 420
          /* ${VARNAME} are not expanded inside single-quotes. */               
          if (sq)                                                               Line 422
            break;                                                              Line 423
                                                                                
          /* Store the ${VARNAME} value. Error checking omitted as              
             the ${VARNAME} was already validated. */                           
          {                                                                     
            char *n = extract_varname (str);                                    Line 428
            char *v = getenv (n);                                               Line 429
            if (v)                                                              Line 430
              {                                                                 
                CHECK_START_NEW_ARG;                                            Line 432
                devmsg ("expanding ${%s} into %s\n", n, quote (v));             Line 433
                dest = stpcpy (dest, v);                                        Line 434
              }                                                                 
            else                                                                Line 436
              devmsg ("replacing ${%s} with null string\n", n);                 Line 437
                                                                                
            str = strchr (str, '}') + 1;                                        Line 439
            continue;                                                           Line 440
          }                                                                     
                                                                                
        }                                                                       
                                                                                
      CHECK_START_NEW_ARG;                                                      Line 445
      *dest++ = newc;                                                           Line 446
      ++str;                                                                    Line 447
    }                                                                           Block 10
                                                                                
 eos:                                                                           Line 450
  *dest = '\0';                                                                 Line 451
  *nextargv = NULL; /* mark the last element in argv as NULL. */                Line 452
                                                                                
  return newargv;                                                               Line 454
}                                                                               
                                                                                
/* Process an "-S" string and create the corresponding argv array.              
   Update the given argc/argv parameters with the new argv.                     
                                                                                
   Example: if executed as:                                                     
      $ env -S"-i -C/tmp A=B" foo bar                                           
   The input argv is:                                                           
      argv[0] = 'env'                                                           
      argv[1] = "-S-i -C/tmp A=B"                                               
      argv[2] = foo                                                             
      argv[3] = bar                                                             
   This function will modify argv to be:                                        
      argv[0] = 'env'                                                           
      argv[1] = "-i"                                                            
      argv[2] = "-C/tmp"                                                        
      argv[3] =  A=B"                                                           
      argv[4] = foo                                                             
      argv[5] = bar                                                             
   argc will be updated from 4 to 6.                                            
   optind will be reset to 0 to force getopt_long to rescan all arguments. */   
static void                                                                     Line 476
parse_split_string (const char* str, int /*out*/ *orig_optind,                  Line 477
                    int /*out*/ *orig_argc, char*** /*out*/ orig_argv)          Line 478
{                                                                               
  int i, newargc;                                                               Line 480
  char **newargv, **nextargv;                                                   Line 481
                                                                                
                                                                                
  while (isspace (*str))                                                        Line 484
    str++;                                                                      Line 485
  if (*str == '\0')                                                             Line 486
    return;                                                                     Line 487
                                                                                
  newargv = build_argv (str, *orig_argc - *orig_optind);                        Line 489
                                                                                
  /* restore argv[0] - the 'env' executable name */                             
  *newargv = (*orig_argv)[0];                                                   Line 492
                                                                                
  /* Start from argv[1] */                                                      
  nextargv = newargv + 1;                                                       Line 495
                                                                                
  /* Print parsed arguments */                                                  
  if (dev_debug && *nextargv)                                                   Line 498
    {                                                                           
      devmsg ("split -S:  %s\n", quote (str));                                  Line 500
      devmsg (" into:    %s\n", quote (*nextargv++));                           Line 501
      while (*nextargv)                                                         Line 502
        devmsg ("     &    %s\n", quote (*nextargv++));                         Line 503
    }                                                                           Block 11
  else                                                                          Line 505
    {                                                                           
      /* Ensure nextargv points to the last argument */                         
      while (*nextargv)                                                         Line 508
        ++nextargv;                                                             Line 509
    }                                                                           
                                                                                
  /* Add remaining arguments from original command line */                      
  for (i = *orig_optind; i < *orig_argc; ++i)                                   Line 513
    *nextargv++ = (*orig_argv)[i];                                              Line 514
  *nextargv = NULL;                                                             Line 515
                                                                                
  /* Count how many new arguments we have */                                    
  newargc = 0;                                                                  Line 518
  for (nextargv = newargv; *nextargv; ++nextargv)                               Line 519
    ++newargc;                                                                  Line 520
                                                                                
  /* set new values for original getopt variables */                            
  *orig_argc = newargc;                                                         Line 523
  *orig_argv = newargv;                                                         Line 524
  *orig_optind = 0; /* tell getopt to restart from first argument */            Line 525
}                                                                               
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 529
{                                                                               
  int optc;                                                                     Line 531
  bool ignore_environment = false;                                              Line 532
  bool opt_nul_terminate_output = false;                                        Line 533
  char const *newdir = NULL;                                                    Line 534
                                                                                
  initialize_main (&argc, &argv);                                               VMS-specific entry point handling wildcard expansion
  set_program_name (argv[0]);                                                   Retains program name and discards path
  setlocale (LC_ALL, "");                                                       Sets up internationalization (i18n)
  bindtextdomain (PACKAGE, LOCALEDIR);                                          Assigns i18n directorySets text domain for _() [gettext()] function
  textdomain (PACKAGE);                                                         Sets text domain for _() [gettext()] function
                                                                                
  initialize_exit_failure (EXIT_CANCELED);                                      Line 542
  atexit (close_stdout);                                                        Close stdout on exit (see gnulib)
                                                                                
  while ((optc = getopt_long (argc, argv, shortopts, longopts, NULL)) != -1)    Line 545
    {                                                                           
      switch (optc)                                                             Line 547
        {                                                                       
        case 'i':                                                               Line 549
          ignore_environment = true;                                            Line 550
          break;                                                                Line 551
        case 'u':                                                               Line 552
          append_unset_var (optarg);                                            Line 553
          break;                                                                Line 554
        case 'v':                                                               Line 555
          dev_debug = true;                                                     Line 556
          break;                                                                Line 557
        case '0':                                                               Line 558
          opt_nul_terminate_output = true;                                      Line 559
          break;                                                                Line 560
        case 'C':                                                               Line 561
          newdir = optarg;                                                      Line 562
          break;                                                                Line 563
        case 'S':                                                               Line 564
          parse_split_string (optarg, &optind, &argc, &argv);                   Line 565
          break;                                                                Line 566
        case ' ':                                                               Line 567
        case '\t':                                                              Line 568
          /* These are undocumented options. Attempt to detect                  
             incorrect shebang usage with extraneous space, e.g.:               
                #!/usr/bin/env -i command                                       
             In which case argv[1] == "-i command".  */                         
          error (0, 0, _("invalid option -- '%c'"), optc);                      Line 573
          error (0, 0, _("use -[v]S to pass options in shebang lines"));        Line 574
          usage (EXIT_CANCELED);                                                Line 575
                                                                                
        case_GETOPT_HELP_CHAR;                                                  Line 577
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 578
        default:                                                                Line 579
          usage (EXIT_CANCELED);                                                Line 580
        }                                                                       
    }                                                                           Block 13
                                                                                
  if (optind < argc && STREQ (argv[optind], "-"))                               Line 584
    {                                                                           
      ignore_environment = true;                                                Line 586
      ++optind;                                                                 Line 587
    }                                                                           Block 14
                                                                                
  if (ignore_environment)                                                       Line 590
    {                                                                           
      devmsg ("cleaning environ\n");                                            Line 592
      static char *dummy_environ[] = { NULL };                                  Line 593
      environ = dummy_environ;                                                  Line 594
    }                                                                           Block 15
  else                                                                          Line 596
    unset_envvars ();                                                           Line 597
                                                                                
  char *eq;                                                                     Line 599
  while (optind < argc && (eq = strchr (argv[optind], '=')))                    Line 600
    {                                                                           
      devmsg ("setenv:   %s\n", argv[optind]);                                  Line 602
                                                                                
      if (putenv (argv[optind]))                                                Line 604
        {                                                                       
          *eq = '\0';                                                           Line 606
          die (EXIT_CANCELED, errno, _("cannot set %s"),                        Line 607
               quote (argv[optind]));                                           Line 608
        }                                                                       
      optind++;                                                                 Line 610
    }                                                                           Block 16
                                                                                
  bool program_specified = optind < argc;                                       Line 613
                                                                                
  if (opt_nul_terminate_output && program_specified)                            Line 615
    {                                                                           
      error (0, 0, _("cannot specify --null (-0) with command"));               Line 617
      usage (EXIT_CANCELED);                                                    Line 618
    }                                                                           Block 17
                                                                                
  if (newdir && ! program_specified)                                            Line 621
    {                                                                           
      error (0, 0, _("must specify command with --chdir (-C)"));                Line 623
      usage (EXIT_CANCELED);                                                    Line 624
    }                                                                           Block 18
                                                                                
  if (! program_specified)                                                      Line 627
    {                                                                           
      /* Print the environment and exit. */                                     
      char *const *e = environ;                                                 Line 630
      while (*e)                                                                Line 631
        printf ("%s%c", *e++, opt_nul_terminate_output ? '\0' : '\n');          Line 632
      return EXIT_SUCCESS;                                                      Line 633
    }                                                                           
                                                                                
  if (newdir)                                                                   Line 636
    {                                                                           
      devmsg ("chdir:    %s\n", quoteaf (newdir));                              Line 638
                                                                                
      if (chdir (newdir) != 0)                                                  Line 640
        die (EXIT_CANCELED, errno, _("cannot change directory to %s"),          Line 641
             quoteaf (newdir));                                                 Line 642
    }                                                                           Block 20
                                                                                
  if (dev_debug)                                                                Line 645
    {                                                                           
      devmsg ("executing: %s\n", argv[optind]);                                 Line 647
      for (int i=optind; i<argc; ++i)                                           Line 648
        devmsg ("   arg[%d]= %s\n", i-optind, quote (argv[i]));                 Line 649
    }                                                                           Block 21
                                                                                
  execvp (argv[optind], &argv[optind]);                                         Line 652
                                                                                
  int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;         Line 654
  error (0, errno, "%s", quote (argv[optind]));                                 Line 655
                                                                                
  if (exit_status == EXIT_ENOENT && strchr (argv[optind], ' '))                 Line 657
    error (0, 0, _("use -[v]S to pass options in shebang lines"));              Line 658
                                                                                
  return exit_status;                                                           Line 660
}