1 /* dircolors - output commands to set the LS_COLOR environment variable
2 Copyright (C) 1996-2006 Free Software Foundation, Inc.
3 Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000 H. Peter Anvin
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
21 #include <sys/types.h>
26 #include "dircolors.h"
34 /* The official name of this program (e.g., no `g' prefix). */
35 #define PROGRAM_NAME "dircolors"
37 #define AUTHORS "H. Peter Anvin"
39 #define obstack_chunk_alloc malloc
40 #define obstack_chunk_free free
49 #define APPEND_CHAR(C) obstack_1grow (&lsc_obstack, C)
50 #define APPEND_TWO_CHAR_STRING(S) \
58 /* Accumulate in this obstack the value for the LS_COLORS environment
60 static struct obstack lsc_obstack
;
62 static const char *const slack_codes
[] =
64 "NORMAL", "NORM", "FILE", "DIR", "LNK", "LINK",
65 "SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK",
66 "CHR", "CHAR", "DOOR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE",
67 "END", "ENDCODE", "SUID", "SETUID", "SGID", "SETGID", "STICKY",
68 "OTHER_WRITABLE", "OWR", "STICKY_OTHER_WRITABLE", "OWT", NULL
71 static const char *const ls_codes
[] =
73 "no", "no", "fi", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
74 "so", "bd", "bd", "cd", "cd", "do", "ex", "lc", "lc", "rc", "rc", "ec", "ec",
75 "su", "su", "sg", "sg", "st", "ow", "ow", "tw", "tw", NULL
77 #define array_len(Array) (sizeof (Array) / sizeof *(Array))
78 verify (array_len (slack_codes
) == array_len (ls_codes
));
80 static struct option
const long_options
[] =
82 {"bourne-shell", no_argument
, NULL
, 'b'},
83 {"sh", no_argument
, NULL
, 'b'},
84 {"csh", no_argument
, NULL
, 'c'},
85 {"c-shell", no_argument
, NULL
, 'c'},
86 {"print-database", no_argument
, NULL
, 'p'},
87 {GETOPT_HELP_OPTION_DECL
},
88 {GETOPT_VERSION_OPTION_DECL
},
97 if (status
!= EXIT_SUCCESS
)
98 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
102 printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name
);
104 Output commands to set the LS_COLORS environment variable.\n\
106 Determine format of output:\n\
107 -b, --sh, --bourne-shell output Bourne shell code to set LS_COLORS\n\
108 -c, --csh, --c-shell output C shell code to set LS_COLORS\n\
109 -p, --print-database output defaults\n\
111 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
112 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
115 If FILE is specified, read it to determine which colors to use for which\n\
116 file types and extensions. Otherwise, a precompiled database is used.\n\
117 For details on the format of these files, run `dircolors --print-database'.\n\
119 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT
);
125 /* If the SHELL environment variable is set to `csh' or `tcsh,'
126 assume C shell. Else Bourne shell. */
128 static enum Shell_syntax
129 guess_shell_syntax (void)
133 shell
= getenv ("SHELL");
134 if (shell
== NULL
|| *shell
== '\0')
135 return SHELL_SYNTAX_UNKNOWN
;
137 shell
= last_component (shell
);
139 if (STREQ (shell
, "csh") || STREQ (shell
, "tcsh"))
140 return SHELL_SYNTAX_C
;
142 return SHELL_SYNTAX_BOURNE
;
146 parse_line (char const *line
, char **keyword
, char **arg
)
149 char const *keyword_start
;
150 char const *arg_start
;
155 for (p
= line
; isspace (to_uchar (*p
)); ++p
)
158 /* Ignore blank lines and shell-style comments. */
159 if (*p
== '\0' || *p
== '#')
164 while (!isspace (to_uchar (*p
)) && *p
!= '\0')
169 *keyword
= xstrndup (keyword_start
, p
- keyword_start
);
177 while (isspace (to_uchar (*p
)));
179 if (*p
== '\0' || *p
== '#')
184 while (*p
!= '\0' && *p
!= '#')
187 for (--p
; isspace (to_uchar (*p
)); --p
)
191 *arg
= xstrndup (arg_start
, p
- arg_start
);
194 /* FIXME: Write a string to standard out, while watching for "dangerous"
195 sequences like unescaped : and = characters. */
198 append_quoted (const char *str
)
200 bool need_backslash
= true;
210 need_backslash
= true;
215 need_backslash
= !need_backslash
;
225 need_backslash
= true;
234 /* Read the file open on FP (with name FILENAME). First, look for a
235 `TERM name' directive where name matches the current terminal type.
236 Once found, translate and accumulate the associated directives onto
237 the global obstack LSC_OBSTACK. Give a diagnostic
238 upon failure (unrecognized keyword is the only way to fail here).
239 Return true if successful. */
242 dc_parse_stream (FILE *fp
, const char *filename
)
244 size_t line_number
= 0;
245 char const *next_G_line
= G_line
;
246 char *input_line
= NULL
;
247 size_t input_line_size
= 0;
252 /* State for the parser. */
253 enum { ST_TERMNO
, ST_TERMYES
, ST_TERMSURE
, ST_GLOBAL
} state
= ST_GLOBAL
;
255 /* Get terminal type */
256 term
= getenv ("TERM");
257 if (term
== NULL
|| *term
== '\0')
269 if (getline (&input_line
, &input_line_size
, fp
) <= 0)
278 if (next_G_line
== G_line
+ sizeof G_line
)
281 next_G_line
+= strlen (next_G_line
) + 1;
284 parse_line (line
, &keywd
, &arg
);
291 error (0, 0, _("%s:%lu: invalid line; missing second token"),
292 filename
, (unsigned long int) line_number
);
298 unrecognized
= false;
299 if (strcasecmp (keywd
, "TERM") == 0)
301 if (STREQ (arg
, term
))
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
? quote (filename
) : _("<internal>")),
365 (unsigned long int) line_number
, keywd
);
377 dc_parse_file (const char *filename
)
381 if (! STREQ (filename
, "-") && freopen (filename
, "r", stdin
) == NULL
)
383 error (0, errno
, "%s", filename
);
387 ok
= dc_parse_stream (stdin
, filename
);
389 if (fclose (stdin
) != 0)
391 error (0, errno
, "%s", quote (filename
));
399 main (int argc
, char **argv
)
403 enum Shell_syntax syntax
= SHELL_SYNTAX_UNKNOWN
;
404 bool print_database
= false;
406 initialize_main (&argc
, &argv
);
407 program_name
= argv
[0];
408 setlocale (LC_ALL
, "");
409 bindtextdomain (PACKAGE
, LOCALEDIR
);
410 textdomain (PACKAGE
);
412 atexit (close_stdout
);
414 while ((optc
= getopt_long (argc
, argv
, "bcp", long_options
, NULL
)) != -1)
417 case 'b': /* Bourne shell syntax. */
418 syntax
= SHELL_SYNTAX_BOURNE
;
421 case 'c': /* C shell syntax. */
422 syntax
= SHELL_SYNTAX_C
;
426 print_database
= true;
429 case_GETOPT_HELP_CHAR
;
431 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
434 usage (EXIT_FAILURE
);
440 /* It doesn't make sense to use --print with either of
441 --bourne or --c-shell. */
442 if (print_database
&& syntax
!= SHELL_SYNTAX_UNKNOWN
)
445 _("the options to output dircolors' internal database and\n\
446 to select a shell syntax are mutually exclusive"));
447 usage (EXIT_FAILURE
);
450 if (!print_database
< argc
)
452 error (0, 0, _("extra operand %s"), quote (argv
[!print_database
]));
454 fprintf (stderr
, "%s\n",
455 _("File operands cannot be combined with "
456 "--print-database (-p)."));
457 usage (EXIT_FAILURE
);
462 char const *p
= G_line
;
463 while (p
< G_line
+ sizeof G_line
)
471 /* If shell syntax was not explicitly specified, try to guess it. */
472 if (syntax
== SHELL_SYNTAX_UNKNOWN
)
474 syntax
= guess_shell_syntax ();
475 if (syntax
== SHELL_SYNTAX_UNKNOWN
)
477 error (EXIT_FAILURE
, 0,
478 _("no SHELL environment variable, and no shell type option given"));
482 obstack_init (&lsc_obstack
);
484 ok
= dc_parse_stream (NULL
, NULL
);
486 ok
= dc_parse_file (argv
[0]);
490 size_t len
= obstack_object_size (&lsc_obstack
);
491 char *s
= obstack_finish (&lsc_obstack
);
495 if (syntax
== SHELL_SYNTAX_BOURNE
)
497 prefix
= "LS_COLORS='";
498 suffix
= "';\nexport LS_COLORS\n";
502 prefix
= "setenv LS_COLORS '";
505 fputs (prefix
, stdout
);
506 fwrite (s
, 1, len
, stdout
);
507 fputs (suffix
, stdout
);
511 exit (ok
? EXIT_SUCCESS
: EXIT_FAILURE
);