*** empty log message ***
[coreutils.git] / src / dircolors.c
blobf9b6c14883cfab2829d54d5239d29c737cd12ba4
1 /* dircolors - output commands to set the LS_COLOR environment variable
2 Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001 H. Peter Anvin
3 Copyright (C) 1996-2000 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)
8 any later version.
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. */
19 #if HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 #include <sys/types.h>
24 #include <ctype.h>
25 #include <getopt.h>
26 #include <stdio.h>
28 #include "system.h"
29 #include "dircolors.h"
30 #include "dirname.h"
31 #include "error.h"
32 #include "getline.h"
33 #include "obstack.h"
34 #include "quote.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
44 enum Shell_syntax
46 SHELL_SYNTAX_BOURNE,
47 SHELL_SYNTAX_C,
48 SHELL_SYNTAX_UNKNOWN
51 #define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)
52 #define APPEND_TWO_CHAR_STRING(S) \
53 do \
54 { \
55 APPEND_CHAR (S[0]); \
56 APPEND_CHAR (S[1]); \
57 } \
58 while (0)
60 /* Accumulate in this obstack the value for the LS_COLORS environment
61 variable. */
62 static struct obstack lsc_obstack;
64 /* Nonzero if the input file was the standard input. */
65 static int 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},
91 {NULL, 0, NULL, 0}
94 char *program_name;
96 void
97 usage (int status)
99 if (status != 0)
100 fprintf (stderr, _("Try `%s --help' for more information.\n"),
101 program_name);
102 else
104 printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
105 fputs (_("\
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\
112 "), stdout);
113 fputs (HELP_OPTION_DESCRIPTION, stdout);
114 fputs (VERSION_OPTION_DESCRIPTION, stdout);
115 fputs (_("\
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\
120 "), stdout);
121 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
124 exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
127 static void *
128 xstrndup (const char *s, size_t n)
130 char *new = strndup (s, n);
131 if (new == NULL)
132 xalloc_die ();
133 return new;
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)
142 char *shell;
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;
156 static void
157 parse_line (unsigned char const *line, char **keyword, char **arg)
159 unsigned char const *p;
160 unsigned char const *keyword_start;
161 unsigned char const *arg_start;
163 *keyword = NULL;
164 *arg = NULL;
166 for (p = line; ISSPACE (*p); ++p)
169 /* Ignore blank lines and shell-style comments. */
170 if (*p == '\0' || *p == '#')
171 return;
173 keyword_start = p;
175 while (!ISSPACE (*p) && *p != '\0')
177 ++p;
180 *keyword = xstrndup ((const char *) keyword_start, p - keyword_start);
181 if (*p == '\0')
182 return;
186 ++p;
188 while (ISSPACE (*p));
190 if (*p == '\0' || *p == '#')
191 return;
193 arg_start = p;
195 while (*p != '\0' && *p != '#')
196 ++p;
198 for (--p; ISSPACE (*p); --p)
200 /* empty */
202 ++p;
204 *arg = xstrndup ((const char *) arg_start, p - arg_start);
207 /* FIXME: Write a string to standard out, while watching for "dangerous"
208 sequences like unescaped : and = characters. */
210 static void
211 append_quoted (const char *str)
213 int need_backslash = 1;
215 while (*str != '\0')
217 switch (*str)
219 case '\\':
220 case '^':
221 need_backslash = !need_backslash;
222 break;
224 case ':':
225 case '=':
226 if (need_backslash)
227 APPEND_CHAR ('\\');
228 /* Fall through */
230 default:
231 need_backslash = 1;
232 break;
235 APPEND_CHAR (*str);
236 ++str;
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. */
247 static int
248 dc_parse_stream (FILE *fp, const char *filename)
250 size_t line_number = 0;
251 char *line = NULL;
252 size_t line_chars_allocated = 0;
253 int state;
254 char *term;
255 int err = 0;
257 /* State for the parser. */
258 enum states { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL };
260 state = ST_GLOBAL;
262 /* Get terminal type */
263 term = getenv ("TERM");
264 if (term == NULL || *term == '\0')
265 term = "none";
267 while (1)
269 int line_length;
270 char *keywd, *arg;
271 int unrecognized;
273 ++line_number;
275 if (fp)
277 line_length = getline (&line, &line_chars_allocated, fp);
278 if (line_length <= 0)
280 if (line)
281 free (line);
282 break;
285 else
287 line = (char *) (G_line[line_number - 1]);
288 line_length = G_line_length[line_number - 1];
289 if (line_number > G_N_LINES)
290 break;
293 parse_line ((unsigned char *) line, &keywd, &arg);
295 if (keywd == NULL)
296 continue;
298 if (arg == NULL)
300 error (0, 0, _("%s:%lu: invalid line; missing second token"),
301 filename, (long unsigned) line_number);
302 err = 1;
303 free (keywd);
304 continue;
307 unrecognized = 0;
308 if (strcasecmp (keywd, "TERM") == 0)
310 if (STREQ (arg, term))
311 state = ST_TERMSURE;
312 else if (state != ST_TERMSURE)
313 state = ST_TERMNO;
315 else
317 if (state == ST_TERMSURE)
318 state = ST_TERMYES; /* Another TERM can cancel */
320 if (state != ST_TERMNO)
322 if (keywd[0] == '.')
324 APPEND_CHAR ('*');
325 append_quoted (keywd);
326 APPEND_CHAR ('=');
327 append_quoted (arg);
328 APPEND_CHAR (':');
330 else if (keywd[0] == '*')
332 append_quoted (keywd);
333 APPEND_CHAR ('=');
334 append_quoted (arg);
335 APPEND_CHAR (':');
337 else if (strcasecmp (keywd, "OPTIONS") == 0
338 || strcasecmp (keywd, "COLOR") == 0
339 || strcasecmp (keywd, "EIGHTBIT") == 0)
341 /* Ignore. */
343 else
345 int i;
347 for (i = 0; slack_codes[i] != NULL; ++i)
348 if (strcasecmp (keywd, slack_codes[i]) == 0)
349 break;
351 if (slack_codes[i] != NULL)
353 APPEND_TWO_CHAR_STRING (ls_codes[i]);
354 APPEND_CHAR ('=');
355 append_quoted (arg);
356 APPEND_CHAR (':');
358 else
360 unrecognized = 1;
364 else
366 unrecognized = 1;
370 if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES))
372 error (0, 0, _("%s:%lu: unrecognized keyword %s"),
373 (filename ? quote (filename) : _("<internal>")),
374 (long unsigned) line_number, keywd);
375 err = 1;
378 free (keywd);
379 if (arg)
380 free (arg);
383 return err;
386 static int
387 dc_parse_file (const char *filename)
389 FILE *fp;
390 int err;
392 if (STREQ (filename, "-"))
394 have_read_stdin = 1;
395 fp = stdin;
397 else
399 /* OPENOPTS is a macro. It varies with the system.
400 Some systems distinguish between internal and
401 external text representations. */
403 fp = fopen (filename, "r");
404 if (fp == NULL)
406 error (0, errno, "%s", quote (filename));
407 return 1;
411 err = dc_parse_stream (fp, filename);
413 if (fp != stdin && fclose (fp) == EOF)
415 error (0, errno, "%s", quote (filename));
416 return 1;
419 return err;
423 main (int argc, char **argv)
425 int err = 0;
426 int optc;
427 enum Shell_syntax syntax = SHELL_SYNTAX_UNKNOWN;
428 int print_database = 0;
430 program_name = argv[0];
431 setlocale (LC_ALL, "");
432 bindtextdomain (PACKAGE, LOCALEDIR);
433 textdomain (PACKAGE);
435 atexit (close_stdout);
437 while ((optc = getopt_long (argc, argv, "bcp", long_options, NULL)) != -1)
438 switch (optc)
440 case 'b': /* Bourne shell syntax. */
441 syntax = SHELL_SYNTAX_BOURNE;
442 break;
444 case 'c': /* C shell syntax. */
445 syntax = SHELL_SYNTAX_C;
446 break;
448 case 'p':
449 print_database = 1;
450 break;
452 case_GETOPT_HELP_CHAR;
454 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
456 default:
457 usage (1);
460 argc -= optind;
461 argv += optind;
463 /* It doesn't make sense to use --print with either of
464 --bourne or --c-shell. */
465 if (print_database && syntax != SHELL_SYNTAX_UNKNOWN)
467 error (0, 0,
468 _("the options to output dircolors' internal database and\n\
469 to select a shell syntax are mutually exclusive"));
470 usage (1);
473 if (print_database && argc > 0)
475 error (0, 0,
476 _("no FILE arguments may be used with the option to output\n\
477 dircolors' internal database"));
478 usage (1);
481 if (!print_database && argc > 1)
483 error (0, 0, _("too many arguments"));
484 usage (1);
487 if (print_database)
489 int i;
490 for (i = 0; i < G_N_LINES; i++)
492 fwrite (G_line[i], 1, G_line_length[i], stdout);
493 fputc ('\n', stdout);
496 else
498 /* If shell syntax was not explicitly specified, try to guess it. */
499 if (syntax == SHELL_SYNTAX_UNKNOWN)
501 syntax = guess_shell_syntax ();
502 if (syntax == SHELL_SYNTAX_UNKNOWN)
504 error (EXIT_FAILURE, 0,
505 _("no SHELL environment variable, and no shell type option given"));
509 obstack_init (&lsc_obstack);
510 if (argc == 0)
511 err = dc_parse_stream (NULL, NULL);
512 else
513 err = dc_parse_file (argv[0]);
515 if (!err)
517 size_t len = obstack_object_size (&lsc_obstack);
518 char *s = obstack_finish (&lsc_obstack);
519 const char *prefix;
520 const char *suffix;
522 if (syntax == SHELL_SYNTAX_BOURNE)
524 prefix = "LS_COLORS='";
525 suffix = "';\nexport LS_COLORS\n";
527 else
529 prefix = "setenv LS_COLORS '";
530 suffix = "'\n";
532 fputs (prefix, stdout);
533 fwrite (s, 1, len, stdout);
534 fputs (suffix, stdout);
539 if (have_read_stdin && fclose (stdin) == EOF)
540 error (EXIT_FAILURE, errno, _("standard input"));
542 exit (err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);