1 /* dircolors - output commands to set the LS_COLOR environment variable
2 Copyright (C) 1996-2006 Free Software Foundation, Inc.
3 Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000 H. Peter Anvin
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 2, or (at your option)
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.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
23 #include <sys/types.h>
28 #include "dircolors.h"
37 /* The official name of this program (e.g., no `g' prefix). */
38 #define PROGRAM_NAME "dircolors"
40 #define AUTHORS "H. Peter Anvin"
42 #define obstack_chunk_alloc malloc
43 #define obstack_chunk_free free
52 #define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)
53 #define APPEND_TWO_CHAR_STRING(S) \
61 /* Accumulate in this obstack the value for the LS_COLORS environment
63 static struct obstack lsc_obstack;
65 static const char *const slack_codes[] =
67 "NORMAL", "NORM", "FILE", "DIR", "LNK", "LINK",
68 "SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK",
69 "CHR", "CHAR", "DOOR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE",
70 "END", "ENDCODE", "SUID", "SETUID", "SGID", "SETGID", "STICKY",
71 "OTHER_WRITABLE", "OWR", "STICKY_OTHER_WRITABLE", "OWT", NULL
74 static const char *const ls_codes[] =
76 "no", "no", "fi", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
77 "so", "bd", "bd", "cd", "cd", "do", "ex", "lc", "lc", "rc", "rc", "ec", "ec",
78 "su", "su", "sg", "sg", "st", "ow", "ow", "tw", "tw", NULL
80 #define array_len(Array) (sizeof (Array) / sizeof *(Array))
81 verify (array_len (slack_codes) == array_len (ls_codes));
83 static struct option const long_options[] =
85 {"bourne-shell", no_argument, NULL, 'b'},
86 {"sh", no_argument, NULL, 'b'},
87 {"csh", no_argument, NULL, 'c'},
88 {"c-shell", no_argument, NULL, 'c'},
89 {"print-database", no_argument, NULL, 'p'},
90 {GETOPT_HELP_OPTION_DECL},
91 {GETOPT_VERSION_OPTION_DECL},
100 if (status != EXIT_SUCCESS)
101 fprintf (stderr, _("Try `%s --help' for more information.\n"),
105 printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
107 Output commands to set the LS_COLORS environment variable.\n\
109 Determine format of output:\n\
110 -b, --sh, --bourne-shell output Bourne shell code to set LS_COLORS\n\
111 -c, --csh, --c-shell output C shell code to set LS_COLORS\n\
112 -p, --print-database output defaults\n\
114 fputs (HELP_OPTION_DESCRIPTION, stdout);
115 fputs (VERSION_OPTION_DESCRIPTION, stdout);
118 If FILE is specified, read it to determine which colors to use for which\n\
119 file types and extensions. Otherwise, a precompiled database is used.\n\
120 For details on the format of these files, run `dircolors --print-database'.\n\
122 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
128 /* If the SHELL environment variable is set to `csh' or `tcsh,'
129 assume C shell. Else Bourne shell. */
131 static enum Shell_syntax
132 guess_shell_syntax (void)
136 shell = getenv ("SHELL");
137 if (shell == NULL || *shell == '\0')
138 return SHELL_SYNTAX_UNKNOWN;
140 shell = last_component (shell);
142 if (STREQ (shell, "csh") || STREQ (shell, "tcsh"))
143 return SHELL_SYNTAX_C;
145 return SHELL_SYNTAX_BOURNE;
149 parse_line (char const *line, char **keyword, char **arg)
152 char const *keyword_start;
153 char const *arg_start;
158 for (p = line; ISSPACE (to_uchar (*p)); ++p)
161 /* Ignore blank lines and shell-style comments. */
162 if (*p == '\0' || *p == '#')
167 while (!ISSPACE (to_uchar (*p)) && *p != '\0')
172 *keyword = xstrndup (keyword_start, p - keyword_start);
180 while (ISSPACE (to_uchar (*p)));
182 if (*p == '\0' || *p == '#')
187 while (*p != '\0' && *p != '#')
190 for (--p; ISSPACE (to_uchar (*p)); --p)
196 *arg = xstrndup (arg_start, p - arg_start);
199 /* FIXME: Write a string to standard out, while watching for "dangerous"
200 sequences like unescaped : and = characters. */
203 append_quoted (const char *str)
205 bool need_backslash = true;
215 need_backslash = true;
220 need_backslash = !need_backslash;
230 need_backslash = true;
239 /* Read the file open on FP (with name FILENAME). First, look for a
240 `TERM name' directive where name matches the current terminal type.
241 Once found, translate and accumulate the associated directives onto
242 the global obstack LSC_OBSTACK. Give a diagnostic
243 upon failure (unrecognized keyword is the only way to fail here).
244 Return true if successful. */
247 dc_parse_stream (FILE *fp, const char *filename)
249 size_t line_number = 0;
250 char const *next_G_line = G_line;
251 char *input_line = NULL;
252 size_t input_line_size = 0;
257 /* State for the parser. */
258 enum { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL } state = ST_GLOBAL;
260 /* Get terminal type */
261 term = getenv ("TERM");
262 if (term == NULL || *term == '\0')
274 if (getline (&input_line, &input_line_size, fp) <= 0)
283 if (next_G_line == G_line + sizeof G_line)
286 next_G_line += strlen (next_G_line) + 1;
289 parse_line (line, &keywd, &arg);
296 error (0, 0, _("%s:%lu: invalid line; missing second token"),
297 filename, (unsigned long int) line_number);
303 unrecognized = false;
304 if (strcasecmp (keywd, "TERM") == 0)
306 if (STREQ (arg, term))
308 else if (state != ST_TERMSURE)
313 if (state == ST_TERMSURE)
314 state = ST_TERMYES; /* Another TERM can cancel */
316 if (state != ST_TERMNO)
321 append_quoted (keywd);
326 else if (keywd[0] == '*')
328 append_quoted (keywd);
333 else if (strcasecmp (keywd, "OPTIONS") == 0
334 || strcasecmp (keywd, "COLOR") == 0
335 || strcasecmp (keywd, "EIGHTBIT") == 0)
343 for (i = 0; slack_codes[i] != NULL; ++i)
344 if (strcasecmp (keywd, slack_codes[i]) == 0)
347 if (slack_codes[i] != NULL)
349 APPEND_TWO_CHAR_STRING (ls_codes[i]);
366 if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES))
368 error (0, 0, _("%s:%lu: unrecognized keyword %s"),
369 (filename ? quote (filename) : _("<internal>")),
370 (unsigned long int) line_number, keywd);
382 dc_parse_file (const char *filename)
386 if (! STREQ (filename, "-") && freopen (filename, "r", stdin) == NULL)
388 error (0, errno, "%s", filename);
392 ok = dc_parse_stream (stdin, filename);
394 if (fclose (stdin) != 0)
396 error (0, errno, "%s", quote (filename));
404 main (int argc, char **argv)
408 enum Shell_syntax syntax = SHELL_SYNTAX_UNKNOWN;
409 bool print_database = false;
411 initialize_main (&argc, &argv);
412 program_name = argv[0];
413 setlocale (LC_ALL, "");
414 bindtextdomain (PACKAGE, LOCALEDIR);
415 textdomain (PACKAGE);
417 atexit (close_stdout);
419 while ((optc = getopt_long (argc, argv, "bcp", long_options, NULL)) != -1)
422 case 'b': /* Bourne shell syntax. */
423 syntax = SHELL_SYNTAX_BOURNE;
426 case 'c': /* C shell syntax. */
427 syntax = SHELL_SYNTAX_C;
431 print_database = true;
434 case_GETOPT_HELP_CHAR;
436 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
439 usage (EXIT_FAILURE);
445 /* It doesn't make sense to use --print with either of
446 --bourne or --c-shell. */
447 if (print_database && syntax != SHELL_SYNTAX_UNKNOWN)
450 _("the options to output dircolors' internal database and\n\
451 to select a shell syntax are mutually exclusive"));
452 usage (EXIT_FAILURE);
455 if (!print_database < argc)
457 error (0, 0, _("extra operand %s"), quote (argv[!print_database]));
459 fprintf (stderr, "%s\n",
460 _("File operands cannot be combined with "
461 "--print-database (-p)."));
462 usage (EXIT_FAILURE);
467 char const *p = G_line;
468 while (p < G_line + sizeof G_line)
476 /* If shell syntax was not explicitly specified, try to guess it. */
477 if (syntax == SHELL_SYNTAX_UNKNOWN)
479 syntax = guess_shell_syntax ();
480 if (syntax == SHELL_SYNTAX_UNKNOWN)
482 error (EXIT_FAILURE, 0,
483 _("no SHELL environment variable, and no shell type option given"));
487 obstack_init (&lsc_obstack);
489 ok = dc_parse_stream (NULL, NULL);
491 ok = dc_parse_file (argv[0]);
495 size_t len = obstack_object_size (&lsc_obstack);
496 char *s = obstack_finish (&lsc_obstack);
500 if (syntax == SHELL_SYNTAX_BOURNE)
502 prefix = "LS_COLORS='";
503 suffix = "';\nexport LS_COLORS\n";
507 prefix = "setenv LS_COLORS '";
510 fputs (prefix, stdout);
511 fwrite (s, 1, len, stdout);
512 fputs (suffix, stdout);
516 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);