*** empty log message ***
[coreutils.git] / src / dircolors.c
blob9c7fc2ebb8faa1908839baffa1d417172adadf24
1 /* dircolors - output commands to set the LS_COLOR environment variable
2 Copyright (C) 1994, 1995, 1997, 1998, 1999 H. Peter Anvin
3 Copyright (C) 96, 97, 1998 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 "getline.h"
30 #include "long-options.h"
31 #include "closeout.h"
32 #include "error.h"
33 #include "obstack.h"
34 #include "dircolors.h"
36 #define obstack_chunk_alloc malloc
37 #define obstack_chunk_free free
39 char *strndup();
41 enum Shell_syntax
43 SHELL_SYNTAX_BOURNE,
44 SHELL_SYNTAX_C,
45 SHELL_SYNTAX_UNKNOWN
48 #define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)
49 #define APPEND_TWO_CHAR_STRING(S) \
50 do \
51 { \
52 APPEND_CHAR (S[0]); \
53 APPEND_CHAR (S[1]); \
54 } \
55 while (0)
57 /* Accumulate in this obstack the value for the LS_COLORS environment
58 variable. */
59 static struct obstack lsc_obstack;
61 /* Nonzero if the input file was the standard input. */
62 static int have_read_stdin;
64 /* FIXME: associate with ls_codes? */
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", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE", "END",
70 "ENDCODE", NULL
73 static const char *const ls_codes[] =
75 "no", "no", "fi", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
76 "so", "bd", "bd", "cd", "cd", "ex", "lc", "lc", "rc", "rc", "ec", "ec"
79 static struct option const long_options[] =
81 {"bourne-shell", no_argument, NULL, 'b'},
82 {"sh", no_argument, NULL, 'b'},
83 {"csh", no_argument, NULL, 'c'},
84 {"c-shell", no_argument, NULL, 'c'},
85 {"help", no_argument, NULL, 'h'},
86 {"print-database", no_argument, NULL, 'p'},
87 {"version", no_argument, NULL, 'v'},
88 {NULL, 0, NULL, 0}
91 char *program_name;
93 void
94 usage (int status)
96 if (status != 0)
97 fprintf (stderr, _("Try `%s --help' for more information.\n"),
98 program_name);
99 else
101 printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
102 printf (_("\
103 Output commands to set the LS_COLORS environment variable.\n\
105 Determine format of output:\n\
106 -b, --sh, --bourne-shell output Bourne shell code to set LS_COLORS\n\
107 -c, --csh, --c-shell output C shell code to set LS_COLORS\n\
108 -p, --print-database output defaults\n\
109 --help display this help and exit\n\
110 --version output version information and exit\n\
112 If FILE is specified, read it to determine which colors to use for which\n\
113 file types and extensions. Otherwise, a precompiled database is used.\n\
114 For details on the format of these files, run `dircolors --print-database'.\n\
115 "));
116 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
117 close_stdout ();
120 exit (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
123 static void *
124 xstrndup (const char *s, size_t n)
126 char *new = strndup (s, n);
127 if (new == NULL)
128 error (EXIT_FAILURE, 0, _("virtual memory exhausted"));
129 return new;
132 /* If the SHELL environment variable is set to `csh' or `tcsh,'
133 assume C shell. Else Bourne shell. */
135 static enum Shell_syntax
136 guess_shell_syntax (void)
138 char *shell;
140 shell = getenv ("SHELL");
141 if (shell == NULL || *shell == '\0')
142 return SHELL_SYNTAX_UNKNOWN;
144 shell = base_name (shell);
146 if (STREQ (shell, "csh") || STREQ (shell, "tcsh"))
147 return SHELL_SYNTAX_C;
149 return SHELL_SYNTAX_BOURNE;
152 static void
153 parse_line (unsigned char const *line, char **keyword, char **arg)
155 unsigned char const *p;
156 unsigned char const *keyword_start;
157 unsigned char const *arg_start;
159 *keyword = NULL;
160 *arg = NULL;
162 for (p = line; ISSPACE (*p); ++p)
165 /* Ignore blank lines and shell-style comments. */
166 if (*p == '\0' || *p == '#')
167 return;
169 keyword_start = p;
171 while (!ISSPACE (*p) && *p != '\0')
173 ++p;
176 *keyword = xstrndup ((const char *) keyword_start, p - keyword_start);
177 if (*p == '\0')
178 return;
182 ++p;
184 while (ISSPACE (*p));
186 if (*p == '\0' || *p == '#')
187 return;
189 arg_start = p;
191 while (*p != '\0' && *p != '#')
192 ++p;
194 for (--p; ISSPACE (*p); --p)
196 /* empty */
198 ++p;
200 *arg = xstrndup ((const char *) arg_start, p - arg_start);
203 /* FIXME: Write a string to standard out, while watching for "dangerous"
204 sequences like unescaped : and = characters. */
206 static void
207 append_quoted (const char *str)
209 int need_backslash = 1;
211 while (*str != '\0')
213 switch (*str)
215 case '\\':
216 case '^':
217 need_backslash = !need_backslash;
218 break;
220 case ':':
221 case '=':
222 if (need_backslash)
223 APPEND_CHAR ('\\');
224 /* Fall through */
226 default:
227 need_backslash = 1;
228 break;
231 APPEND_CHAR (*str);
232 ++str;
236 /* Read the file open on FP (with name FILENAME). First, look for a
237 `TERM name' directive where name matches the current terminal type.
238 Once found, translate and accumulate the associated directives onto
239 the global obstack LSC_OBSTACK. Give a diagnostic and return nonzero
240 upon failure (unrecognized keyword is the only way to fail here).
241 Return zero otherwise. */
243 static int
244 dc_parse_stream (FILE *fp, const char *filename)
246 size_t line_number = 0;
247 char *line = NULL;
248 size_t line_chars_allocated = 0;
249 int state;
250 char *term;
251 int err = 0;
253 /* State for the parser. */
254 enum states { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL };
256 state = ST_GLOBAL;
258 /* Get terminal type */
259 term = getenv ("TERM");
260 if (term == NULL || *term == '\0')
261 term = "none";
263 while (1)
265 int line_length;
266 char *keywd, *arg;
267 int unrecognized;
269 ++line_number;
271 if (fp)
273 line_length = getline (&line, &line_chars_allocated, fp);
274 if (line_length <= 0)
276 if (line)
277 free (line);
278 break;
281 else
283 line = (char *) (G_line[line_number - 1]);
284 line_length = G_line_length[line_number - 1];
285 if (line_number > G_N_LINES)
286 break;
289 parse_line ((unsigned char *) line, &keywd, &arg);
291 if (keywd == NULL)
292 continue;
294 if (arg == NULL)
296 error (0, 0, _("%s:%lu: invalid line; missing second token"),
297 filename, (long unsigned) line_number);
298 err = 1;
299 free (keywd);
300 continue;
303 unrecognized = 0;
304 if (strcasecmp (keywd, "TERM") == 0)
306 if (STREQ (arg, term))
307 state = ST_TERMSURE;
308 else if (state != ST_TERMSURE)
309 state = ST_TERMNO;
311 else
313 if (state == ST_TERMSURE)
314 state = ST_TERMYES; /* Another TERM can cancel */
316 if (state != ST_TERMNO)
318 if (keywd[0] == '.')
320 APPEND_CHAR ('*');
321 append_quoted (keywd);
322 APPEND_CHAR ('=');
323 append_quoted (arg);
324 APPEND_CHAR (':');
326 else if (keywd[0] == '*')
328 append_quoted (keywd);
329 APPEND_CHAR ('=');
330 append_quoted (arg);
331 APPEND_CHAR (':');
333 else if (strcasecmp (keywd, "OPTIONS") == 0
334 || strcasecmp (keywd, "COLOR") == 0
335 || strcasecmp (keywd, "EIGHTBIT") == 0)
337 /* Ignore. */
339 else
341 int i;
343 for (i = 0; slack_codes[i] != NULL; ++i)
344 if (strcasecmp (keywd, slack_codes[i]) == 0)
345 break;
347 if (slack_codes[i] != NULL)
349 APPEND_TWO_CHAR_STRING (ls_codes[i]);
350 APPEND_CHAR ('=');
351 append_quoted (arg);
352 APPEND_CHAR (':');
354 else
356 unrecognized = 1;
360 else
362 unrecognized = 1;
366 if (unrecognized && (state == ST_TERMSURE || state == ST_TERMYES))
368 error (0, 0, _("%s:%lu: unrecognized keyword `%s'"),
369 filename, (long unsigned) line_number, keywd);
370 err = 1;
373 free (keywd);
374 if (arg)
375 free (arg);
378 return err;
381 static int
382 dc_parse_file (const char *filename)
384 FILE *fp;
385 int err;
387 if (STREQ (filename, "-"))
389 have_read_stdin = 1;
390 fp = stdin;
392 else
394 /* OPENOPTS is a macro. It varies with the system.
395 Some systems distinguish between internal and
396 external text representations. */
398 fp = fopen (filename, "r");
399 if (fp == NULL)
401 error (0, errno, "%s", filename);
402 return 1;
406 err = dc_parse_stream (fp, filename);
408 if (fp != stdin && fclose (fp) == EOF)
410 error (0, errno, "%s", filename);
411 return 1;
414 return err;
418 main (int argc, char **argv)
420 int err = 0;
421 int optc;
422 enum Shell_syntax syntax = SHELL_SYNTAX_UNKNOWN;
423 int print_database = 0;
425 program_name = argv[0];
426 setlocale (LC_ALL, "");
427 bindtextdomain (PACKAGE, LOCALEDIR);
428 textdomain (PACKAGE);
430 parse_long_options (argc, argv, "dircolors", GNU_PACKAGE, VERSION, usage);
432 while ((optc = getopt_long (argc, argv, "bcp", long_options, NULL)) != -1)
433 switch (optc)
435 case 'b': /* Bourne shell syntax. */
436 syntax = SHELL_SYNTAX_BOURNE;
437 break;
439 case 'c': /* C shell syntax. */
440 syntax = SHELL_SYNTAX_C;
441 break;
443 case 'p':
444 print_database = 1;
445 break;
447 default:
448 usage (1);
451 argc -= optind;
452 argv += optind;
454 /* It doesn't make sense to use --print with either of
455 --bourne or --c-shell. */
456 if (print_database && syntax != SHELL_SYNTAX_UNKNOWN)
458 error (0, 0,
459 _("the options to output dircolors' internal database and\n\
460 to select a shell syntax are mutually exclusive"));
461 usage (1);
464 if (print_database && argc > 0)
466 error (0, 0,
467 _("no FILE arguments may be used with the option to output\n\
468 dircolors' internal database"));
469 usage (1);
472 if (!print_database && argc > 1)
474 error (0, 0, _("too many arguments"));
475 usage (1);
478 if (print_database)
480 int i;
481 for (i = 0; i < G_N_LINES; i++)
483 fwrite (G_line[i], 1, G_line_length[i], stdout);
484 fputc ('\n', stdout);
487 else
489 /* If shell syntax was not explicitly specified, try to guess it. */
490 if (syntax == SHELL_SYNTAX_UNKNOWN)
492 syntax = guess_shell_syntax ();
493 if (syntax == SHELL_SYNTAX_UNKNOWN)
495 error (EXIT_FAILURE, 0,
496 _("no SHELL environment variable, and no shell type option given"));
500 obstack_init (&lsc_obstack);
501 if (argc == 0)
502 err = dc_parse_stream (NULL, NULL);
503 else
504 err = dc_parse_file (argv[0]);
506 if (!err)
508 size_t len = obstack_object_size (&lsc_obstack);
509 char *s = obstack_finish (&lsc_obstack);
510 const char *prefix;
511 const char *suffix;
513 if (syntax == SHELL_SYNTAX_BOURNE)
515 prefix = "LS_COLORS='";
516 suffix = "';\nexport LS_COLORS\n";
518 else
520 prefix = "setenv LS_COLORS '";
521 suffix = "'\n";
523 fputs (prefix, stdout);
524 fwrite (s, 1, len, stdout);
525 fputs (suffix, stdout);
529 close_stdout ();
531 if (have_read_stdin && fclose (stdin) == EOF)
532 error (EXIT_FAILURE, errno, _("standard input"));
534 exit (err == 0 ? EXIT_SUCCESS : EXIT_FAILURE);