LCOV - code coverage report
Current view: top level - src - dircolors.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 164 197 83.2 %
Date: 2018-01-30 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /* dircolors - output commands to set the LS_COLOR environment variable
       2             :    Copyright (C) 1996-2008 Free Software Foundation, Inc.
       3             :    Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000 H. Peter Anvin
       4             : 
       5             :    This program is free software: you can redistribute it and/or modify
       6             :    it under the terms of the GNU General Public License as published by
       7             :    the Free Software Foundation, either version 3 of the License, or
       8             :    (at your option) any later version.
       9             : 
      10             :    This program is distributed in the hope that it will be useful,
      11             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             :    GNU General Public License for more details.
      14             : 
      15             :    You should have received a copy of the GNU General Public License
      16             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      17             : 
      18             : #include <config.h>
      19             : 
      20             : #include <sys/types.h>
      21             : #include <getopt.h>
      22             : #include <stdio.h>
      23             : 
      24             : #include "system.h"
      25             : #include "dircolors.h"
      26             : #include "c-strcase.h"
      27             : #include "error.h"
      28             : #include "obstack.h"
      29             : #include "quote.h"
      30             : #include "xstrndup.h"
      31             : 
      32             : /* The official name of this program (e.g., no `g' prefix).  */
      33             : #define PROGRAM_NAME "dircolors"
      34             : 
      35             : #define AUTHORS "H. Peter Anvin"
      36             : 
      37             : #define obstack_chunk_alloc malloc
      38             : #define obstack_chunk_free free
      39             : 
      40             : enum Shell_syntax
      41             : {
      42             :   SHELL_SYNTAX_BOURNE,
      43             :   SHELL_SYNTAX_C,
      44             :   SHELL_SYNTAX_UNKNOWN
      45             : };
      46             : 
      47             : #define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)
      48             : #define APPEND_TWO_CHAR_STRING(S)                                       \
      49             :   do                                                                    \
      50             :     {                                                                   \
      51             :       APPEND_CHAR (S[0]);                                               \
      52             :       APPEND_CHAR (S[1]);                                               \
      53             :     }                                                                   \
      54             :   while (0)
      55             : 
      56             : /* Accumulate in this obstack the value for the LS_COLORS environment
      57             :    variable.  */
      58             : static struct obstack lsc_obstack;
      59             : 
      60             : static const char *const slack_codes[] =
      61             : {
      62             :   "NORMAL", "NORM", "FILE", "RESET", "DIR", "LNK", "LINK",
      63             :   "SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK",
      64             :   "CHR", "CHAR", "DOOR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE",
      65             :   "END", "ENDCODE", "SUID", "SETUID", "SGID", "SETGID", "STICKY",
      66             :   "OTHER_WRITABLE", "OWR", "STICKY_OTHER_WRITABLE", "OWT", NULL
      67             : };
      68             : 
      69             : static const char *const ls_codes[] =
      70             : {
      71             :   "no", "no", "fi", "rs", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
      72             :   "so", "bd", "bd", "cd", "cd", "do", "ex", "lc", "lc", "rc", "rc", "ec", "ec",
      73             :   "su", "su", "sg", "sg", "st", "ow", "ow", "tw", "tw", NULL
      74             : };
      75             : #define array_len(Array) (sizeof (Array) / sizeof *(Array))
      76             : verify (array_len (slack_codes) == array_len (ls_codes));
      77             : 
      78             : static struct option const long_options[] =
      79             :   {
      80             :     {"bourne-shell", no_argument, NULL, 'b'},
      81             :     {"sh", no_argument, NULL, 'b'},
      82             :     {"csh", no_argument, NULL, 'c'},
      83             :     {"c-shell", no_argument, NULL, 'c'},
      84             :     {"print-database", no_argument, NULL, 'p'},
      85             :     {GETOPT_HELP_OPTION_DECL},
      86             :     {GETOPT_VERSION_OPTION_DECL},
      87             :     {NULL, 0, NULL, 0}
      88             :   };
      89             : 
      90             : char *program_name;
      91             : 
      92             : void
      93          33 : usage (int status)
      94             : {
      95          33 :   if (status != EXIT_SUCCESS)
      96          32 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
      97             :              program_name);
      98             :   else
      99             :     {
     100           1 :       printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
     101           1 :       fputs (_("\
     102             : Output commands to set the LS_COLORS environment variable.\n\
     103             : \n\
     104             : Determine format of output:\n\
     105             :   -b, --sh, --bourne-shell    output Bourne shell code to set LS_COLORS\n\
     106             :   -c, --csh, --c-shell        output C shell code to set LS_COLORS\n\
     107             :   -p, --print-database        output defaults\n\
     108             : "), stdout);
     109           1 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     110           1 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     111           1 :       fputs (_("\
     112             : \n\
     113             : If FILE is specified, read it to determine which colors to use for which\n\
     114             : file types and extensions.  Otherwise, a precompiled database is used.\n\
     115             : For details on the format of these files, run `dircolors --print-database'.\n\
     116             : "), stdout);
     117           1 :       emit_bug_reporting_address ();
     118             :     }
     119             : 
     120          33 :   exit (status);
     121             : }
     122             : 
     123             : /* If the SHELL environment variable is set to `csh' or `tcsh,'
     124             :    assume C shell.  Else Bourne shell.  */
     125             : 
     126             : static enum Shell_syntax
     127          24 : guess_shell_syntax (void)
     128             : {
     129             :   char *shell;
     130             : 
     131          24 :   shell = getenv ("SHELL");
     132          24 :   if (shell == NULL || *shell == '\0')
     133          24 :     return SHELL_SYNTAX_UNKNOWN;
     134             : 
     135           0 :   shell = last_component (shell);
     136             : 
     137           0 :   if (STREQ (shell, "csh") || STREQ (shell, "tcsh"))
     138           0 :     return SHELL_SYNTAX_C;
     139             : 
     140           0 :   return SHELL_SYNTAX_BOURNE;
     141             : }
     142             : 
     143             : static void
     144         370 : parse_line (char const *line, char **keyword, char **arg)
     145             : {
     146             :   char const *p;
     147             :   char const *keyword_start;
     148             :   char const *arg_start;
     149             : 
     150         370 :   *keyword = NULL;
     151         370 :   *arg = NULL;
     152             : 
     153         398 :   for (p = line; isspace (to_uchar (*p)); ++p)
     154          28 :     continue;
     155             : 
     156             :   /* Ignore blank lines and shell-style comments.  */
     157         370 :   if (*p == '\0' || *p == '#')
     158          98 :     return;
     159             : 
     160         272 :   keyword_start = p;
     161             : 
     162        1688 :   while (!isspace (to_uchar (*p)) && *p != '\0')
     163             :     {
     164        1144 :       ++p;
     165             :     }
     166             : 
     167         272 :   *keyword = xstrndup (keyword_start, p - keyword_start);
     168         272 :   if (*p  == '\0')
     169           0 :     return;
     170             : 
     171             :   do
     172             :     {
     173         272 :       ++p;
     174             :     }
     175         272 :   while (isspace (to_uchar (*p)));
     176             : 
     177         272 :   if (*p == '\0' || *p == '#')
     178           0 :     return;
     179             : 
     180         272 :   arg_start = p;
     181             : 
     182        2262 :   while (*p != '\0' && *p != '#')
     183        1718 :     ++p;
     184             : 
     185         300 :   for (--p; isspace (to_uchar (*p)); --p)
     186          28 :     continue;
     187         272 :   ++p;
     188             : 
     189         272 :   *arg = xstrndup (arg_start, p - arg_start);
     190             : }
     191             : 
     192             : /* FIXME: Write a string to standard out, while watching for "dangerous"
     193             :    sequences like unescaped : and = characters.  */
     194             : 
     195             : static void
     196         338 : append_quoted (const char *str)
     197             : {
     198         338 :   bool need_backslash = true;
     199             : 
     200        2212 :   while (*str != '\0')
     201             :     {
     202        1536 :       switch (*str)
     203             :         {
     204           0 :         case '\'':
     205           0 :           APPEND_CHAR ('\'');
     206           0 :           APPEND_CHAR ('\\');
     207           0 :           APPEND_CHAR ('\'');
     208           0 :           need_backslash = true;
     209           0 :           break;
     210             : 
     211           0 :         case '\\':
     212             :         case '^':
     213           0 :           need_backslash = !need_backslash;
     214           0 :           break;
     215             : 
     216           0 :         case ':':
     217             :         case '=':
     218           0 :           if (need_backslash)
     219           0 :             APPEND_CHAR ('\\');
     220             :           /* Fall through */
     221             : 
     222             :         default:
     223        1536 :           need_backslash = true;
     224        1536 :           break;
     225             :         }
     226             : 
     227        1536 :       APPEND_CHAR (*str);
     228        1536 :       ++str;
     229             :     }
     230         338 : }
     231             : 
     232             : /* Read the file open on FP (with name FILENAME).  First, look for a
     233             :    `TERM name' directive where name matches the current terminal type.
     234             :    Once found, translate and accumulate the associated directives onto
     235             :    the global obstack LSC_OBSTACK.  Give a diagnostic
     236             :    upon failure (unrecognized keyword is the only way to fail here).
     237             :    Return true if successful.  */
     238             : 
     239             : static bool
     240           5 : dc_parse_stream (FILE *fp, const char *filename)
     241             : {
     242           5 :   size_t line_number = 0;
     243           5 :   char const *next_G_line = G_line;
     244           5 :   char *input_line = NULL;
     245           5 :   size_t input_line_size = 0;
     246             :   char const *line;
     247             :   char const *term;
     248           5 :   bool ok = true;
     249             : 
     250             :   /* State for the parser.  */
     251           5 :   enum { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL } state = ST_GLOBAL;
     252             : 
     253             :   /* Get terminal type */
     254           5 :   term = getenv ("TERM");
     255           5 :   if (term == NULL || *term == '\0')
     256           0 :     term = "none";
     257             : 
     258             :   while (1)
     259         370 :     {
     260             :       char *keywd, *arg;
     261             :       bool unrecognized;
     262             : 
     263         375 :       ++line_number;
     264             : 
     265         375 :       if (fp)
     266             :         {
     267          27 :           if (getline (&input_line, &input_line_size, fp) <= 0)
     268             :             {
     269           3 :               free (input_line);
     270           8 :               break;
     271             :             }
     272          24 :           line = input_line;
     273             :         }
     274             :       else
     275             :         {
     276         348 :           if (next_G_line == G_line + sizeof G_line)
     277           2 :             break;
     278         346 :           line = next_G_line;
     279         346 :           next_G_line += strlen (next_G_line) + 1;
     280             :         }
     281             : 
     282         370 :       parse_line (line, &keywd, &arg);
     283             : 
     284         370 :       if (keywd == NULL)
     285         196 :         continue;
     286             : 
     287         272 :       if (arg == NULL)
     288             :         {
     289           0 :           error (0, 0, _("%s:%lu: invalid line;  missing second token"),
     290             :                  filename, (unsigned long int) line_number);
     291           0 :           ok = false;
     292           0 :           free (keywd);
     293           0 :           continue;
     294             :         }
     295             : 
     296         272 :       unrecognized = false;
     297         272 :       if (c_strcasecmp (keywd, "TERM") == 0)
     298             :         {
     299          88 :           if (STREQ (arg, term))
     300           2 :             state = ST_TERMSURE;
     301          86 :           else if (state != ST_TERMSURE)
     302          76 :             state = ST_TERMNO;
     303             :         }
     304             :       else
     305             :         {
     306         184 :           if (state == ST_TERMSURE)
     307           2 :             state = ST_TERMYES; /* Another TERM can cancel */
     308             : 
     309         184 :           if (state != ST_TERMNO)
     310             :             {
     311         184 :               if (keywd[0] == '.')
     312             :                 {
     313         154 :                   APPEND_CHAR ('*');
     314         154 :                   append_quoted (keywd);
     315         154 :                   APPEND_CHAR ('=');
     316         154 :                   append_quoted (arg);
     317         154 :                   APPEND_CHAR (':');
     318             :                 }
     319          30 :               else if (keywd[0] == '*')
     320             :                 {
     321           0 :                   append_quoted (keywd);
     322           0 :                   APPEND_CHAR ('=');
     323           0 :                   append_quoted (arg);
     324           0 :                   APPEND_CHAR (':');
     325             :                 }
     326          30 :               else if (c_strcasecmp (keywd, "OPTIONS") == 0
     327          30 :                        || c_strcasecmp (keywd, "COLOR") == 0
     328          30 :                        || c_strcasecmp (keywd, "EIGHTBIT") == 0)
     329             :                 {
     330             :                   /* Ignore.  */
     331             :                 }
     332             :               else
     333             :                 {
     334             :                   int i;
     335             : 
     336         532 :                   for (i = 0; slack_codes[i] != NULL; ++i)
     337         532 :                     if (c_strcasecmp (keywd, slack_codes[i]) == 0)
     338          30 :                       break;
     339             : 
     340          30 :                   if (slack_codes[i] != NULL)
     341             :                     {
     342          30 :                       APPEND_TWO_CHAR_STRING (ls_codes[i]);
     343          30 :                       APPEND_CHAR ('=');
     344          30 :                       append_quoted (arg);
     345          30 :                       APPEND_CHAR (':');
     346             :                     }
     347             :                   else
     348             :                     {
     349           0 :                       unrecognized = true;
     350             :                     }
     351             :                 }
     352             :             }
     353             :           else
     354             :             {
     355           0 :               unrecognized = true;
     356             :             }
     357             :         }
     358             : 
     359         272 :       if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES))
     360             :         {
     361           0 :           error (0, 0, _("%s:%lu: unrecognized keyword %s"),
     362             :                  (filename ? quote (filename) : _("<internal>")),
     363             :                  (unsigned long int) line_number, keywd);
     364           0 :           ok = false;
     365             :         }
     366             : 
     367         272 :       free (keywd);
     368         272 :       free (arg);
     369             :     }
     370             : 
     371          10 :   return ok;
     372             : }
     373             : 
     374             : static bool
     375           5 : dc_parse_file (const char *filename)
     376             : {
     377             :   bool ok;
     378             : 
     379           5 :   if (! STREQ (filename, "-") && freopen (filename, "r", stdin) == NULL)
     380             :     {
     381           2 :       error (0, errno, "%s", filename);
     382           2 :       return false;
     383             :     }
     384             : 
     385           3 :   ok = dc_parse_stream (stdin, filename);
     386             : 
     387           3 :   if (fclose (stdin) != 0)
     388             :     {
     389           0 :       error (0, errno, "%s", quote (filename));
     390           0 :       return false;
     391             :     }
     392             : 
     393           3 :   return ok;
     394             : }
     395             : 
     396             : int
     397          67 : main (int argc, char **argv)
     398             : {
     399          67 :   bool ok = true;
     400             :   int optc;
     401          67 :   enum Shell_syntax syntax = SHELL_SYNTAX_UNKNOWN;
     402          67 :   bool print_database = false;
     403             : 
     404             :   initialize_main (&argc, &argv);
     405          67 :   program_name = argv[0];
     406          67 :   setlocale (LC_ALL, "");
     407             :   bindtextdomain (PACKAGE, LOCALEDIR);
     408             :   textdomain (PACKAGE);
     409             : 
     410          67 :   atexit (close_stdout);
     411             : 
     412         150 :   while ((optc = getopt_long (argc, argv, "bcp", long_options, NULL)) != -1)
     413          23 :     switch (optc)
     414             :       {
     415          10 :       case 'b': /* Bourne shell syntax.  */
     416          10 :         syntax = SHELL_SYNTAX_BOURNE;
     417          10 :         break;
     418             : 
     419           2 :       case 'c': /* C shell syntax.  */
     420           2 :         syntax = SHELL_SYNTAX_C;
     421           2 :         break;
     422             : 
     423           4 :       case 'p':
     424           4 :         print_database = true;
     425           4 :         break;
     426             : 
     427           1 :       case_GETOPT_HELP_CHAR;
     428             : 
     429           1 :       case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     430             : 
     431           5 :       default:
     432           5 :         usage (EXIT_FAILURE);
     433             :       }
     434             : 
     435          60 :   argc -= optind;
     436          60 :   argv += optind;
     437             : 
     438             :   /* It doesn't make sense to use --print with either of
     439             :      --bourne or --c-shell.  */
     440          60 :   if (print_database && syntax != SHELL_SYNTAX_UNKNOWN)
     441             :     {
     442           1 :       error (0, 0,
     443             :              _("the options to output dircolors' internal database and\n\
     444             : to select a shell syntax are mutually exclusive"));
     445           1 :       usage (EXIT_FAILURE);
     446             :     }
     447             : 
     448          59 :   if (!print_database < argc)
     449             :     {
     450          26 :       error (0, 0, _("extra operand %s"), quote (argv[!print_database]));
     451          26 :       if (print_database)
     452           1 :         fprintf (stderr, "%s\n",
     453             :                  _("File operands cannot be combined with "
     454             :                    "--print-database (-p)."));
     455          26 :       usage (EXIT_FAILURE);
     456             :     }
     457             : 
     458          33 :   if (print_database)
     459             :     {
     460           2 :       char const *p = G_line;
     461         350 :       while (p < G_line + sizeof G_line)
     462             :         {
     463         346 :           puts (p);
     464         346 :           p += strlen (p) + 1;
     465             :         }
     466             :     }
     467             :   else
     468             :     {
     469             :       /* If shell syntax was not explicitly specified, try to guess it. */
     470          31 :       if (syntax == SHELL_SYNTAX_UNKNOWN)
     471             :         {
     472          24 :           syntax = guess_shell_syntax ();
     473          24 :           if (syntax == SHELL_SYNTAX_UNKNOWN)
     474             :             {
     475          24 :               error (EXIT_FAILURE, 0,
     476             :          _("no SHELL environment variable, and no shell type option given"));
     477             :             }
     478             :         }
     479             : 
     480           7 :       obstack_init (&lsc_obstack);
     481           7 :       if (argc == 0)
     482           2 :         ok = dc_parse_stream (NULL, NULL);
     483             :       else
     484           5 :         ok = dc_parse_file (argv[0]);
     485             : 
     486           7 :       if (ok)
     487             :         {
     488           5 :           size_t len = obstack_object_size (&lsc_obstack);
     489           5 :           char *s = obstack_finish (&lsc_obstack);
     490             :           const char *prefix;
     491             :           const char *suffix;
     492             : 
     493           5 :           if (syntax == SHELL_SYNTAX_BOURNE)
     494             :             {
     495           4 :               prefix = "LS_COLORS='";
     496           4 :               suffix = "';\nexport LS_COLORS\n";
     497             :             }
     498             :           else
     499             :             {
     500           1 :               prefix = "setenv LS_COLORS '";
     501           1 :               suffix = "'\n";
     502             :             }
     503           5 :           fputs (prefix, stdout);
     504           5 :           fwrite (s, 1, len, stdout);
     505           5 :           fputs (suffix, stdout);
     506             :         }
     507             :     }
     508             : 
     509           9 :   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
     510             : }

Generated by: LCOV version 1.10