|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <config.h> |
|
|
|
|
|
#include <sys/types.h> |
|
|
#include <fnmatch.h> |
|
|
#include <getopt.h> |
|
|
|
|
|
#include "system.h" |
|
|
#include "dircolors.h" |
|
|
#include "c-ctype.h" |
|
|
#include "c-strcase.h" |
|
|
#include "obstack.h" |
|
|
#include "quote.h" |
|
|
#include "stdio--.h" |
|
|
|
|
|
|
|
|
#define PROGRAM_NAME "dircolors" |
|
|
|
|
|
#define AUTHORS proper_name ("H. Peter Anvin") |
|
|
|
|
|
#define obstack_chunk_alloc malloc |
|
|
#define obstack_chunk_free free |
|
|
|
|
|
enum Shell_syntax |
|
|
{ |
|
|
SHELL_SYNTAX_BOURNE, |
|
|
SHELL_SYNTAX_C, |
|
|
SHELL_SYNTAX_UNKNOWN |
|
|
}; |
|
|
|
|
|
#define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C) |
|
|
|
|
|
|
|
|
|
|
|
static struct obstack lsc_obstack; |
|
|
|
|
|
static char const *const slack_codes[] = |
|
|
{ |
|
|
"NORMAL", "NORM", "FILE", "RESET", "DIR", "LNK", "LINK", |
|
|
"SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK", |
|
|
"CHR", "CHAR", "DOOR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE", |
|
|
"END", "ENDCODE", "SUID", "SETUID", "SGID", "SETGID", "STICKY", |
|
|
"OTHER_WRITABLE", "OWR", "STICKY_OTHER_WRITABLE", "OWT", "CAPABILITY", |
|
|
"MULTIHARDLINK", "CLRTOEOL", nullptr |
|
|
}; |
|
|
|
|
|
static char const *const ls_codes[] = |
|
|
{ |
|
|
"no", "no", "fi", "rs", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi", |
|
|
"so", "bd", "bd", "cd", "cd", "do", "ex", "lc", "lc", "rc", "rc", "ec", "ec", |
|
|
"su", "su", "sg", "sg", "st", "ow", "ow", "tw", "tw", "ca", "mh", "cl", |
|
|
nullptr |
|
|
}; |
|
|
static_assert (countof (slack_codes) == countof (ls_codes)); |
|
|
|
|
|
|
|
|
static bool print_ls_colors; |
|
|
|
|
|
|
|
|
|
|
|
enum |
|
|
{ |
|
|
PRINT_LS_COLORS_OPTION = CHAR_MAX + 1, |
|
|
}; |
|
|
|
|
|
static struct option const long_options[] = |
|
|
{ |
|
|
{"bourne-shell", no_argument, nullptr, 'b'}, |
|
|
{"sh", no_argument, nullptr, 'b'}, |
|
|
{"csh", no_argument, nullptr, 'c'}, |
|
|
{"c-shell", no_argument, nullptr, 'c'}, |
|
|
{"print-database", no_argument, nullptr, 'p'}, |
|
|
{"print-ls-colors", no_argument, nullptr, PRINT_LS_COLORS_OPTION}, |
|
|
{GETOPT_HELP_OPTION_DECL}, |
|
|
{GETOPT_VERSION_OPTION_DECL}, |
|
|
{nullptr, 0, nullptr, 0} |
|
|
}; |
|
|
|
|
|
void |
|
|
usage (int status) |
|
|
{ |
|
|
if (status != EXIT_SUCCESS) |
|
|
emit_try_help (); |
|
|
else |
|
|
{ |
|
|
printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name); |
|
|
fputs (_("\ |
|
|
Output commands to set the LS_COLORS environment variable.\n\ |
|
|
\n\ |
|
|
Determine format of output:\n\ |
|
|
-b, --sh, --bourne-shell output Bourne shell code to set LS_COLORS\n\ |
|
|
-c, --csh, --c-shell output C shell code to set LS_COLORS\n\ |
|
|
-p, --print-database output defaults\n\ |
|
|
--print-ls-colors output fully escaped colors for display\n\ |
|
|
"), stdout); |
|
|
fputs (HELP_OPTION_DESCRIPTION, stdout); |
|
|
fputs (VERSION_OPTION_DESCRIPTION, stdout); |
|
|
fputs (_("\ |
|
|
\n\ |
|
|
If FILE is specified, read it to determine which colors to use for which\n\ |
|
|
file types and extensions. Otherwise, a precompiled database is used.\n\ |
|
|
For details on the format of these files, run 'dircolors --print-database'.\n\ |
|
|
"), stdout); |
|
|
emit_ancillary_info (PROGRAM_NAME); |
|
|
} |
|
|
|
|
|
exit (status); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static enum Shell_syntax |
|
|
guess_shell_syntax (void) |
|
|
{ |
|
|
char *shell; |
|
|
|
|
|
shell = getenv ("SHELL"); |
|
|
if (shell == nullptr || *shell == '\0') |
|
|
return SHELL_SYNTAX_UNKNOWN; |
|
|
|
|
|
shell = last_component (shell); |
|
|
|
|
|
if (streq (shell, "csh") || streq (shell, "tcsh")) |
|
|
return SHELL_SYNTAX_C; |
|
|
|
|
|
return SHELL_SYNTAX_BOURNE; |
|
|
} |
|
|
|
|
|
static void |
|
|
parse_line (char const *line, char **keyword, char **arg) |
|
|
{ |
|
|
char const *p; |
|
|
char const *keyword_start; |
|
|
char const *arg_start; |
|
|
|
|
|
*keyword = nullptr; |
|
|
*arg = nullptr; |
|
|
|
|
|
for (p = line; c_isspace (*p); ++p) |
|
|
continue; |
|
|
|
|
|
|
|
|
if (*p == '\0' || *p == '#') |
|
|
return; |
|
|
|
|
|
keyword_start = p; |
|
|
|
|
|
while (!c_isspace (*p) && *p != '\0') |
|
|
{ |
|
|
++p; |
|
|
} |
|
|
|
|
|
*keyword = ximemdup0 (keyword_start, p - keyword_start); |
|
|
if (*p == '\0') |
|
|
return; |
|
|
|
|
|
do |
|
|
{ |
|
|
++p; |
|
|
} |
|
|
while (c_isspace (*p)); |
|
|
|
|
|
if (*p == '\0' || *p == '#') |
|
|
return; |
|
|
|
|
|
arg_start = p; |
|
|
|
|
|
while (*p != '\0' && *p != '#') |
|
|
++p; |
|
|
|
|
|
for (--p; c_isspace (*p); --p) |
|
|
continue; |
|
|
++p; |
|
|
|
|
|
*arg = ximemdup0 (arg_start, p - arg_start); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
append_quoted (char const *str) |
|
|
{ |
|
|
bool need_backslash = true; |
|
|
|
|
|
while (*str != '\0') |
|
|
{ |
|
|
if (! print_ls_colors) |
|
|
switch (*str) |
|
|
{ |
|
|
case '\'': |
|
|
APPEND_CHAR ('\''); |
|
|
APPEND_CHAR ('\\'); |
|
|
APPEND_CHAR ('\''); |
|
|
need_backslash = true; |
|
|
break; |
|
|
|
|
|
case '\\': |
|
|
case '^': |
|
|
need_backslash = !need_backslash; |
|
|
break; |
|
|
|
|
|
case ':': |
|
|
case '=': |
|
|
if (need_backslash) |
|
|
APPEND_CHAR ('\\'); |
|
|
FALLTHROUGH; |
|
|
|
|
|
default: |
|
|
need_backslash = true; |
|
|
break; |
|
|
} |
|
|
|
|
|
APPEND_CHAR (*str); |
|
|
++str; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
append_entry (char prefix, char const *item, char const *arg) |
|
|
{ |
|
|
if (print_ls_colors) |
|
|
{ |
|
|
append_quoted ("\x1B["); |
|
|
append_quoted (arg); |
|
|
APPEND_CHAR ('m'); |
|
|
} |
|
|
if (prefix) |
|
|
APPEND_CHAR (prefix); |
|
|
append_quoted (item); |
|
|
APPEND_CHAR (print_ls_colors ? '\t' : '='); |
|
|
append_quoted (arg); |
|
|
if (print_ls_colors) |
|
|
append_quoted ("\x1B[0m"); |
|
|
APPEND_CHAR (print_ls_colors ? '\n' : ':'); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static bool |
|
|
dc_parse_stream (FILE *fp, char const *filename) |
|
|
{ |
|
|
idx_t line_number = 0; |
|
|
char const *next_G_line = G_line; |
|
|
char *input_line = nullptr; |
|
|
size_t input_line_size = 0; |
|
|
char const *line; |
|
|
char const *term; |
|
|
char const *colorterm; |
|
|
bool ok = true; |
|
|
|
|
|
|
|
|
enum { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL } state = ST_GLOBAL; |
|
|
|
|
|
|
|
|
term = getenv ("TERM"); |
|
|
if (term == nullptr || *term == '\0') |
|
|
term = "none"; |
|
|
|
|
|
|
|
|
colorterm = getenv ("COLORTERM"); |
|
|
if (colorterm == nullptr) |
|
|
colorterm = ""; |
|
|
|
|
|
while (true) |
|
|
{ |
|
|
char *keywd, *arg; |
|
|
bool unrecognized; |
|
|
|
|
|
++line_number; |
|
|
|
|
|
if (fp) |
|
|
{ |
|
|
if (getline (&input_line, &input_line_size, fp) <= 0) |
|
|
{ |
|
|
if (ferror (fp)) |
|
|
{ |
|
|
error (0, errno, _("%s: read error"), quotef (filename)); |
|
|
ok = false; |
|
|
} |
|
|
free (input_line); |
|
|
break; |
|
|
} |
|
|
line = input_line; |
|
|
} |
|
|
else |
|
|
{ |
|
|
if (next_G_line == G_line + sizeof G_line) |
|
|
break; |
|
|
line = next_G_line; |
|
|
next_G_line += strlen (next_G_line) + 1; |
|
|
} |
|
|
|
|
|
parse_line (line, &keywd, &arg); |
|
|
|
|
|
if (keywd == nullptr) |
|
|
continue; |
|
|
|
|
|
if (arg == nullptr) |
|
|
{ |
|
|
error (0, 0, _("%s:%td: invalid line; missing second token"), |
|
|
quotef (filename), line_number); |
|
|
ok = false; |
|
|
free (keywd); |
|
|
continue; |
|
|
} |
|
|
|
|
|
unrecognized = false; |
|
|
if (c_strcasecmp (keywd, "TERM") == 0) |
|
|
{ |
|
|
if (state != ST_TERMSURE) |
|
|
state = fnmatch (arg, term, 0) == 0 ? ST_TERMSURE : ST_TERMNO; |
|
|
} |
|
|
else if (c_strcasecmp (keywd, "COLORTERM") == 0) |
|
|
{ |
|
|
if (state != ST_TERMSURE) |
|
|
state = fnmatch (arg, colorterm, 0) == 0 ? ST_TERMSURE : ST_TERMNO; |
|
|
} |
|
|
else |
|
|
{ |
|
|
if (state == ST_TERMSURE) |
|
|
state = ST_TERMYES; |
|
|
|
|
|
if (state != ST_TERMNO) |
|
|
{ |
|
|
if (keywd[0] == '.') |
|
|
append_entry ('*', keywd, arg); |
|
|
else if (keywd[0] == '*') |
|
|
append_entry (0, keywd, arg); |
|
|
else if (c_strcasecmp (keywd, "OPTIONS") == 0 |
|
|
|| c_strcasecmp (keywd, "COLOR") == 0 |
|
|
|| c_strcasecmp (keywd, "EIGHTBIT") == 0) |
|
|
{ |
|
|
|
|
|
} |
|
|
else |
|
|
{ |
|
|
int i; |
|
|
|
|
|
for (i = 0; slack_codes[i] != nullptr; ++i) |
|
|
if (c_strcasecmp (keywd, slack_codes[i]) == 0) |
|
|
break; |
|
|
|
|
|
if (slack_codes[i] != nullptr) |
|
|
append_entry (0, ls_codes[i], arg); |
|
|
else |
|
|
unrecognized = true; |
|
|
} |
|
|
} |
|
|
else |
|
|
unrecognized = true; |
|
|
} |
|
|
|
|
|
if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES)) |
|
|
{ |
|
|
error (0, 0, _("%s:%td: unrecognized keyword %s"), |
|
|
(filename ? quotef (filename) : _("<internal>")), |
|
|
line_number, keywd); |
|
|
ok = false; |
|
|
} |
|
|
|
|
|
free (keywd); |
|
|
free (arg); |
|
|
} |
|
|
|
|
|
return ok; |
|
|
} |
|
|
|
|
|
static bool |
|
|
dc_parse_file (char const *filename) |
|
|
{ |
|
|
bool ok; |
|
|
|
|
|
if (! streq (filename, "-") && freopen (filename, "r", stdin) == nullptr) |
|
|
{ |
|
|
error (0, errno, "%s", quotef (filename)); |
|
|
return false; |
|
|
} |
|
|
|
|
|
ok = dc_parse_stream (stdin, filename); |
|
|
|
|
|
if (fclose (stdin) != 0) |
|
|
{ |
|
|
error (0, errno, "%s", quotef (filename)); |
|
|
return false; |
|
|
} |
|
|
|
|
|
return ok; |
|
|
} |
|
|
|
|
|
int |
|
|
main (int argc, char **argv) |
|
|
{ |
|
|
bool ok = true; |
|
|
int optc; |
|
|
enum Shell_syntax syntax = SHELL_SYNTAX_UNKNOWN; |
|
|
bool print_database = false; |
|
|
|
|
|
initialize_main (&argc, &argv); |
|
|
set_program_name (argv[0]); |
|
|
setlocale (LC_ALL, ""); |
|
|
bindtextdomain (PACKAGE, LOCALEDIR); |
|
|
textdomain (PACKAGE); |
|
|
|
|
|
atexit (close_stdout); |
|
|
|
|
|
while ((optc = getopt_long (argc, argv, "bcp", long_options, nullptr)) != -1) |
|
|
switch (optc) |
|
|
{ |
|
|
case 'b': |
|
|
syntax = SHELL_SYNTAX_BOURNE; |
|
|
break; |
|
|
|
|
|
case 'c': |
|
|
syntax = SHELL_SYNTAX_C; |
|
|
break; |
|
|
|
|
|
case 'p': |
|
|
print_database = true; |
|
|
break; |
|
|
|
|
|
case PRINT_LS_COLORS_OPTION: |
|
|
print_ls_colors = true; |
|
|
break; |
|
|
|
|
|
case_GETOPT_HELP_CHAR; |
|
|
|
|
|
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); |
|
|
|
|
|
default: |
|
|
usage (EXIT_FAILURE); |
|
|
} |
|
|
|
|
|
argc -= optind; |
|
|
argv += optind; |
|
|
|
|
|
|
|
|
|
|
|
if ((print_database | print_ls_colors) && syntax != SHELL_SYNTAX_UNKNOWN) |
|
|
{ |
|
|
error (0, 0, |
|
|
_("the options to output non shell syntax,\n" |
|
|
"and to select a shell syntax are mutually exclusive")); |
|
|
usage (EXIT_FAILURE); |
|
|
} |
|
|
|
|
|
if (print_database && print_ls_colors) |
|
|
{ |
|
|
error (0, 0, |
|
|
_("options --print-database and --print-ls-colors " |
|
|
"are mutually exclusive")); |
|
|
usage (EXIT_FAILURE); |
|
|
} |
|
|
|
|
|
if ((!print_database) < argc) |
|
|
{ |
|
|
error (0, 0, _("extra operand %s"), |
|
|
quote (argv[!print_database])); |
|
|
if (print_database) |
|
|
fprintf (stderr, "%s\n", |
|
|
_("file operands cannot be combined with " |
|
|
"--print-database (-p)")); |
|
|
usage (EXIT_FAILURE); |
|
|
} |
|
|
|
|
|
if (print_database) |
|
|
{ |
|
|
char const *p = G_line; |
|
|
while (p - G_line < sizeof G_line) |
|
|
{ |
|
|
puts (p); |
|
|
p += strlen (p) + 1; |
|
|
} |
|
|
} |
|
|
else |
|
|
{ |
|
|
|
|
|
if (syntax == SHELL_SYNTAX_UNKNOWN && ! print_ls_colors) |
|
|
{ |
|
|
syntax = guess_shell_syntax (); |
|
|
if (syntax == SHELL_SYNTAX_UNKNOWN) |
|
|
error (EXIT_FAILURE, 0, |
|
|
_("no SHELL environment variable," |
|
|
" and no shell type option given")); |
|
|
} |
|
|
|
|
|
obstack_init (&lsc_obstack); |
|
|
if (argc == 0) |
|
|
ok = dc_parse_stream (nullptr, nullptr); |
|
|
else |
|
|
ok = dc_parse_file (argv[0]); |
|
|
|
|
|
if (ok) |
|
|
{ |
|
|
size_t len = obstack_object_size (&lsc_obstack); |
|
|
char *s = obstack_finish (&lsc_obstack); |
|
|
char const *prefix; |
|
|
char const *suffix; |
|
|
|
|
|
if (syntax == SHELL_SYNTAX_BOURNE) |
|
|
{ |
|
|
prefix = "LS_COLORS='"; |
|
|
suffix = "';\nexport LS_COLORS\n"; |
|
|
} |
|
|
else |
|
|
{ |
|
|
prefix = "setenv LS_COLORS '"; |
|
|
suffix = "'\n"; |
|
|
} |
|
|
if (! print_ls_colors) |
|
|
fputs (prefix, stdout); |
|
|
fwrite (s, 1, len, stdout); |
|
|
if (! print_ls_colors) |
|
|
fputs (suffix, stdout); |
|
|
} |
|
|
} |
|
|
|
|
|
return ok ? EXIT_SUCCESS : EXIT_FAILURE; |
|
|
} |
|
|
|