.
[coreutils.git] / src / dircolors.c
blob9a1a3337dc8cf1c0ab9cb7897ee69662f67ebd6a
1 /* FIXME: accept, but ignore EIGHTBIT option
2 FIXME: embed contents of default mapping file
3 FIXME: add option to print that default mapping?
4 */
5 /* dircolors - parse a Slackware-style DIR_COLORS file.
6 Copyright (C) 1994, 1995 H. Peter Anvin
7 Copyright (C) 1996 Free Software Foundation, Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2, or (at your option)
12 any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
27 #include <ctype.h>
28 #include <getopt.h>
29 #include <stdio.h>
31 #include "system.h"
32 #include "error.h"
34 char *xmalloc ();
36 #define USER_FILE ".dir_colors" /* Versus user's home directory */
37 #define SYSTEM_FILE "DIR_COLORS" /* System-wide file in directory SYSTEM_DIR
38 (defined on the cc command line). */
40 #define STRINGLEN 2048 /* Max length of a string */
42 enum modes { MO_SH, MO_CSH, MO_KSH, MO_ZSH, MO_UNKNOWN, MO_ERR };
44 /* FIXME: associate these arrays? */
45 static const char *const shells[] =
46 { "sh", "ash", "csh", "tcsh", "bash", "ksh", "zsh", NULL };
48 static const int shell_mode[] =
49 { MO_SH, MO_SH, MO_CSH, MO_CSH, MO_KSH, MO_KSH, MO_ZSH };
51 /* Parser needs these state variables. */
52 enum states { ST_TERMNO, ST_TERMYES, ST_TERMSURE, ST_GLOBAL };
54 /* FIXME: associate with ls_codes? */
55 static const char *const slack_codes[] =
57 "NORMAL", "NORM", "FILE", "DIR", "LNK", "LINK",
58 "SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK",
59 "CHR", "CHAR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE", "END",
60 "ENDCODE", NULL
63 static const char *const ls_codes[] =
65 "no", "no", "fi", "di", "ln", "ln", "ln", "or", "mi", "pi", "pi",
66 "so", "bd", "bd", "cd", "cd", "ex", "lc", "lc", "rc", "rc", "ec", "ec"
69 enum color_opts { col_yes, col_no, col_tty };
71 static struct option const long_options[] =
73 {"ash", no_argument, NULL, 'a'},
74 {"bash", no_argument, NULL, 'b'},
75 {"csh", no_argument, NULL, 'c'},
76 {"help", no_argument, NULL, 'h'},
77 {"no-path", no_argument, NULL, 'P'},
78 {"sh", no_argument, NULL, 's'},
79 {"tcsh", no_argument, NULL, 't'},
80 {"version", no_argument, NULL, 'v'},
81 {"zsh", no_argument, NULL, 'z'},
84 char *program_name;
86 static void
87 usage (int status)
89 if (status != 0)
90 fprintf (stderr, _("Try `%s --help' for more information.\n"),
91 program_name);
92 else
94 printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name);
95 printf (_("\
96 -h, --help display this help and exit\n\
97 -P, --no-path do not look for shell in PATH\n\
98 --version output version information and exit\n\
99 Determine format of output:\n\
100 -a, --ash assume ash shell\n\
101 -b, --bash assume bash shell\n\
102 -c, --csh assume csh shell\n\
103 -s, --sh assume Bourne shell\n\
104 -t, --tcsh assume tcsh shell\n\
105 -z, --zsh assume zsh shell\n"));
108 exit (status);
111 static int
112 figure_mode (void)
114 char *shell, *shellv;
115 int i;
117 shellv = getenv ("SHELL");
118 if (shellv == NULL || *shellv == '\0')
119 error (1, 0, _("\
120 No SHELL variable, and no mode option specified"));
122 shell = strrchr (shellv, '/');
123 if (shell != NULL)
124 ++shell;
125 else
126 shell = shellv;
128 for (i = 0; shells[i]; ++i)
129 if (strcmp (shell, shells[i]) == 0)
130 return shell_mode[i];
132 error (1, 0, _("Unknown shell `%s'\n"), shell);
133 /* NOTREACHED */
136 static void
137 parse_line (char **keyword, char **arg, char *line)
139 char *p;
141 *keyword = *arg = "";
143 for (p = line; isspace (*p); ++p)
146 if (*p == '\0' || *p == '#')
147 return;
149 *keyword = p;
151 while (!isspace (*p))
152 if (*p++ == '\0')
153 return;
155 *p++ = '\0';
157 while (isspace (*p))
158 ++p;
160 if (*p == '\0' || *p == '#')
161 return;
163 *arg = p;
165 while (*p != '\0' && *p != '#')
166 ++p;
167 for (--p; isspace (*p); --p)
169 ++p;
171 *p = '\0';
174 /* Write a string to standard out, while watching for "dangerous"
175 sequences like unescaped : and = characters. */
177 static void
178 put_seq (const char *str, char follow)
180 int danger = 1;
182 while (*str != '\0')
184 switch (*str)
186 case '\\':
187 case '^':
188 danger = !danger;
189 break;
191 case ':':
192 case '=':
193 if (danger)
194 putchar ('\\');
195 /* Fall through */
197 default:
198 danger = 1;
199 break;
202 putchar (*str++);
205 putchar (follow); /* The character that ends the sequence. */
209 main (int argc, char *argv[])
211 char *p, *q;
212 int optc;
213 int mode = MO_UNKNOWN;
214 FILE *fp = NULL;
215 char *term;
216 int state;
218 char line[STRINGLEN];
219 char useropts[2048] = "";
220 char *keywd, *arg;
222 int color_opt = col_no; /* Assume --color=no */
224 int no_path = 0; /* Do not search PATH */
225 int do_help = 0;
226 int do_version = 0;
228 const char *copt;
229 char *input_file;
231 program_name = argv[0];
232 setlocale (LC_ALL, "");
233 bindtextdomain (PACKAGE, LOCALEDIR);
234 textdomain (PACKAGE);
236 /* Parse command line. */
238 while ((optc = getopt_long (argc, argv, "abhckPstz", long_options, NULL))
239 != EOF)
240 switch (optc)
242 case 'a':
243 case 's': /* Plain sh mode */
244 mode = MO_SH;
245 break;
247 case 'c':
248 case 't':
249 mode = MO_CSH;
250 break;
252 case 'b':
253 case 'k':
254 mode = MO_KSH;
255 break;
257 case 'h':
258 do_help = 1;
259 break;
261 case 'z':
262 mode = MO_ZSH;
263 break;
265 case 'P':
266 no_path = 1;
267 break;
269 case 'v':
270 do_version = 1;
271 break;
273 default:
274 usage (1);
277 if (do_version)
279 printf ("%s - %s\n", program_name, PACKAGE_VERSION);
280 exit (0);
283 if (do_help)
284 usage (0);
286 /* Use shell to determine mode, if not already done. */
287 if (mode == MO_UNKNOWN)
288 mode = figure_mode ();
290 /* Open dir_colors file */
291 if (optind == argc)
293 p = getenv ("HOME");
294 if (p != NULL && *p != '\0')
296 /* Note: deliberate leak. It's not worth freeing this. */
297 input_file = xmalloc (strlen (p) + 1
298 + strlen (USER_FILE) + 1);
299 stpcpy (stpcpy (stpcpy (input_file, p), "/"), USER_FILE);
300 fp = fopen (input_file, "r");
303 if (fp == NULL)
305 /* Note: deliberate leak. It's not worth freeing this. */
306 input_file = xmalloc (strlen (SHAREDIR) + 1
307 + strlen (USER_FILE) + 1);
308 stpcpy (stpcpy (stpcpy (input_file, SHAREDIR), "/"),
309 SYSTEM_FILE);
310 fp = fopen (input_file, "r");
313 else
315 input_file = argv[optind];
316 fp = fopen (input_file, "r");
319 if (fp == NULL)
320 error (1, errno, _("while opening input file `%s'"), input_file);
322 /* Get terminal type */
323 term = getenv ("TERM");
324 if (term == NULL || *term == '\0')
325 term = "none";
327 /* Write out common start */
328 switch (mode)
330 case MO_CSH:
331 puts ("set noglob;\n\
332 setenv LS_COLORS \':");
333 break;
334 case MO_SH:
335 case MO_KSH:
336 case MO_ZSH:
337 fputs ("LS_COLORS=\'", stdout);
338 break;
341 state = ST_GLOBAL;
343 /* FIXME: use getline */
344 while (fgets (line, STRINGLEN, fp) != NULL)
346 parse_line (&keywd, &arg, line);
347 if (*keywd != '\0')
349 if (strcasecmp (keywd, "TERM") == 0)
351 if (strcmp (arg, term) == 0)
352 state = ST_TERMSURE;
353 else if (state != ST_TERMSURE)
354 state = ST_TERMNO;
356 else
358 if (state == ST_TERMSURE)
359 state = ST_TERMYES; /* Another TERM can cancel */
361 if (state != ST_TERMNO)
363 if (keywd[0] == '.')
365 putchar ('*');
366 put_seq (keywd, '=');
367 put_seq (arg, ':');
369 else if (keywd[0] == '*')
371 put_seq (keywd, '=');
372 put_seq (arg, ':');
374 else if (strcasecmp (keywd, "OPTIONS") == 0)
376 strcat (useropts, " ");
377 strcat (useropts, arg);
379 else if (strcasecmp (keywd, "COLOR") == 0)
381 switch (arg[0])
383 case 'a':
384 case 'y':
385 case '1':
386 color_opt = col_yes;
387 break;
389 case 'n':
390 case '0':
391 color_opt = col_no;
392 break;
394 case 't':
395 color_opt = col_tty;
396 break;
398 default:
399 error (0, 0, _("Unknown COLOR option `%s'\n"), arg);
400 break;
403 else
405 int i;
407 for (i = 0; slack_codes[i] != NULL; ++i)
408 if (strcasecmp (keywd, slack_codes[i]) == 0)
409 break;
411 if (slack_codes[i] != NULL)
413 printf ("%s=", ls_codes[i]);
414 put_seq (arg, ':');
416 else
417 error (0, 0, _("Unknown keyword %s\n"), keywd);
424 fclose (fp);
426 /* Decide on the options. */
427 switch (color_opt)
429 case col_yes:
430 copt = "--color=yes";
431 break;
433 case col_no:
434 copt = "--color=no";
435 break;
437 case col_tty:
438 copt = "--color=tty";
439 break;
442 /* Find ls in the path. */
443 if (no_path == 0)
445 no_path = 1; /* Assume we won't find one. */
447 p = getenv ("PATH");
448 if (p != NULL && *p != '\0')
450 while (*p != '\0')
452 while (*p == ':')
453 ++p;
455 if (*p != '/') /* Skip relative path entries. */
456 while (*p != '\0' && *p != ':')
457 ++p;
458 else
460 q = line;
461 while (*p != '\0' && *p != ':')
462 *q++ = *p++;
463 /* Make sure it ends in slash. */
464 if (*(q - 1) != '/' )
465 *q++ = '/';
467 strcpy (q, "ls");
468 if (access (line, X_OK) == 0)
470 no_path = 0; /* Found it. */
471 break;
478 /* Write it out. */
479 switch (mode)
481 case MO_SH:
482 if (no_path)
483 printf ("\';\n\
484 export LS_COLORS;\n\
485 LS_OPTIONS='%s%s';\n\
486 export LS_OPTIONS;\n\
487 ls () { ( exec ls $LS_OPTIONS \"$@\" ) };\n\
488 dir () { ( exec dir $LS_OPTIONS \"$@\" ) };\n\
489 vdir () { ( exec vdir $LS_OPTIONS \"$@\" ) };\n\
490 d () { dir \"$@\" ; };\n\
491 v () { vdir \"$@\" ; };\n", copt, useropts);
492 else
493 printf ("\';\n\
494 export LS_COLORS;\n\
495 LS_OPTIONS='%s%s';\n\
496 ls () { %s $LS_OPTIONS \"$@\" ; };\n\
497 dir () { %s $LS_OPTIONS --format=vertical \"$@\" ; };\n\
498 vdir () { %s $LS_OPTIONS --format=long \"$@\" ; };\n\
499 d () { dir \"$@\" ; };\n\
500 v () { vdir \"$@\" ; };\n", copt, useropts, line, line, line);
501 break;
503 case MO_CSH:
504 if (no_path)
505 printf ("\';\n\
506 setenv LS_OPTIONS '%s%s';\n\
507 alias ls \'ls $LS_OPTIONS\';\n\
508 alias dir \'dir $LS_OPTIONS\';\n\
509 alias vdir \'vdir $LS_OPTIONS\';\n\
510 alias d dir;\n\
511 alias v vdir;\n\
512 unset noglob;\n", copt, useropts);
513 else
514 printf ("\';\n\
515 setenv LS_OPTIONS '%s%s';\n\
516 alias ls \'%s $LS_OPTIONS\';\n\
517 alias dir \'%s $LS_OPTIONS --format=vertical\';\n\
518 alias vdir \'%s $LS_OPTIONS --format=long\';\n\
519 alias d dir;\n\
520 alias v vdir;\n\
521 unset noglob;\n", copt, useropts, line, line, line);
522 break;
524 case MO_KSH:
525 if (no_path)
526 printf ("\';\n\
527 export LS_COLORS;\n\
528 LS_OPTIONS='%s%s';\n\
529 export LS_OPTIONS;\n\
530 alias ls=\'ls $LS_OPTIONS\';\n\
531 alias dir=\'dir $LS_OPTIONS\';\n\
532 alias vdir=\'vdir $LS_OPTIONS\';\n\
533 alias d=dir;\n\
534 alias v=vdir;\n", copt, useropts);
535 else
536 printf ("\';\n\
537 export LS_COLORS;\n\
538 LS_OPTIONS='%s%s';\n\
539 export LS_OPTIONS;\n\
540 alias ls=\'%s $LS_OPTIONS\';\n\
541 alias dir=\'%s $LS_OPTIONS --format=vertical\';\n\
542 alias vdir=\'%s $LS_OPTIONS --format=long\';\n\
543 alias d=dir;\n\
544 alias v=vdir;\n", copt, useropts, line, line, line);
545 break;
547 case MO_ZSH:
548 if (no_path)
549 printf ("\';\n\
550 export LS_COLORS;\n\
551 LS_OPTIONS=(%s%s);\n\
552 export LS_OPTIONS;\n\
553 alias ls=\'ls $LS_OPTIONS\';\n\
554 alias dir=\'dir $LS_OPTIONS\';\n\
555 alias vdir=\'vdir $LS_OPTIONS\';\n\
556 alias d=dir;\n\
557 alias v=vdir;\n", copt, useropts);
558 else
559 printf ("\';\n\
560 export LS_COLORS;\n\
561 LS_OPTIONS=(%s%s);\n\
562 export LS_OPTIONS;\n\
563 alias ls=\'%s $LS_OPTIONS\';\n\
564 alias dir=\'%s $LS_OPTIONS --format=vertical\';\n\
565 alias vdir=\'%s $LS_OPTIONS --format=long\';\n\
566 alias d=dir;\n\
567 alias v=vdir;\n", copt, useropts, line, line, line);
568 break;
571 exit (0);