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)
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>
29 #include "dircolors.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
50 #define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)
51 #define APPEND_TWO_CHAR_STRING(S) \
59 /* Accumulate in this obstack the value for the LS_COLORS environment
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
},
99 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
103 printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name
);
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\
118 puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
121 exit (status
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
);
125 xstrndup (const char *s
, size_t n
)
127 char *new = strndup (s
, n
);
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)
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
;
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
;
163 for (p
= line
; ISSPACE (*p
); ++p
)
166 /* Ignore blank lines and shell-style comments. */
167 if (*p
== '\0' || *p
== '#')
172 while (!ISSPACE (*p
) && *p
!= '\0')
177 *keyword
= xstrndup ((const char *) keyword_start
, p
- keyword_start
);
185 while (ISSPACE (*p
));
187 if (*p
== '\0' || *p
== '#')
192 while (*p
!= '\0' && *p
!= '#')
195 for (--p
; ISSPACE (*p
); --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. */
208 append_quoted (const char *str
)
210 int need_backslash
= 1;
218 need_backslash
= !need_backslash
;
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. */
245 dc_parse_stream (FILE *fp
, const char *filename
)
247 size_t line_number
= 0;
249 size_t line_chars_allocated
= 0;
254 /* State for the parser. */
255 enum states
{ ST_TERMNO
, ST_TERMYES
, ST_TERMSURE
, ST_GLOBAL
};
259 /* Get terminal type */
260 term
= getenv ("TERM");
261 if (term
== NULL
|| *term
== '\0')
274 line_length
= getline (&line
, &line_chars_allocated
, fp
);
275 if (line_length
<= 0)
284 line
= (char *) (G_line
[line_number
- 1]);
285 line_length
= G_line_length
[line_number
- 1];
286 if (line_number
> G_N_LINES
)
290 parse_line ((unsigned char *) line
, &keywd
, &arg
);
297 error (0, 0, _("%s:%lu: invalid line; missing second token"),
298 filename
, (long unsigned) line_number
);
305 if (strcasecmp (keywd
, "TERM") == 0)
307 if (STREQ (arg
, term
))
309 else if (state
!= ST_TERMSURE
)
314 if (state
== ST_TERMSURE
)
315 state
= ST_TERMYES
; /* Another TERM can cancel */
317 if (state
!= ST_TERMNO
)
322 append_quoted (keywd
);
327 else if (keywd
[0] == '*')
329 append_quoted (keywd
);
334 else if (strcasecmp (keywd
, "OPTIONS") == 0
335 || strcasecmp (keywd
, "COLOR") == 0
336 || strcasecmp (keywd
, "EIGHTBIT") == 0)
344 for (i
= 0; slack_codes
[i
] != NULL
; ++i
)
345 if (strcasecmp (keywd
, slack_codes
[i
]) == 0)
348 if (slack_codes
[i
] != NULL
)
350 APPEND_TWO_CHAR_STRING (ls_codes
[i
]);
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
);
384 dc_parse_file (const char *filename
)
389 if (STREQ (filename
, "-"))
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");
403 error (0, errno
, "%s", quote (filename
));
408 err
= dc_parse_stream (fp
, filename
);
410 if (fp
!= stdin
&& fclose (fp
) == EOF
)
412 error (0, errno
, "%s", quote (filename
));
420 main (int argc
, char **argv
)
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)
437 case 'b': /* Bourne shell syntax. */
438 syntax
= SHELL_SYNTAX_BOURNE
;
441 case 'c': /* C shell syntax. */
442 syntax
= SHELL_SYNTAX_C
;
449 case_GETOPT_HELP_CHAR
;
451 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
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
)
465 _("the options to output dircolors' internal database and\n\
466 to select a shell syntax are mutually exclusive"));
470 if (print_database
&& argc
> 0)
473 _("no FILE arguments may be used with the option to output\n\
474 dircolors' internal database"));
478 if (!print_database
&& argc
> 1)
480 error (0, 0, _("too many arguments"));
487 for (i
= 0; i
< G_N_LINES
; i
++)
489 fwrite (G_line
[i
], 1, G_line_length
[i
], stdout
);
490 fputc ('\n', stdout
);
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
);
508 err
= dc_parse_stream (NULL
, NULL
);
510 err
= dc_parse_file (argv
[0]);
514 size_t len
= obstack_object_size (&lsc_obstack
);
515 char *s
= obstack_finish (&lsc_obstack
);
519 if (syntax
== SHELL_SYNTAX_BOURNE
)
521 prefix
= "LS_COLORS='";
522 suffix
= "';\nexport LS_COLORS\n";
526 prefix
= "setenv LS_COLORS '";
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
);