1 /* dircolors - output commands to set the LS_COLOR environment variable
2 Copyright (C) 1996-2004 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 #include <sys/types.h>
28 #include "dircolors.h"
36 /* The official name of this program (e.g., no `g' prefix). */
37 #define PROGRAM_NAME "dircolors"
39 #define AUTHORS "H. Peter Anvin"
41 #define obstack_chunk_alloc malloc
42 #define obstack_chunk_free free
51 #define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)
52 #define APPEND_TWO_CHAR_STRING(S) \
60 /* Accumulate in this obstack the value for the LS_COLORS environment
62 static struct obstack lsc_obstack;
64 /* True if the input file was the standard input. */
65 static bool have_read_stdin;
67 /* FIXME: associate with ls_codes? */
68 static const char *const slack_codes[] =
70 "NORMAL", "NORM", "FILE", "DIR", "LNK", "LINK",
71 "SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK",
72 "CHR", "CHAR", "DOOR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE",
73 "END", "ENDCODE", NULL
76 static const char *const ls_codes[] =
78 "no", "no", "fi", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
79 "so", "bd", "bd", "cd", "cd", "do", "ex", "lc", "lc", "rc", "rc", "ec", "ec"
82 static struct option const long_options[] =
84 {"bourne-shell", no_argument, NULL, 'b'},
85 {"sh", no_argument, NULL, 'b'},
86 {"csh", no_argument, NULL, 'c'},
87 {"c-shell", no_argument, NULL, 'c'},
88 {"print-database", no_argument, NULL, 'p'},
89 {GETOPT_HELP_OPTION_DECL},
90 {GETOPT_VERSION_OPTION_DECL},
99 if (status != EXIT_SUCCESS)
100 fprintf (stderr, _("Try `%s --help' for more information.\n"),
104 printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
106 Output commands to set the LS_COLORS environment variable.\n\
108 Determine format of output:\n\
109 -b, --sh, --bourne-shell output Bourne shell code to set LS_COLORS\n\
110 -c, --csh, --c-shell output C shell code to set LS_COLORS\n\
111 -p, --print-database output defaults\n\
113 fputs (HELP_OPTION_DESCRIPTION, stdout);
114 fputs (VERSION_OPTION_DESCRIPTION, stdout);
117 If FILE is specified, read it to determine which colors to use for which\n\
118 file types and extensions. Otherwise, a precompiled database is used.\n\
119 For details on the format of these files, run `dircolors --print-database'.\n\
121 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
127 /* If the SHELL environment variable is set to `csh' or `tcsh,'
128 assume C shell. Else Bourne shell. */
130 static enum Shell_syntax
131 guess_shell_syntax (void)
135 shell = getenv ("SHELL");
136 if (shell == NULL || *shell == '\0')
137 return SHELL_SYNTAX_UNKNOWN;
139 shell = base_name (shell);
141 if (STREQ (shell, "csh") || STREQ (shell, "tcsh"))
142 return SHELL_SYNTAX_C;
144 return SHELL_SYNTAX_BOURNE;
148 parse_line (unsigned char const *line, char **keyword, char **arg)
150 unsigned char const *p;
151 unsigned char const *keyword_start;
152 unsigned char const *arg_start;
157 for (p = line; ISSPACE (*p); ++p)
160 /* Ignore blank lines and shell-style comments. */
161 if (*p == '\0' || *p == '#')
166 while (!ISSPACE (*p) && *p != '\0')
171 *keyword = xstrndup ((const char *) keyword_start, p - keyword_start);
179 while (ISSPACE (*p));
181 if (*p == '\0' || *p == '#')
186 while (*p != '\0' && *p != '#')
189 for (--p; ISSPACE (*p); --p)
195 *arg = xstrndup ((const char *) arg_start, p - arg_start);
198 /* FIXME: Write a string to standard out, while watching for "dangerous"
199 sequences like unescaped : and = characters. */
202 append_quoted (const char *str)
204 bool need_backslash = true;
212 need_backslash = !need_backslash;
222 need_backslash = true;
231 /* Read the file open on FP (with name FILENAME). First, look for a
232 `TERM name' directive where name matches the current terminal type.
233 Once found, translate and accumulate the associated directives onto
234 the global obstack LSC_OBSTACK. Give a diagnostic
235 upon failure (unrecognized keyword is the only way to fail here).
236 Return true if successful. */
239 dc_parse_stream (FILE *fp, const char *filename)
241 size_t line_number = 0;
243 size_t line_chars_allocated = 0;
247 /* State for the parser. */
248 enum { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL } state = ST_GLOBAL;
250 /* Get terminal type */
251 term = getenv ("TERM");
252 if (term == NULL || *term == '\0')
265 line_length = getline (&line, &line_chars_allocated, fp);
266 if (line_length <= 0)
275 line = (char *) (G_line[line_number - 1]);
276 line_length = G_line_length[line_number - 1];
277 if (line_number > G_N_LINES)
281 parse_line ((unsigned char *) line, &keywd, &arg);
288 error (0, 0, _("%s:%lu: invalid line; missing second token"),
289 filename, (unsigned long int) line_number);
295 unrecognized = false;
296 if (strcasecmp (keywd, "TERM") == 0)
298 if (STREQ (arg, term))
300 else if (state != ST_TERMSURE)
305 if (state == ST_TERMSURE)
306 state = ST_TERMYES; /* Another TERM can cancel */
308 if (state != ST_TERMNO)
313 append_quoted (keywd);
318 else if (keywd[0] == '*')
320 append_quoted (keywd);
325 else if (strcasecmp (keywd, "OPTIONS") == 0
326 || strcasecmp (keywd, "COLOR") == 0
327 || strcasecmp (keywd, "EIGHTBIT") == 0)
335 for (i = 0; slack_codes[i] != NULL; ++i)
336 if (strcasecmp (keywd, slack_codes[i]) == 0)
339 if (slack_codes[i] != NULL)
341 APPEND_TWO_CHAR_STRING (ls_codes[i]);
358 if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES))
360 error (0, 0, _("%s:%lu: unrecognized keyword %s"),
361 (filename ? quote (filename) : _("<internal>")),
362 (unsigned long int) line_number, keywd);
375 dc_parse_file (const char *filename)
380 if (STREQ (filename, "-"))
382 have_read_stdin = true;
387 /* OPENOPTS is a macro. It varies with the system.
388 Some systems distinguish between internal and
389 external text representations. */
391 fp = fopen (filename, "r");
394 error (0, errno, "%s", quote (filename));
399 ok = dc_parse_stream (fp, filename);
401 if (fp != stdin && fclose (fp) == EOF)
403 error (0, errno, "%s", quote (filename));
411 main (int argc, char **argv)
415 enum Shell_syntax syntax = SHELL_SYNTAX_UNKNOWN;
416 bool print_database = false;
418 initialize_main (&argc, &argv);
419 program_name = argv[0];
420 setlocale (LC_ALL, "");
421 bindtextdomain (PACKAGE, LOCALEDIR);
422 textdomain (PACKAGE);
424 atexit (close_stdout);
426 while ((optc = getopt_long (argc, argv, "bcp", long_options, NULL)) != -1)
429 case 'b': /* Bourne shell syntax. */
430 syntax = SHELL_SYNTAX_BOURNE;
433 case 'c': /* C shell syntax. */
434 syntax = SHELL_SYNTAX_C;
438 print_database = true;
441 case_GETOPT_HELP_CHAR;
443 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
446 usage (EXIT_FAILURE);
452 /* It doesn't make sense to use --print with either of
453 --bourne or --c-shell. */
454 if (print_database && syntax != SHELL_SYNTAX_UNKNOWN)
457 _("the options to output dircolors' internal database and\n\
458 to select a shell syntax are mutually exclusive"));
459 usage (EXIT_FAILURE);
462 if (!print_database < argc)
464 error (0, 0, _("extra operand %s"), quote (argv[!print_database]));
466 fprintf (stderr, "%s\n",
467 _("File operands cannot be combined with "
468 "--print-database (-p)."));
469 usage (EXIT_FAILURE);
475 for (i = 0; i < G_N_LINES; i++)
477 fwrite (G_line[i], 1, G_line_length[i], stdout);
478 fputc ('\n', stdout);
483 /* If shell syntax was not explicitly specified, try to guess it. */
484 if (syntax == SHELL_SYNTAX_UNKNOWN)
486 syntax = guess_shell_syntax ();
487 if (syntax == SHELL_SYNTAX_UNKNOWN)
489 error (EXIT_FAILURE, 0,
490 _("no SHELL environment variable, and no shell type option given"));
494 obstack_init (&lsc_obstack);
496 ok = dc_parse_stream (NULL, NULL);
498 ok = dc_parse_file (argv[0]);
502 size_t len = obstack_object_size (&lsc_obstack);
503 char *s = obstack_finish (&lsc_obstack);
507 if (syntax == SHELL_SYNTAX_BOURNE)
509 prefix = "LS_COLORS='";
510 suffix = "';\nexport LS_COLORS\n";
514 prefix = "setenv LS_COLORS '";
517 fputs (prefix, stdout);
518 fwrite (s, 1, len, stdout);
519 fputs (suffix, stdout);
524 if (have_read_stdin && fclose (stdin) == EOF)
525 error (EXIT_FAILURE, errno, _("standard input"));
527 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);