/* 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