/* dircolors - output commands to set the LS_COLOR environment variable         This is the dircolors utility
   Copyright (C) 1996-2018 Free Software Foundation, Inc.                       
   Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000 H. Peter Anvin              
                                                                                
   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
                                                                                
#include <config.h>                                                             Provides system specific information
                                                                                
#include <sys/types.h>                                                          Provides system data types
#include <fnmatch.h>                                                            ...!includes auto-comment...
#include <getopt.h>                                                             ...!includes auto-comment...
                                                                                
#include "system.h"                                                             ...!includes auto-comment...
#include "dircolors.h"                                                          ...!includes auto-comment...
#include "c-strcase.h"                                                          ...!includes auto-comment...
#include "die.h"                                                                ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "obstack.h"                                                            ...!includes auto-comment...
#include "quote.h"                                                              ...!includes auto-comment...
#include "stdio--.h"                                                            ...!includes auto-comment...
#include "xstrndup.h"                                                           ...!includes auto-comment...
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "dircolors"                                                Line 35
                                                                                
#define AUTHORS proper_name ("H. Peter Anvin")                                  Line 37
                                                                                
#define obstack_chunk_alloc malloc                                              Line 39
#define obstack_chunk_free free                                                 Line 40
                                                                                
enum Shell_syntax                                                               Line 42
{                                                                               
  SHELL_SYNTAX_BOURNE,                                                          Line 44
  SHELL_SYNTAX_C,                                                               Line 45
  SHELL_SYNTAX_UNKNOWN                                                          Line 46
};                                                                              Block 1
                                                                                
#define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)                          Line 49
#define APPEND_TWO_CHAR_STRING(S)     \                                         Line 50
  do         \                                                                  Line 51
    {         \                                                                 Line 52
      APPEND_CHAR (S[0]);      \                                                Line 53
      APPEND_CHAR (S[1]);      \                                                Line 54
    }         \                                                                 Line 55Block 2
  while (0)                                                                     Line 56
                                                                                
/* Accumulate in this obstack the value for the LS_COLORS environment           
   variable.  */                                                                
static struct obstack lsc_obstack;                                              Line 60
                                                                                
static const char *const slack_codes[] =                                        Line 62
{                                                                               
  "NORMAL", "NORM", "FILE", "RESET", "DIR", "LNK", "LINK",                      Line 64
  "SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK",       Line 65
  "CHR", "CHAR", "DOOR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE",      Line 66
  "END", "ENDCODE", "SUID", "SETUID", "SGID", "SETGID", "STICKY",               Line 67
  "OTHER_WRITABLE", "OWR", "STICKY_OTHER_WRITABLE", "OWT", "CAPABILITY",        Line 68
  "MULTIHARDLINK", "CLRTOEOL", NULL                                             Line 69
};                                                                              Block 3
                                                                                
static const char *const ls_codes[] =                                           Line 72
{                                                                               
  "no", "no", "fi", "rs", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",       Line 74
  "so", "bd", "bd", "cd", "cd", "do", "ex", "lc", "lc", "rc", "rc", "ec", "ec", Line 75
  "su", "su", "sg", "sg", "st", "ow", "ow", "tw", "tw", "ca", "mh", "cl", NULL  Line 76
};                                                                              Block 4
verify (ARRAY_CARDINALITY (slack_codes) == ARRAY_CARDINALITY (ls_codes));       Line 78
                                                                                
static struct option const long_options[] =                                     Line 80
  {                                                                             
    {"bourne-shell", no_argument, NULL, 'b'},                                   Line 82
    {"sh", no_argument, NULL, 'b'},                                             Line 83
    {"csh", no_argument, NULL, 'c'},                                            Line 84
    {"c-shell", no_argument, NULL, 'c'},                                        Line 85
    {"print-database", no_argument, NULL, 'p'},                                 Line 86
    {GETOPT_HELP_OPTION_DECL},                                                  Line 87
    {GETOPT_VERSION_OPTION_DECL},                                               Line 88
    {NULL, 0, NULL, 0}                                                          Line 89
  };                                                                            Block 5
                                                                                
void                                                                            Line 92
usage (int status)                                                              Line 93
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 95
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 97
    {                                                                           
      printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);               Line 99
      fputs (_("\                                                               Line 100
Output commands to set the LS_COLORS environment variable.\n\                   Line 101
\n\                                                                             
Determine format of output:\n\                                                  Line 103
  -b, --sh, --bourne-shell    output Bourne shell code to set LS_COLORS\n\      Line 104
  -c, --csh, --c-shell        output C shell code to set LS_COLORS\n\           Line 105
  -p, --print-database        output defaults\n\                                Line 106
"), stdout);                                                                    Line 107
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 108
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 109
      fputs (_("\                                                               Line 110
\n\                                                                             
If FILE is specified, read it to determine which colors to use for which\n\     Line 112
file types and extensions.  Otherwise, a precompiled database is used.\n\       Line 113
For details on the format of these files, run 'dircolors --print-database'.\n\  Line 114
"), stdout);                                                                    Line 115
      emit_ancillary_info (PROGRAM_NAME);                                       Line 116
    }                                                                           
                                                                                
  exit (status);                                                                Line 119
}                                                                               Block 6
                                                                                
/* If the SHELL environment variable is set to 'csh' or 'tcsh,'                 
   assume C shell.  Else Bourne shell.  */                                      
                                                                                
static enum Shell_syntax                                                        Line 125
guess_shell_syntax (void)                                                       Line 126
{                                                                               
  char *shell;                                                                  Line 128
                                                                                
  shell = getenv ("SHELL");                                                     Line 130
  if (shell == NULL || *shell == '\0')                                          Line 131
    return SHELL_SYNTAX_UNKNOWN;                                                Line 132
                                                                                
  shell = last_component (shell);                                               Line 134
                                                                                
  if (STREQ (shell, "csh") || STREQ (shell, "tcsh"))                            Line 136
    return SHELL_SYNTAX_C;                                                      Line 137
                                                                                
  return SHELL_SYNTAX_BOURNE;                                                   Line 139
}                                                                               Block 7
                                                                                
static void                                                                     Line 142
parse_line (char const *line, char **keyword, char **arg)                       Line 143
{                                                                               
  char const *p;                                                                Line 145
  char const *keyword_start;                                                    Line 146
  char const *arg_start;                                                        Line 147
                                                                                
  *keyword = NULL;                                                              Line 149
  *arg = NULL;                                                                  Line 150
                                                                                
  for (p = line; isspace (to_uchar (*p)); ++p)                                  Line 152
    continue;                                                                   Line 153
                                                                                
  /* Ignore blank lines and shell-style comments.  */                           
  if (*p == '\0' || *p == '#')                                                  Line 156
    return;                                                                     Line 157
                                                                                
  keyword_start = p;                                                            Line 159
                                                                                
  while (!isspace (to_uchar (*p)) && *p != '\0')                                Line 161
    {                                                                           
      ++p;                                                                      Line 163
    }                                                                           
                                                                                
  *keyword = xstrndup (keyword_start, p - keyword_start);                       Line 166
  if (*p  == '\0')                                                              Line 167
    return;                                                                     Line 168
                                                                                
  do                                                                            
    {                                                                           
      ++p;                                                                      Line 172
    }                                                                           
  while (isspace (to_uchar (*p)));                                              Line 174
                                                                                
  if (*p == '\0' || *p == '#')                                                  Line 176
    return;                                                                     Line 177
                                                                                
  arg_start = p;                                                                Line 179
                                                                                
  while (*p != '\0' && *p != '#')                                               Line 181
    ++p;                                                                        Line 182
                                                                                
  for (--p; isspace (to_uchar (*p)); --p)                                       Line 184
    continue;                                                                   Line 185
  ++p;                                                                          Line 186
                                                                                
  *arg = xstrndup (arg_start, p - arg_start);                                   Line 188
}                                                                               Block 8
                                                                                
/* FIXME: Write a string to standard out, while watching for "dangerous"        
   sequences like unescaped : and = characters.  */                             
                                                                                
static void                                                                     Line 194
append_quoted (const char *str)                                                 Line 195
{                                                                               
  bool need_backslash = true;                                                   Line 197
                                                                                
  while (*str != '\0')                                                          Line 199
    {                                                                           
      switch (*str)                                                             Line 201
        {                                                                       
        case '\'':                                                              Line 203
          APPEND_CHAR ('\'');                                                   Line 204
          APPEND_CHAR ('\\');                                                   Line 205
          APPEND_CHAR ('\'');                                                   Line 206
          need_backslash = true;                                                Line 207
          break;                                                                Line 208
                                                                                
        case '\\':                                                              Line 210
        case '^':                                                               Line 211
          need_backslash = !need_backslash;                                     Line 212
          break;                                                                Line 213
                                                                                
        case ':':                                                               Line 215
        case '=':                                                               Line 216
          if (need_backslash)                                                   Line 217
            APPEND_CHAR ('\\');                                                 Line 218
          FALLTHROUGH;                                                          Line 219
                                                                                
        default:                                                                Line 221
          need_backslash = true;                                                Line 222
          break;                                                                Line 223
        }                                                                       
                                                                                
      APPEND_CHAR (*str);                                                       Line 226
      ++str;                                                                    Line 227
    }                                                                           
}                                                                               Block 9
                                                                                
/* Read the file open on FP (with name FILENAME).  First, look for a            
   'TERM name' directive where name matches the current terminal type.          
   Once found, translate and accumulate the associated directives onto          
   the global obstack LSC_OBSTACK.  Give a diagnostic                           
   upon failure (unrecognized keyword is the only way to fail here).            
   Return true if successful.  */                                               
                                                                                
static bool                                                                     Line 238
dc_parse_stream (FILE *fp, const char *filename)                                Line 239
{                                                                               
  size_t line_number = 0;                                                       Line 241
  char const *next_G_line = G_line;                                             Line 242
  char *input_line = NULL;                                                      Line 243
  size_t input_line_size = 0;                                                   Line 244
  char const *line;                                                             Line 245
  char const *term;                                                             Line 246
  bool ok = true;                                                               Line 247
                                                                                
  /* State for the parser.  */                                                  
  enum { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL } state = ST_GLOBAL;     Line 250
                                                                                
  /* Get terminal type */                                                       
  term = getenv ("TERM");                                                       Line 253
  if (term == NULL || *term == '\0')                                            Line 254
    term = "none";                                                              Line 255
                                                                                
  while (1)                                                                     Line 257
    {                                                                           
      char *keywd, *arg;                                                        Line 259
      bool unrecognized;                                                        Line 260
                                                                                
      ++line_number;                                                            Line 262
                                                                                
      if (fp)                                                                   Line 264
        {                                                                       
          if (getline (&input_line, &input_line_size, fp) <= 0)                 Line 266
            {                                                                   
              free (input_line);                                                Line 268
              break;                                                            Line 269
            }                                                                   
          line = input_line;                                                    Line 271
        }                                                                       
      else                                                                      Line 273
        {                                                                       
          if (next_G_line == G_line + sizeof G_line)                            Line 275
            break;                                                              Line 276
          line = next_G_line;                                                   Line 277
          next_G_line += strlen (next_G_line) + 1;                              Line 278
        }                                                                       
                                                                                
      parse_line (line, &keywd, &arg);                                          Line 281
                                                                                
      if (keywd == NULL)                                                        Line 283
        continue;                                                               Line 284
                                                                                
      if (arg == NULL)                                                          Line 286
        {                                                                       
          error (0, 0, _("%s:%lu: invalid line;  missing second token"),        Line 288
                 quotef (filename), (unsigned long int) line_number);           Line 289
          ok = false;                                                           Line 290
          free (keywd);                                                         Line 291
          continue;                                                             Line 292
        }                                                                       
                                                                                
      unrecognized = false;                                                     Line 295
      if (c_strcasecmp (keywd, "TERM") == 0)                                    Line 296
        {                                                                       
          if (fnmatch (arg, term, 0) == 0)                                      Line 298
            state = ST_TERMSURE;                                                Line 299
          else if (state != ST_TERMSURE)                                        Line 300
            state = ST_TERMNO;                                                  Line 301
        }                                                                       
      else                                                                      Line 303
        {                                                                       
          if (state == ST_TERMSURE)                                             Line 305
            state = ST_TERMYES; /* Another TERM can cancel */                   Line 306
                                                                                
          if (state != ST_TERMNO)                                               Line 308
            {                                                                   
              if (keywd[0] == '.')                                              Line 310
                {                                                               
                  APPEND_CHAR ('*');                                            Line 312
                  append_quoted (keywd);                                        Line 313
                  APPEND_CHAR ('=');                                            Line 314
                  append_quoted (arg);                                          Line 315
                  APPEND_CHAR (':');                                            Line 316
                }                                                               
              else if (keywd[0] == '*')                                         Line 318
                {                                                               
                  append_quoted (keywd);                                        Line 320
                  APPEND_CHAR ('=');                                            Line 321
                  append_quoted (arg);                                          Line 322
                  APPEND_CHAR (':');                                            Line 323
                }                                                               
              else if (c_strcasecmp (keywd, "OPTIONS") == 0                     Line 325
                       || c_strcasecmp (keywd, "COLOR") == 0                    Line 326
                       || c_strcasecmp (keywd, "EIGHTBIT") == 0)                Line 327
                {                                                               
                  /* Ignore.  */                                                
                }                                                               
              else                                                              Line 331
                {                                                               
                  int i;                                                        Line 333
                                                                                
                  for (i = 0; slack_codes[i] != NULL; ++i)                      Line 335
                    if (c_strcasecmp (keywd, slack_codes[i]) == 0)              Line 336
                      break;                                                    Line 337
                                                                                
                  if (slack_codes[i] != NULL)                                   Line 339
                    {                                                           
                      APPEND_TWO_CHAR_STRING (ls_codes[i]);                     Line 341
                      APPEND_CHAR ('=');                                        Line 342
                      append_quoted (arg);                                      Line 343
                      APPEND_CHAR (':');                                        Line 344
                    }                                                           
                  else                                                          Line 346
                    {                                                           
                      unrecognized = true;                                      Line 348
                    }                                                           
                }                                                               
            }                                                                   
          else                                                                  Line 352
            {                                                                   
              unrecognized = true;                                              Line 354
            }                                                                   
        }                                                                       
                                                                                
      if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES))        Line 358
        {                                                                       
          error (0, 0, _("%s:%lu: unrecognized keyword %s"),                    Line 360
                 (filename ? quotef (filename) : _("<internal>")),              Line 361
                 (unsigned long int) line_number, keywd);                       Line 362
          ok = false;                                                           Line 363
        }                                                                       
                                                                                
      free (keywd);                                                             Line 366
      free (arg);                                                               Line 367
    }                                                                           
                                                                                
  return ok;                                                                    Line 370
}                                                                               Block 10
                                                                                
static bool                                                                     Line 373
dc_parse_file (const char *filename)                                            Line 374
{                                                                               
  bool ok;                                                                      Line 376
                                                                                
  if (! STREQ (filename, "-") && freopen (filename, "r", stdin) == NULL)        Line 378...!syscalls auto-comment...
    {                                                                           
      error (0, errno, "%s", quotef (filename));                                Line 380
      return false;                                                             Line 381
    }                                                                           
                                                                                
  ok = dc_parse_stream (stdin, filename);                                       Line 384
                                                                                
  if (fclose (stdin) != 0)                                                      Line 386...!syscalls auto-comment...
    {                                                                           
      error (0, errno, "%s", quotef (filename));                                Line 388
      return false;                                                             Line 389
    }                                                                           
                                                                                
  return ok;                                                                    Line 392
}                                                                               Block 11
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 396
{                                                                               
  bool ok = true;                                                               Line 398
  int optc;                                                                     Line 399
  enum Shell_syntax syntax = SHELL_SYNTAX_UNKNOWN;                              Line 400
  bool print_database = false;                                                  Line 401
                                                                                
  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
                                                                                
  atexit (close_stdout);                                                        Close stdout on exit (see gnulib)
                                                                                
  while ((optc = getopt_long (argc, argv, "bcp", long_options, NULL)) != -1)    Line 411
    switch (optc)                                                               Line 412
      {                                                                         
      case 'b': /* Bourne shell syntax.  */                                     Line 414
        syntax = SHELL_SYNTAX_BOURNE;                                           Line 415
        break;                                                                  Line 416
                                                                                
      case 'c': /* C shell syntax.  */                                          Line 418
        syntax = SHELL_SYNTAX_C;                                                Line 419
        break;                                                                  Line 420
                                                                                
      case 'p':                                                                 Line 422
        print_database = true;                                                  Line 423
        break;                                                                  Line 424
                                                                                
      case_GETOPT_HELP_CHAR;                                                    Line 426
                                                                                
      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                         Line 428
                                                                                
      default:                                                                  Line 430
        usage (EXIT_FAILURE);                                                   Line 431
      }                                                                         
                                                                                
  argc -= optind;                                                               Line 434
  argv += optind;                                                               Line 435
                                                                                
  /* It doesn't make sense to use --print with either of                        
     --bourne or --c-shell.  */                                                 
  if (print_database && syntax != SHELL_SYNTAX_UNKNOWN)                         Line 439
    {                                                                           
      error (0, 0,                                                              Line 441
             _("the options to output dircolors' internal database and\n"       Line 442
               "to select a shell syntax are mutually exclusive"));             Line 443
      usage (EXIT_FAILURE);                                                     Line 444
    }                                                                           
                                                                                
  if ((!print_database) < argc)                                                 Line 447
    {                                                                           
      error (0, 0, _("extra operand %s"), quote (argv[!print_database]));       Line 449
      if (print_database)                                                       Line 450
        fprintf (stderr, "%s\n",                                                Line 451
                 _("file operands cannot be combined with "                     Line 452
                   "--print-database (-p)"));                                   Line 453
      usage (EXIT_FAILURE);                                                     Line 454
    }                                                                           
                                                                                
  if (print_database)                                                           Line 457
    {                                                                           
      char const *p = G_line;                                                   Line 459
      while (p - G_line < sizeof G_line)                                        Line 460
        {                                                                       
          puts (p);                                                             Line 462
          p += strlen (p) + 1;                                                  Line 463
        }                                                                       
    }                                                                           
  else                                                                          Line 466
    {                                                                           
      /* If shell syntax was not explicitly specified, try to guess it. */      
      if (syntax == SHELL_SYNTAX_UNKNOWN)                                       Line 469
        {                                                                       
          syntax = guess_shell_syntax ();                                       Line 471
          if (syntax == SHELL_SYNTAX_UNKNOWN)                                   Line 472
            {                                                                   
              die (EXIT_FAILURE, 0,                                             Line 474
         _("no SHELL environment variable, and no shell type option given"));   Line 475
            }                                                                   
        }                                                                       
                                                                                
      obstack_init (&lsc_obstack);                                              Line 479
      if (argc == 0)                                                            Line 480
        ok = dc_parse_stream (NULL, NULL);                                      Line 481
      else                                                                      Line 482
        ok = dc_parse_file (argv[0]);                                           Line 483
                                                                                
      if (ok)                                                                   Line 485
        {                                                                       
          size_t len = obstack_object_size (&lsc_obstack);                      Line 487
          char *s = obstack_finish (&lsc_obstack);                              Line 488
          const char *prefix;                                                   Line 489
          const char *suffix;                                                   Line 490
                                                                                
          if (syntax == SHELL_SYNTAX_BOURNE)                                    Line 492
            {                                                                   
              prefix = "LS_COLORS='";                                           Line 494
              suffix = "';\nexport LS_COLORS\n";                                Line 495
            }                                                                   
          else                                                                  Line 497
            {                                                                   
              prefix = "setenv LS_COLORS '";                                    Line 499
              suffix = "'\n";                                                   Line 500
            }                                                                   
          fputs (prefix, stdout);                                               Line 502
          fwrite (s, 1, len, stdout);                                           Line 503...!syscalls auto-comment...
          fputs (suffix, stdout);                                               Line 504
        }                                                                       
    }                                                                           
                                                                                
  return ok ? EXIT_SUCCESS : EXIT_FAILURE;                                      Line 508
}                                                                               Block 12