1 /* dircolors - output commands to set the LS_COLOR environment variable
2 Copyright (C) 1994, 1995, 1997 H. Peter Anvin
3 Copyright (C) 96, 1997 Free Software Foundation, Inc.
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
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 #include <sys/types.h>
30 #include "long-options.h"
33 #include "dircolors.h"
35 #define obstack_chunk_alloc malloc
36 #define obstack_chunk_free free
53 #define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)
54 #define APPEND_TWO_CHAR_STRING(S) \
62 /* Accumulate in this obstack the value for the LS_COLORS environment
64 static struct obstack lsc_obstack;
66 /* Nonzero if the input file was the standard input. */
67 static int have_read_stdin;
69 /* FIXME: associate with ls_codes? */
70 static const char *const slack_codes[] =
72 "NORMAL", "NORM", "FILE", "DIR", "LNK", "LINK",
73 "SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK",
74 "CHR", "CHAR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE", "END",
78 static const char *const ls_codes[] =
80 "no", "no", "fi", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
81 "so", "bd", "bd", "cd", "cd", "ex", "lc", "lc", "rc", "rc", "ec", "ec"
84 static struct option const long_options[] =
86 {"bourne-shell", no_argument, NULL, 'b'},
87 {"sh", no_argument, NULL, 'b'},
88 {"csh", no_argument, NULL, 'c'},
89 {"c-shell", no_argument, NULL, 'c'},
90 {"help", no_argument, NULL, 'h'},
91 {"print-database", no_argument, NULL, 'p'},
92 {"version", no_argument, NULL, 'v'},
102 fprintf (stderr, _("Try `%s --help' for more information.\n"),
106 printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
108 Output commands to set the LS_COLORS environment variable.\n\
110 Determine format of output:\n\
111 -b, --sh, --bourne-shell output Bourne shell code to set LS_COLORS\n\
112 -c, --csh, --c-shell output C shell code to set LS_COLORS\n\
113 -p, --print-database output defaults\n\
114 --help display this help and exit\n\
115 --version output version information and exit\n\
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 puts (_("\nReport bugs to <fileutils-bugs@gnu.ai.mit.edu>."));
124 exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
128 xstrndup (const char *s, size_t n)
130 char *new = strndup (s, n);
132 error (EXIT_FAILURE, 0, _("virtual memory exhausted"));
136 /* If the SHELL environment variable is set to `csh' or `tcsh,'
137 assume C shell. Else Bourne shell. */
139 static enum Shell_syntax
140 guess_shell_syntax (void)
144 shell = getenv ("SHELL");
145 if (shell == NULL || *shell == '\0')
146 return SHELL_SYNTAX_UNKNOWN;
148 shell = base_name (shell);
150 if (STREQ (shell, "csh") || STREQ (shell, "tcsh"))
151 return SHELL_SYNTAX_C;
153 return SHELL_SYNTAX_BOURNE;
157 parse_line (const char *line, char **keyword, char **arg)
160 const char *keyword_start;
161 const char *arg_start;
166 for (p = line; isspace (*p); ++p)
169 /* Ignore blank lines and shell-style comments. */
170 if (*p == '\0' || *p == '#')
175 while (!isspace (*p) && *p != '\0')
180 *keyword = xstrndup (keyword_start, p - keyword_start);
188 while (isspace (*p));
190 if (*p == '\0' || *p == '#')
195 while (*p != '\0' && *p != '#')
198 for (--p; isspace (*p); --p)
204 *arg = xstrndup (arg_start, p - arg_start);
207 /* FIXME: Write a string to standard out, while watching for "dangerous"
208 sequences like unescaped : and = characters. */
211 append_quoted (const char *str)
213 int need_backslash = 1;
221 need_backslash = !need_backslash;
240 /* Read the file open on FP (with name FILENAME). First, look for a
241 `TERM name' directive where name matches the current terminal type.
242 Once found, translate and accumulate the associated directives onto
243 the global obstack LSC_OBSTACK. Give a diagnostic and return nonzero
244 upon failure (unrecognized keyword is the only way to fail here).
245 Return zero otherwise. */
248 dc_parse_stream (FILE *fp, const char *filename)
250 size_t line_number = 0;
252 size_t line_chars_allocated = 0;
257 /* State for the parser. */
258 enum states { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL };
262 /* Get terminal type */
263 term = getenv ("TERM");
264 if (term == NULL || *term == '\0')
277 line_length = getline (&line, &line_chars_allocated, fp);
278 if (line_length <= 0)
287 line = (char *) (G_line[line_number - 1]);
288 line_length = G_line_length[line_number - 1];
289 if (line_number > G_N_LINES)
293 parse_line (line, &keywd, &arg);
299 if (strcasecmp (keywd, "TERM") == 0)
301 if (strcmp (arg, term) == 0)
303 else if (state != ST_TERMSURE)
308 if (state == ST_TERMSURE)
309 state = ST_TERMYES; /* Another TERM can cancel */
311 if (state != ST_TERMNO)
316 append_quoted (keywd);
321 else if (keywd[0] == '*')
323 append_quoted (keywd);
328 else if (strcasecmp (keywd, "OPTIONS") == 0
329 || strcasecmp (keywd, "COLOR") == 0
330 || strcasecmp (keywd, "EIGHTBIT") == 0)
338 for (i = 0; slack_codes[i] != NULL; ++i)
339 if (strcasecmp (keywd, slack_codes[i]) == 0)
342 if (slack_codes[i] != NULL)
344 APPEND_TWO_CHAR_STRING (ls_codes[i]);
361 if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES))
363 error (0, 0, _("%s:%lu: unrecognized keyword `%s'"),
364 filename, (long unsigned) line_number, keywd);
377 dc_parse_file (const char *filename)
382 if (strcmp (filename, "-") == 0)
389 /* OPENOPTS is a macro. It varies with the system.
390 Some systems distinguish between internal and
391 external text representations. */
393 fp = fopen (filename, "r");
396 error (0, errno, "%s", filename);
401 err = dc_parse_stream (fp, filename);
403 if (fp != stdin && fclose (fp) == EOF)
405 error (0, errno, "%s", filename);
413 main (int argc, char **argv)
417 enum Shell_syntax syntax = SHELL_SYNTAX_UNKNOWN;
418 int print_database = 0;
420 program_name = argv[0];
421 setlocale (LC_ALL, "");
422 bindtextdomain (PACKAGE, LOCALEDIR);
423 textdomain (PACKAGE);
425 parse_long_options (argc, argv, "dircolors", GNU_PACKAGE, VERSION, usage);
427 while ((optc = getopt_long (argc, argv, "bcp", long_options, NULL)) != -1)
430 case 'b': /* Bourne shell syntax. */
431 syntax = SHELL_SYNTAX_BOURNE;
434 case 'c': /* C shell syntax. */
435 syntax = SHELL_SYNTAX_C;
449 /* It doesn't make sense to use --print with either of
450 --bourne or --c-shell. */
451 if (print_database && syntax != SHELL_SYNTAX_UNKNOWN)
454 _("the options to output dircolors' internal database and\n\
455 to select a shell syntax are mutually exclusive"));
459 if (print_database && argc > 0)
462 _("no FILE arguments may be used with the option to output\n\
463 dircolors' internal database"));
467 if (!print_database && argc > 1)
469 error (0, 0, _("too many arguments"));
476 for (i = 0; i < G_N_LINES; i++)
478 fwrite (G_line[i], 1, G_line_length[i], stdout);
479 fputc ('\n', stdout);
484 /* If shell syntax was not explicitly specified, try to guess it. */
485 if (syntax == SHELL_SYNTAX_UNKNOWN)
487 syntax = guess_shell_syntax ();
488 if (syntax == SHELL_SYNTAX_UNKNOWN)
490 error (EXIT_FAILURE, 0,
491 _("no SHELL environment variable, and no shell type option given"));
495 obstack_init (&lsc_obstack);
497 err = dc_parse_stream (NULL, NULL);
499 err = dc_parse_file (argv[0]);
503 size_t len = obstack_object_size (&lsc_obstack);
504 char *s = obstack_finish (&lsc_obstack);
508 if (syntax == SHELL_SYNTAX_BOURNE)
510 prefix = "LS_COLORS='";
511 suffix = "';\nexport LS_COLORS\n";
515 prefix = "setenv LS_COLORS '";
518 fputs (prefix, stdout);
519 fwrite (s, 1, len, stdout);
520 fputs (suffix, stdout);
524 if (fclose (stdout) == EOF)
525 error (EXIT_FAILURE, errno, _("write error"));
527 if (have_read_stdin && fclose (stdin) == EOF)
528 error (EXIT_FAILURE, errno, _("standard input"));
530 exit (err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);