*** empty log message ***
[coreutils.git] / src / dircolors.c
blob38e1c5071e73c7422c8f91026e743a11f0502f58
1 /* dircolors - output commands to set the LS_COLOR environment variable
2 Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000 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 "error.h"
31 #include "getline.h"
32 #include "obstack.h"
33 #include "quote.h"
35 /* The official name of this program (e.g., no `g' prefix). */
36 #define PROGRAM_NAME "dircolors"
38 #define AUTHORS "H. Peter Anvin"
40 #define obstack_chunk_alloc malloc
41 #define obstack_chunk_free free
43 enum Shell_syntax
45 SHELL_SYNTAX_BOURNE,
46 SHELL_SYNTAX_C,
47 SHELL_SYNTAX_UNKNOWN
50 #define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)
51 #define APPEND_TWO_CHAR_STRING(S) \
52 do \
53 { \
54 APPEND_CHAR (S[0]); \
55 APPEND_CHAR (S[1]); \
56 } \
57 while (0)
59 /* Accumulate in this obstack the value for the LS_COLORS environment
60 variable. */
61 static struct obstack lsc_obstack;
63 /* Nonzero if the input file was the standard input. */
64 static int have_read_stdin;
66 /* FIXME: associate with ls_codes? */
67 static const char *const slack_codes[] =
69 "NORMAL", "NORM", "FILE", "DIR", "LNK", "LINK",
70 "SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK",
71 "CHR", "CHAR", "DOOR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE",
72 "END", "ENDCODE", NULL
75 static const char *const ls_codes[] =
77 "no", "no", "fi", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
78 "so", "bd", "bd", "cd", "cd", "do", "ex", "lc", "lc", "rc", "rc", "ec", "ec"
81 static struct option const long_options[] =
83 {"bourne-shell", no_argument, NULL, 'b'},
84 {"sh", no_argument, NULL, 'b'},
85 {"csh", no_argument, NULL, 'c'},
86 {"c-shell", no_argument, NULL, 'c'},
87 {"print-database", no_argument, NULL, 'p'},
88 {GETOPT_HELP_OPTION_DECL},
89 {GETOPT_VERSION_OPTION_DECL},
90 {NULL, 0, NULL, 0}
93 char *program_name;
95 void
96 usage (int status)
98 if (status != 0)
99 fprintf (stderr, _("Try `%s --help' for more information.\n"),
100 program_name);
101 else
103 printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
104 printf (_("\
105 Output commands to set the LS_COLORS environment variable.\n\
107 Determine format of output:\n\
108 -b, --sh, --bourne-shell output Bourne shell code to set LS_COLORS\n\
109 -c, --csh, --c-shell output C shell code to set LS_COLORS\n\
110 -p, --print-database output defaults\n\
111 --help display this help and exit\n\
112 --version output version information and exit\n\
114 If FILE is specified, read it to determine which colors to use for which\n\
115 file types and extensions. Otherwise, a precompiled database is used.\n\
116 For details on the format of these files, run `dircolors --print-database'.\n\
117 "));
118 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
121 exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
124 static void *
125 xstrndup (const char *s, size_t n)
127 char *new = strndup (s, n);
128 if (new == NULL)
129 xalloc_die ();
130 return new;
133 /* If the SHELL environment variable is set to `csh' or `tcsh,'
134 assume C shell. Else Bourne shell. */
136 static enum Shell_syntax
137 guess_shell_syntax (void)
139 char *shell;
141 shell = getenv ("SHELL");
142 if (shell == NULL || *shell == '\0')
143 return SHELL_SYNTAX_UNKNOWN;
145 shell = base_name (shell);
147 if (STREQ (shell, "csh") || STREQ (shell, "tcsh"))
148 return SHELL_SYNTAX_C;
150 return SHELL_SYNTAX_BOURNE;
153 static void
154 parse_line (unsigned char const *line, char **keyword, char **arg)
156 unsigned char const *p;
157 unsigned char const *keyword_start;
158 unsigned char const *arg_start;
160 *keyword = NULL;
161 *arg = NULL;
163 for (p = line; ISSPACE (*p); ++p)
166 /* Ignore blank lines and shell-style comments. */
167 if (*p == '\0' || *p == '#')
168 return;
170 keyword_start = p;
172 while (!ISSPACE (*p) && *p != '\0')
174 ++p;
177 *keyword = xstrndup ((const char *) keyword_start, p - keyword_start);
178 if (*p == '\0')
179 return;
183 ++p;
185 while (ISSPACE (*p));
187 if (*p == '\0' || *p == '#')
188 return;
190 arg_start = p;
192 while (*p != '\0' && *p != '#')
193 ++p;
195 for (--p; ISSPACE (*p); --p)
197 /* empty */
199 ++p;
201 *arg = xstrndup ((const char *) arg_start, p - arg_start);
204 /* FIXME: Write a string to standard out, while watching for "dangerous"
205 sequences like unescaped : and = characters. */
207 static void
208 append_quoted (const char *str)
210 int need_backslash = 1;
212 while (*str != '\0')
214 switch (*str)
216 case '\\':
217 case '^':
218 need_backslash = !need_backslash;
219 break;
221 case ':':
222 case '=':
223 if (need_backslash)
224 APPEND_CHAR ('\\');
225 /* Fall through */
227 default:
228 need_backslash = 1;
229 break;
232 APPEND_CHAR (*str);
233 ++str;
237 /* Read the file open on FP (with name FILENAME). First, look for a
238 `TERM name' directive where name matches the current terminal type.
239 Once found, translate and accumulate the associated directives onto
240 the global obstack LSC_OBSTACK. Give a diagnostic and return nonzero
241 upon failure (unrecognized keyword is the only way to fail here).
242 Return zero otherwise. */
244 static int
245 dc_parse_stream (FILE *fp, const char *filename)
247 size_t line_number = 0;
248 char *line = NULL;
249 size_t line_chars_allocated = 0;
250 int state;
251 char *term;
252 int err = 0;
254 /* State for the parser. */
255 enum states { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL };
257 state = ST_GLOBAL;
259 /* Get terminal type */
260 term = getenv ("TERM");
261 if (term == NULL || *term == '\0')
262 term = "none";
264 while (1)
266 int line_length;
267 char *keywd, *arg;
268 int unrecognized;
270 ++line_number;
272 if (fp)
274 line_length = getline (&line, &line_chars_allocated, fp);
275 if (line_length <= 0)
277 if (line)
278 free (line);
279 break;
282 else
284 line = (char *) (G_line[line_number - 1]);
285 line_length = G_line_length[line_number - 1];
286 if (line_number > G_N_LINES)
287 break;
290 parse_line ((unsigned char *) line, &keywd, &arg);
292 if (keywd == NULL)
293 continue;
295 if (arg == NULL)
297 error (0, 0, _("%s:%lu: invalid line; missing second token"),
298 filename, (long unsigned) line_number);
299 err = 1;
300 free (keywd);
301 continue;
304 unrecognized = 0;
305 if (strcasecmp (keywd, "TERM") == 0)
307 if (STREQ (arg, term))
308 state = ST_TERMSURE;
309 else if (state != ST_TERMSURE)
310 state = ST_TERMNO;
312 else
314 if (state == ST_TERMSURE)
315 state = ST_TERMYES; /* Another TERM can cancel */
317 if (state != ST_TERMNO)
319 if (keywd[0] == '.')
321 APPEND_CHAR ('*');
322 append_quoted (keywd);
323 APPEND_CHAR ('=');
324 append_quoted (arg);
325 APPEND_CHAR (':');
327 else if (keywd[0] == '*')
329 append_quoted (keywd);
330 APPEND_CHAR ('=');
331 append_quoted (arg);
332 APPEND_CHAR (':');
334 else if (strcasecmp (keywd, "OPTIONS") == 0
335 || strcasecmp (keywd, "COLOR") == 0
336 || strcasecmp (keywd, "EIGHTBIT") == 0)
338 /* Ignore. */
340 else
342 int i;
344 for (i = 0; slack_codes[i] != NULL; ++i)
345 if (strcasecmp (keywd, slack_codes[i]) == 0)
346 break;
348 if (slack_codes[i] != NULL)
350 APPEND_TWO_CHAR_STRING (ls_codes[i]);
351 APPEND_CHAR ('=');
352 append_quoted (arg);
353 APPEND_CHAR (':');
355 else
357 unrecognized = 1;
361 else
363 unrecognized = 1;
367 if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES))
369 error (0, 0, _("%s:%lu: unrecognized keyword %s"),
370 (filename ? quote (filename) : _("<internal>")),
371 (long unsigned) line_number, keywd);
372 err = 1;
375 free (keywd);
376 if (arg)
377 free (arg);
380 return err;
383 static int
384 dc_parse_file (const char *filename)
386 FILE *fp;
387 int err;
389 if (STREQ (filename, "-"))
391 have_read_stdin = 1;
392 fp = stdin;
394 else
396 /* OPENOPTS is a macro. It varies with the system.
397 Some systems distinguish between internal and
398 external text representations. */
400 fp = fopen (filename, "r");
401 if (fp == NULL)
403 error (0, errno, "%s", quote (filename));
404 return 1;
408 err = dc_parse_stream (fp, filename);
410 if (fp != stdin && fclose (fp) == EOF)
412 error (0, errno, "%s", quote (filename));
413 return 1;
416 return err;
420 main (int argc, char **argv)
422 int err = 0;
423 int optc;
424 enum Shell_syntax syntax = SHELL_SYNTAX_UNKNOWN;
425 int print_database = 0;
427 program_name = argv[0];
428 setlocale (LC_ALL, "");
429 bindtextdomain (PACKAGE, LOCALEDIR);
430 textdomain (PACKAGE);
432 atexit (close_stdout);
434 while ((optc = getopt_long (argc, argv, "bcp", long_options, NULL)) != -1)
435 switch (optc)
437 case 'b': /* Bourne shell syntax. */
438 syntax = SHELL_SYNTAX_BOURNE;
439 break;
441 case 'c': /* C shell syntax. */
442 syntax = SHELL_SYNTAX_C;
443 break;
445 case 'p':
446 print_database = 1;
447 break;
449 case_GETOPT_HELP_CHAR;
451 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
453 default:
454 usage (1);
457 argc -= optind;
458 argv += optind;
460 /* It doesn't make sense to use --print with either of
461 --bourne or --c-shell. */
462 if (print_database && syntax != SHELL_SYNTAX_UNKNOWN)
464 error (0, 0,
465 _("the options to output dircolors' internal database and\n\
466 to select a shell syntax are mutually exclusive"));
467 usage (1);
470 if (print_database && argc > 0)
472 error (0, 0,
473 _("no FILE arguments may be used with the option to output\n\
474 dircolors' internal database"));
475 usage (1);
478 if (!print_database && argc > 1)
480 error (0, 0, _("too many arguments"));
481 usage (1);
484 if (print_database)
486 int i;
487 for (i = 0; i < G_N_LINES; i++)
489 fwrite (G_line[i], 1, G_line_length[i], stdout);
490 fputc ('\n', stdout);
493 else
495 /* If shell syntax was not explicitly specified, try to guess it. */
496 if (syntax == SHELL_SYNTAX_UNKNOWN)
498 syntax = guess_shell_syntax ();
499 if (syntax == SHELL_SYNTAX_UNKNOWN)
501 error (EXIT_FAILURE, 0,
502 _("no SHELL environment variable, and no shell type option given"));
506 obstack_init (&lsc_obstack);
507 if (argc == 0)
508 err = dc_parse_stream (NULL, NULL);
509 else
510 err = dc_parse_file (argv[0]);
512 if (!err)
514 size_t len = obstack_object_size (&lsc_obstack);
515 char *s = obstack_finish (&lsc_obstack);
516 const char *prefix;
517 const char *suffix;
519 if (syntax == SHELL_SYNTAX_BOURNE)
521 prefix = "LS_COLORS='";
522 suffix = "';\nexport LS_COLORS\n";
524 else
526 prefix = "setenv LS_COLORS '";
527 suffix = "'\n";
529 fputs (prefix, stdout);
530 fwrite (s, 1, len, stdout);
531 fputs (suffix, stdout);
536 if (have_read_stdin && fclose (stdin) == EOF)
537 error (EXIT_FAILURE, errno, _("standard input"));
539 exit (err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);