diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/dircolors.c | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/src/dircolors.c b/src/dircolors.c new file mode 100644 index 000000000..ce5441308 --- /dev/null +++ b/src/dircolors.c @@ -0,0 +1,563 @@ +/* dircolors - parse a Slackware-style DIR_COLORS file. + Copyright (C) 1994, 1995 H. Peter Anvin + Copyright (C) 1996 Free Software Foundation, Inc. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <ctype.h> +#include <getopt.h> +#include <stdio.h> + +#include "system.h" +#include "error.h" + +#define USER_FILE ".dir_colors" /* Versus user's home directory */ +#define SYSTEM_FILE "/DIR_COLORS" /* System-wide file in directory SYSTEM_DIR + (defined on the cc command line). */ + +#define STRINGLEN 2048 /* Max length of a string */ + +enum modes { mo_sh, mo_csh, mo_ksh, mo_zsh, mo_unknown, mo_err }; + +const char *shells[] = { "sh", "ash", "csh", "tcsh", "bash", "ksh", + "zsh", NULL }; + +const int shell_mode[] = { mo_sh, mo_sh, mo_csh, mo_csh, + mo_ksh, mo_ksh, mo_zsh }; + +char *program_name; + + +static void usage __P ((int status)); + + +static int +figure_mode (void) +{ + char *shell, *shellv; + int i; + + shellv = getenv ("SHELL"); + if (shellv == NULL || *shellv == '\0') + error (1, 0, _("\ +No SHELL variable, and no mode option specified")); + + shell = strrchr (shellv, '/'); + if (shell != NULL) + ++shell; + else + shell = shellv; + + for (i = 0; shells[i]; ++i) + if (strcmp (shell, shells[i]) == 0) + return shell_mode[i]; + + error (1, 0, _("Unknown shell `%s'\n"), shell); + /* NOTREACHED */ +} + +static void +parse_line (char **keyword, char **arg, char *line) +{ + char *p; + + *keyword = *arg = ""; + + for (p = line; isspace (*p); ++p) + ; + + if (*p == '\0' || *p == '#') + return; + + *keyword = p; + + while (!isspace (*p)) + if (*p++ == '\0') + return; + + *p++ = '\0'; + + while (isspace (*p)) + ++p; + + if (*p == '\0' || *p == '#') + return; + + *arg = p; + + while (*p != '\0' && *p != '#') + ++p; + for (--p; isspace (*p); --p) + ; + ++p; + + *p = '\0'; +} + +/* Write a string to standard out, while watching for "dangerous" + sequences like unescaped : and = characters. */ + +static void +put_seq (char *str, char follow) +{ + int danger = 1; + + while (*str != '\0') + { + switch (*str) + { + case '\\': + case '^': + danger = !danger; + break; + + case ':': + case '=': + if (danger) + putchar ('\\'); + /* Fall through */ + + default: + danger = 1; + break; + } + + putchar (*str++); + } + + putchar (follow); /* The character that ends the sequence. */ +} + +/* Parser needs these state variables. */ +enum states { st_termno, st_termyes, st_termsure, st_global }; + +const char *slack_codes[] = {"NORMAL", "NORM", "FILE", "DIR", "LNK", "LINK", +"SYMLINK", "ORPHAN", "MISSING", "FIFO", "PIPE", "SOCK", "BLK", "BLOCK", +"CHR", "CHAR", "EXEC", "LEFT", "LEFTCODE", "RIGHT", "RIGHTCODE", "END", +"ENDCODE", NULL}; + +const char *ls_codes[] = {"no", "no", "fi", "di", "ln", "ln", "ln", +"or", "mi", "pi", "pi", "so", "bd", "bd", "cd", "cd", "ex", "lc", "lc", "rc", +"rc", "ec", "ec"}; + +enum color_opts { col_yes, col_no, col_tty }; + + +static struct option long_options[] = + { + {"ash", no_argument, NULL, 'a'}, + {"bash", no_argument, NULL, 'b'}, + {"csh", no_argument, NULL, 'c'}, + {"help", no_argument, NULL, 'h'}, + {"no-path", no_argument, NULL, 'P'}, + {"sh", no_argument, NULL, 's'}, + {"tcsh", no_argument, NULL, 't'}, + {"version", no_argument, NULL, 'v'}, + {"zsh", no_argument, NULL, 'z'}, + }; + + +int +main (int argc, char *argv[]) +{ + char *p, *q; + char *file = NULL; + int optc; + int mode = mo_unknown; + FILE *fp = NULL; + char *term; + int state; + + char line[STRINGLEN]; + char useropts[2048] = ""; + char *keywd, *arg; + + int color_opt = col_no; /* Assume --color=no */ + + int no_path = 0; /* Do not search PATH */ + int do_help = 0; + int do_version = 0; + + char *copt; + char *input_file; + + program_name = argv[0]; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + /* Parse command line. */ + + while ((optc = getopt_long (argc, argv, "abhckPstz", long_options, NULL)) + != EOF) + switch (optc) + { + case 'a': + case 's': /* Plain sh mode */ + mode = mo_sh; + break; + + case 'c': + case 't': + mode = mo_csh; + break; + + case 'b': + case 'k': + mode = mo_ksh; + break; + + case 'h': + do_help = 1; + break; + + case 'z': + mode = mo_zsh; + break; + + case 'P': + no_path = 1; + break; + + case 'v': + do_version = 1; + break; + + default: + usage (1); + } + + /* If version information is wanted show it. */ + if (do_version) + { + printf ("%s - %s\n", program_name, PACKAGE_VERSION); + exit (0); + } + + /* If help is wanted show the screen. */ + if (do_help) + usage (0); + + /* Use shell to determine mode, if not already done. */ + if (mode == mo_unknown) + mode = figure_mode (); + + /* Open dir_colors file */ + if (optind == argc) + { + p = getenv ("HOME"); + if (p != NULL && *p != '\0') + { + chdir (p); + input_file = USER_FILE; + fp = fopen (input_file, "r"); + } + + if (fp == NULL) + { + input_file = SHAREDIR SYSTEM_FILE; + fp = fopen (input_file, "r"); + } + } + else + { + input_file = argv[optind]; + fp = fopen (input_file, "r"); + } + + if (fp == NULL) + error (1, errno, _("while opening input file `%s'"), input_file); + + /* Get terminal type */ + term = getenv ("TERM"); + if (term == NULL || *term == '\0') + term = "none"; + + /* Write out common start */ + switch (mode) + { + case mo_csh: + puts ("set noglob;\n\ +setenv LS_COLORS \':"); + break; + case mo_sh: + case mo_ksh: + case mo_zsh: + puts ("LS_COLORS=\'"); + break; + } + + /* Start parsing that sucker */ + state = st_global; + + while (fgets (line, STRINGLEN, fp) != NULL ) + { + parse_line (&keywd, &arg, line); + if (*keywd != '\0') + { + if (strcasecmp (keywd, "TERM") == 0) + { + if (strcmp (arg, term) == 0) + state = st_termsure; + else if (state != st_termsure) + state = st_termno; + } + else + { + if (state == st_termsure) + state = st_termyes; /* Another TERM can cancel */ + + if (state != st_termno) + { + if (keywd[0] == '.') + { + putchar ('*'); + put_seq (keywd, '='); + put_seq (arg, ':'); + } + else if (keywd[0] == '*') + { + put_seq (keywd, '='); + put_seq (arg, ':'); + } + else if (strcasecmp(keywd, "OPTIONS") == 0) + { + strcat (useropts, " "); + strcat (useropts, arg); + } + else if (strcasecmp(keywd, "COLOR") == 0) + { + switch (arg[0]) + { + case 'a': + case 'y': + case '1': + color_opt = col_yes; + break; + + case 'n': + case '0': + color_opt = col_no; + break; + + case 't': + color_opt = col_tty; + break; + + default: + error (0, 0, _("Unknown COLOR option `%s'\n"), arg); + break; + } + } + else + { + int i; + + for (i = 0; slack_codes[i] != NULL; ++i) + if (strcasecmp (keywd, slack_codes[i]) == 0) + break; + + if (slack_codes[i] != NULL) + { + printf ("%s=", ls_codes[i]); + put_seq (arg, ':'); + } + else + error (0, 0, _("Unknown keyword %s\n"), keywd); + } + } + } + } + } + + fclose (fp); + + /* Decide on the options. */ + switch (color_opt) + { + case col_yes: + copt = "--color=yes"; + break; + + case col_no: + copt = "--color=no"; + break; + + case col_tty: + copt = "--color=tty"; + break; + } + + /* Find ls in the path. */ + if (no_path == NULL) + { + no_path = 1; /* Assume we won't find one. */ + + p = getenv ("PATH"); + if (p != NULL && *p != '\0') + { + while (*p != '\0') + { + while (*p == ':') + ++p; + + if (*p != '/') /* Skip relative path entries. */ + while (*p != '\0' && *p != ':') + ++p; + else + { + q = line; + while (*p != '\0' && *p != ':') + *q++ = *p++; + /* Make sure it ends in slash. */ + if (*(q-1) != '/' ) + *q++ = '/'; + + strcpy (q, "ls"); + if (access (line, X_OK) == 0) + { + no_path = 0; /* Found it. */ + break; + } + } + } + } + } + + /* Write it out. */ + switch (mode) + { + case mo_sh: + if (no_path) + printf ("\';\n\ +export LS_COLORS;\n\ +LS_OPTIONS='%s%s';\n\ +export LS_OPTIONS;\n\ +ls () { ( exec ls $LS_OPTIONS \"$@\" ) };\n\ +dir () { ( exec dir $LS_OPTIONS \"$@\" ) };\n\ +vdir () { ( exec vdir $LS_OPTIONS \"$@\" ) };\n\ +d () { dir \"$@\" ; };\n\ +v () { vdir \"$@\" ; };\n", copt, useropts); + else + printf ("\';\n\ +export LS_COLORS;\n\ +LS_OPTIONS='%s%s';\n\ +ls () { %s $LS_OPTIONS \"$@\" ; };\n\ +dir () { %s $LS_OPTIONS --format=vertical \"$@\" ; };\n\ +vdir () { %s $LS_OPTIONS --format=long \"$@\" ; };\n\ +d () { dir \"$@\" ; };\n\ +v () { vdir \"$@\" ; };\n", copt, useropts, line, line, line); + break; + + case mo_csh: + if (no_path) + printf ("\';\n\ +setenv LS_OPTIONS '%s%s';\n\ +alias ls \'ls $LS_OPTIONS\';\n\ +alias dir \'dir $LS_OPTIONS\';\n\ +alias vdir \'vdir $LS_OPTIONS\';\n\ +alias d dir;\n\ +alias v vdir;\n\ +unset noglob;\n", copt, useropts); + else + printf ("\';\n\ +setenv LS_OPTIONS '%s%s';\n\ +alias ls \'%s $LS_OPTIONS\';\n\ +alias dir \'%s $LS_OPTIONS --format=vertical\';\n\ +alias vdir \'%s $LS_OPTIONS --format=long\';\n\ +alias d dir;\n\ +alias v vdir;\n\ +unset noglob;\n", copt, useropts, line, line, line); + break; + + case mo_ksh: + if (no_path) + printf ("\';\n\ +export LS_COLORS;\n\ +LS_OPTIONS='%s%s';\n\ +export LS_OPTIONS;\n\ +alias ls=\'ls $LS_OPTIONS\';\n\ +alias dir=\'dir $LS_OPTIONS\';\n\ +alias vdir=\'vdir $LS_OPTIONS\';\n\ +alias d=dir;\n\ +alias v=vdir;\n", copt, useropts); + else + printf ("\';\n\ +export LS_COLORS;\n\ +LS_OPTIONS='%s%s';\n\ +export LS_OPTIONS;\n\ +alias ls=\'%s $LS_OPTIONS\';\n\ +alias dir=\'%s $LS_OPTIONS --format=vertical\';\n\ +alias vdir=\'%s $LS_OPTIONS --format=long\';\n\ +alias d=dir;\n\ +alias v=vdir;\n", copt, useropts, line, line, line); + break; + + case mo_zsh: + if (no_path) + printf ("\';\n\ +export LS_COLORS;\n\ +LS_OPTIONS=(%s%s);\n\ +export LS_OPTIONS;\n\ +alias ls=\'ls $LS_OPTIONS\';\n\ +alias dir=\'dir $LS_OPTIONS\';\n\ +alias vdir=\'vdir $LS_OPTIONS\';\n\ +alias d=dir;\n\ +alias v=vdir;\n", copt, useropts); + else + printf ("\';\n\ +export LS_COLORS;\n\ +LS_OPTIONS=(%s%s);\n\ +export LS_OPTIONS;\n\ +alias ls=\'%s $LS_OPTIONS\';\n\ +alias dir=\'%s $LS_OPTIONS --format=vertical\';\n\ +alias vdir=\'%s $LS_OPTIONS --format=long\';\n\ +alias d=dir;\n\ +alias v=vdir;\n", copt, useropts, line, line, line); + break; + } + + exit (0); +} + + +static void +usage (int status) +{ + if (status != 0) + fprintf (stderr, _("Try `%s --help' for more information.\n"), + program_name); + else + { + printf (_("Usage: %s [OPTION]... [FILE]\n"), program_name); + printf (_("\ + -h, --help display this help and exit\n\ + -P, --no-path do not look for shell in PATH\n\ + --version output version information and exit\n\ +Determine format of output:\n\ + -a, --ash assume ash shell\n\ + -b, --bash assume bash shell\n\ + -c, --csh assume csh shell\n\ + -s, --sh assume Bourne shell\n\ + -t, --tcsh assume tcsh shell\n\ + -z, --zsh assume zsh shell\n")); + } + + exit (status); +} |