/* `dir', `vdir' and `ls' directory listing programs for GNU.
- Copyright (C) 1985, 1988, 1990, 1991, 1995 Free Software Foundation, Inc.
+ Copyright (C) 85, 88, 90, 91, 95, 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
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
-\f
-/* If the macro MULTI_COL is defined,
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* If ls_mode is LS_MULTI_COL,
the multi-column format is the default regardless
of the type of output device.
This is for the `dir' program.
- If the macro LONG_FORMAT is defined,
+ If ls_mode is LS_LONG_FORMAT,
the long format is the default regardless of the
type of output device.
This is for the `vdir' program.
- If neither is defined,
+ If ls_mode is LS_LS,
the output format depends on whether the output
device is a terminal.
This is for the `ls' program. */
-/* Written by Richard Stallman and David MacKenzie. */
+/* Written by Richard Stallman and David MacKenzie. */
+
+/* Color support by Peter Anvin <Peter.Anvin@linux.org> and Dennis
+ Flaherty <dennisf@denix.elk.miles.com> based on original patches by
+ Greg Lee <lee@uhunix.uhcc.hawaii.edu>. */
#ifdef _AIX
#pragma alloca
#include <config.h>
#include <sys/types.h>
-#if HAVE_SYS_IOCTL_H
+#include <termios.h>
+#ifdef GWINSZ_IN_SYS_IOCTL
# include <sys/ioctl.h>
#endif
/* limits.h must come before system.h because limits.h on some systems
undefs PATH_MAX, whereas system.h includes pathmax.h which sets
PATH_MAX. */
-#include <limits.h>
+# include <limits.h>
#endif
#include "system.h"
#include "obstack.h"
#include "ls.h"
-#include "version.h"
#include "error.h"
+#include "argmatch.h"
+#include "xstrtol.h"
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
#ifndef INT_MAX
-#define INT_MAX 2147483647
+# define INT_MAX 2147483647
#endif
/* Return an int indicating the result of comparing two longs. */
#if (INT_MAX <= 65535)
-#define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b) ? 1 : 0)
+# define longdiff(a, b) ((a) < (b) ? -1 : (a) > (b) ? 1 : 0)
#else
-#define longdiff(a, b) ((a) - (b))
+# define longdiff(a, b) ((a) - (b))
#endif
/* The maximum number of digits required to print an inode number
in an unsigned format. */
#ifndef INODE_DIGITS
-#define INODE_DIGITS 7
+# define INODE_DIGITS 7
#endif
enum filetype
zero. */
unsigned int linkmode;
+ /* For symbolic link and color printing, 1 if linked-to file
+ exists, otherwise 0. */
+ int linkok;
+
enum filetype filetype;
};
+#define LEN_STR_PAIR(s) sizeof (s) - 1, s
+
+/* Null is a valid character in a color indicator (think about Epson
+ printers, for example) so we have to use a length/buffer string
+ type. */
+
+struct bin_str
+ {
+ unsigned int len; /* Number of bytes */
+ char *string; /* Pointer to the same */
+ };
+
#ifndef STDC_HEADERS
-char *ctime ();
time_t time ();
void free ();
#endif
char *getuser ();
char *xmalloc ();
char *xrealloc ();
-int argmatch ();
void invalid_arg ();
-static char *make_link_path __P ((char *path, char *linkname));
-static int compare_atime __P ((struct fileinfo *file1,
- struct fileinfo *file2));
-static int rev_cmp_atime __P ((struct fileinfo *file2,
- struct fileinfo *file1));
-static int compare_ctime __P ((struct fileinfo *file1,
- struct fileinfo *file2));
-static int rev_cmp_ctime __P ((struct fileinfo *file2,
- struct fileinfo *file1));
-static int compare_mtime __P ((struct fileinfo *file1,
- struct fileinfo *file2));
-static int rev_cmp_mtime __P ((struct fileinfo *file2,
- struct fileinfo *file1));
-static int compare_size __P ((struct fileinfo *file1,
- struct fileinfo *file2));
-static int rev_cmp_size __P ((struct fileinfo *file2,
- struct fileinfo *file1));
-static int compare_name __P ((struct fileinfo *file1,
- struct fileinfo *file2));
-static int rev_cmp_name __P ((struct fileinfo *file2,
- struct fileinfo *file1));
-static int compare_extension __P ((struct fileinfo *file1,
- struct fileinfo *file2));
-static int rev_cmp_extension __P ((struct fileinfo *file2,
- struct fileinfo *file1));
+static char *make_link_path __P ((const char *path, const char *linkname));
+static int compare_atime __P ((const struct fileinfo *file1,
+ const struct fileinfo *file2));
+static int rev_cmp_atime __P ((const struct fileinfo *file2,
+ const struct fileinfo *file1));
+static int compare_ctime __P ((const struct fileinfo *file1,
+ const struct fileinfo *file2));
+static int rev_cmp_ctime __P ((const struct fileinfo *file2,
+ const struct fileinfo *file1));
+static int compare_mtime __P ((const struct fileinfo *file1,
+ const struct fileinfo *file2));
+static int rev_cmp_mtime __P ((const struct fileinfo *file2,
+ const struct fileinfo *file1));
+static int compare_size __P ((const struct fileinfo *file1,
+ const struct fileinfo *file2));
+static int rev_cmp_size __P ((const struct fileinfo *file2,
+ const struct fileinfo *file1));
+static int compare_name __P ((const struct fileinfo *file1,
+ const struct fileinfo *file2));
+static int rev_cmp_name __P ((const struct fileinfo *file2,
+ const struct fileinfo *file1));
+static int compare_extension __P ((const struct fileinfo *file1,
+ const struct fileinfo *file2));
+static int rev_cmp_extension __P ((const struct fileinfo *file2,
+ const struct fileinfo *file1));
static int decode_switches __P ((int argc, char **argv));
-static void parse_ls_color __P ((void));
-static int file_interesting __P ((register struct dirent *next));
+static int file_interesting __P ((const struct dirent *next));
static int gobble_file __P ((const char *name, int explicit_arg,
const char *dirname));
-static int is_not_dot_or_dotdot __P ((char *name));
-static int length_of_file_name_and_frills __P ((struct fileinfo *f));
-static void add_ignore_pattern __P ((char *pattern));
+static int is_not_dot_or_dotdot __P ((const char *name));
+static void print_color_indicator __P ((const char *name, unsigned int mode,
+ int linkok));
+static void put_indicator __P ((const struct bin_str *ind));
+static int length_of_file_name_and_frills __P ((const struct fileinfo *f));
+static void add_ignore_pattern __P ((const char *pattern));
static void attach __P ((char *dest, const char *dirname, const char *name));
static void clear_files __P ((void));
static void extract_dirs_from_files __P ((const char *dirname, int recursive));
-static void get_link_name __P ((char *filename, struct fileinfo *f));
+static void get_link_name __P ((const char *filename, struct fileinfo *f));
static void indent __P ((int from, int to));
static void print_current_files __P ((void));
static void print_dir __P ((const char *name, const char *realname));
-static void print_file_name_and_frills __P ((struct fileinfo *f));
+static void print_file_name_and_frills __P ((const struct fileinfo *f));
static void print_horizontal __P ((void));
-static void print_long_format __P ((struct fileinfo *f));
+static void print_long_format __P ((const struct fileinfo *f));
static void print_many_per_line __P ((void));
-static void print_name_with_quoting __P ((register char *p));
+static void print_name_with_quoting __P ((const char *p, unsigned int mode,
+ int linkok));
+static void prep_non_filename_text __P ((void));
static void print_type_indicator __P ((unsigned int mode));
-static void print_color_indicator __P ((unsigned int mode));
-static void put_indicator __P ((int n));
static void print_with_commas __P ((void));
-static void queue_directory __P ((char *name, char *realname));
+static void queue_directory __P ((const char *name, const char *realname));
static void sort_files __P ((void));
+static void parse_ls_color __P ((void));
static void usage __P ((int status));
-\f
+
/* The name the program was run with, stripped of any leading path. */
char *program_name;
4, or more if needed for bigger numbers. */
static int block_size_size;
-\f
+
/* Option flags */
/* long_format for lots of info, one per line.
strange characters in file names. */
static int dired;
-/* none means don't mention the type of files.
- all means mention the types of all files.
- not_programs means do so except for executables.
+/* `none' means don't mention the type of files.
+ `all' means mention the types of all files.
+ `not_programs' means do so except for executables.
Controlled by -F and -p. */
enum indicator_style
{
- none, /* default */
- all, /* -F */
- not_programs /* -p */
+ none, /* default */
+ all, /* -F */
+ not_programs /* -p */
};
static enum indicator_style indicator_style;
/* Nonzero means use colors to mark types. Also define the different
colors as well as the stuff for the LS_COLORS environment variable.
- The LS_COLORS variable is now in a termcap-like format. -o or
- --color-if-tty. */
+ The LS_COLORS variable is now in a termcap-like format. */
static int print_with_color;
enum color_type
{
- color_no, /* 0: default or --color=no */
- color_yes, /* 1: -o or --color=yes */
+ color_never, /* 0: default or --color=never */
+ color_always, /* 1: --color=always */
color_if_tty /* 2: --color=tty */
};
-/* Note that color_no and color_yes equals boolean values; they will
- be assigned to print_with_color which is a boolean variable */
-
-#define MAXCOLORLEN 16 /* Max # of characters in a color sequence */
-
enum indicator_no
{
- C_LEFT, C_RIGHT, C_END, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK,
- C_BLK, C_CHR, C_EXEC
+ C_LEFT, C_RIGHT, C_END, C_NORM, C_FILE, C_DIR, C_LINK, C_FIFO, C_SOCK,
+ C_BLK, C_CHR, C_MISSING, C_ORPHAN, C_EXEC
};
-const char *const indicator_name[] =
-{
- "lc", "rc", "ec", "fi", "di", "ln", "pi", "so", "bd", "cd", "ex", NULL
-};
-
-struct indicator_type
+static const char *const indicator_name[]=
{
- int len;
- char string[MAXCOLORLEN];
+ "lc", "rc", "ec", "no", "fi", "di", "ln", "pi", "so",
+ "bd", "cd", "mi", "or", "ex", NULL
};
-#define LEN_STR_PAIR(s) sizeof (s) - 1, s
+struct col_ext_type
+ {
+ struct bin_str ext; /* The extension we're looking for */
+ struct bin_str seq; /* The sequence to output when we do */
+ struct col_ext_type *next; /* Next in list */
+ };
-static struct indicator_type color_indicator[] =
-{
- {LEN_STR_PAIR ("\033[")}, /* lc: Left of color sequence */
- {LEN_STR_PAIR ("m")}, /* rc: Right of color sequence */
- {LEN_STR_PAIR ("\033[0m")}, /* ec: End color */
- {LEN_STR_PAIR ("0")}, /* fi: File: default */
- {LEN_STR_PAIR ("32")}, /* di: Directory: green */
- {LEN_STR_PAIR ("36")}, /* ln: Symlink: cyan */
- {LEN_STR_PAIR ("31")}, /* pi: Pipe: red */
- {LEN_STR_PAIR ("33")}, /* so: Socket: yellow/brown */
- {LEN_STR_PAIR ("44;37")}, /* bd: Block device: white on blue */
- {LEN_STR_PAIR ("44;37")}, /* cd: Char device: white on blue */
- {LEN_STR_PAIR ("35")}, /* ex: Executable: purple */
-};
+static struct bin_str color_indicator[] =
+ {
+ { LEN_STR_PAIR ("\033[") }, /* lc: Left of color sequence */
+ { LEN_STR_PAIR ("m") }, /* rc: Right of color sequence */
+ { 0, NULL }, /* ec: End color (replaces lc+no+rc) */
+ { LEN_STR_PAIR ("0") }, /* no: Normal */
+ { LEN_STR_PAIR ("0") }, /* fi: File: default */
+ { LEN_STR_PAIR ("01;34") }, /* di: Directory: bright blue */
+ { LEN_STR_PAIR ("01;36") }, /* ln: Symlink: bright cyan */
+ { LEN_STR_PAIR ("33") }, /* pi: Pipe: yellow/brown */
+ { LEN_STR_PAIR ("01;35") }, /* so: Socket: bright magenta */
+ { LEN_STR_PAIR ("01;33") }, /* bd: Block device: bright yellow */
+ { LEN_STR_PAIR ("01;33") }, /* cd: Char device: bright yellow */
+ { 0, NULL }, /* mi: Missing file: undefined */
+ { 0, NULL }, /* or: Orphanned symlink: undefined */
+ { LEN_STR_PAIR ("01;32") } /* ex: Executable: bright green */
+ };
-/* Nonzero means print using ISO 8859 characters. The default is specified
- here as well. -8 enables, -7 disables. */
+/* FIXME: comment */
+struct col_ext_type *col_ext_list = NULL;
-static int print_iso8859;
-#ifndef DEFAULT_ISO8859
-#define DEFAULT_ISO8859 1
-#endif
+/* Buffer for color sequences */
+static char *color_buf;
/* Nonzero means mention the inode number of each file. -i */
struct ignore_pattern
{
- char *pattern;
+ const char *pattern;
struct ignore_pattern *next;
};
static int quote_as_string;
-/* The number of chars per hardware tab stop. -T */
+/* The number of chars per hardware tab stop. Setting this to zero
+ inhibits the use of TAB characters for separating columns. -T */
static int tabsize;
/* Nonzero means we are listing the working directory because no
static int exit_status;
-/* If non-zero, display usage information and exit. */
+/* If nonzero, display usage information and exit. */
static int show_help;
-/* If non-zero, print the version on standard output and exit. */
+/* If nonzero, print the version on standard output and exit. */
static int show_version;
static struct option const long_options[] =
{"time", required_argument, 0, 11},
{"help", no_argument, &show_help, 1},
{"version", no_argument, &show_version, 1},
- {"color", optional_argument, 0, 'o'},
- {"colour", optional_argument, 0, 'o'},
- {"7bit", no_argument, 0, '7'},
- {"8bit", no_argument, 0, '8'},
- {0, 0, 0, 0}
+ {"color", optional_argument, 0, 13},
+ {NULL, 0, NULL, 0}
};
static char const *const format_args[] =
};
static char const *const color_args[] =
-{
- /* Note: "no" is a prefix of "none" so we don't include it */
- "yes", "force", "none", "tty", "if-tty"
-};
+ {
+ /* Note: "no" is a prefix of "none" so we don't include it. */
+ /* force and none are for compatibility with another color-ls version */
+ "always", "yes", "force",
+ "never", "none",
+ "auto", "tty", "if-tty", 0
+ };
static enum color_type const color_types[] =
-{
- color_yes, color_yes, color_no, color_if_tty, color_if_tty
-};
-\f
+ {
+ color_always, color_always, color_always,
+ color_never, color_never,
+ color_if_tty, color_if_tty, color_if_tty
+ };
+
+
/* Write to standard output the string PREFIX followed by a space-separated
list of the integers stored in OS all on one line. */
}
}
-void
+int
main (int argc, char **argv)
{
register int i;
register struct pending *thispend;
+ program_name = argv[0];
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
exit_status = 0;
dir_defaulted = 1;
print_dir_name = 1;
pending_dirs = 0;
current_time = time ((time_t *) 0);
- program_name = argv[0];
i = decode_switches (argc, argv);
if (show_version)
{
- printf ("ls - %s\n", version_string);
- exit (0);
+ printf ("%s (%s) %s\n",
+ (ls_mode == LS_LS ? "ls"
+ : (ls_mode == LS_MULTI_COL ? "dir" : "vdir")),
+ GNU_PACKAGE, VERSION);
+ exit (EXIT_SUCCESS);
}
if (show_help)
- usage (0);
+ usage (EXIT_SUCCESS);
+
+ if (print_with_color)
+ {
+ parse_ls_color ();
+ prep_non_filename_text ();
+ }
format_needs_stat = sort_type == sort_time || sort_type == sort_size
|| format == long_format
dired_dump_obstack ("//SUBDIRED//", &subdired_obstack);
}
+ if (fclose (stdout) == EOF)
+ error (EXIT_FAILURE, errno, _("write error"));
+
+ /* Restore default color before exiting */
+ if (print_with_color)
+ {
+ put_indicator (&color_indicator[C_LEFT]);
+ put_indicator (&color_indicator[C_RIGHT]);
+ }
+
exit (exit_status);
}
-\f
+
/* Set all the option flags according to the switches specified.
Return the index of the first non-option argument. */
register char *p;
int c;
int i;
+ long int tmp_long;
qmark_funny_chars = 0;
quote_funny_chars = 0;
really_all_files = 0;
ignore_patterns = 0;
quote_as_string = 0;
- print_with_color = 0;
- print_iso8859 = DEFAULT_ISO8859;
- p = getenv ("COLUMNS");
- line_length = p ? atoi (p) : 80;
+ line_length = 80;
+ if ((p = getenv ("COLUMNS")))
+ {
+ if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK
+ && 0 < tmp_long && tmp_long <= INT_MAX)
+ {
+ line_length = (int) tmp_long;
+ }
+ else
+ {
+ error (0, 0,
+ _("ignoring invalid width in environment variable COLUMNS: %s"),
+ p);
+ }
+ }
#ifdef TIOCGWINSZ
{
}
#endif
- /* FIXME: reference TABSIZE iff !posix_pedantic. */
- p = getenv ("TABSIZE");
- /* FIXME: use strtol here! */
- tabsize = p ? atoi (p) : 8;
- if (tabsize < 1)
+ /* Using the TABSIZE environment variable is not POSIX-approved.
+ Ignore it when POSIXLY_CORRECT is set. */
+ tabsize = 8;
+ if (!getenv ("POSIXLY_CORRECT") && (p = getenv ("TABSIZE")))
{
- error (0, 0,
- "ignoring invalid tab size in enironment variable TABSIZE: %s",
- optarg);
- tabsize = 8;
+ if (xstrtol (p, NULL, 0, &tmp_long, NULL) == LONGINT_OK
+ && 0 <= tmp_long && tmp_long <= INT_MAX)
+ {
+ tabsize = (int) tmp_long;
+ }
+ else
+ {
+ error (0, 0,
+ _("ignoring invalid tab size in environment variable TABSIZE: %s"),
+ p);
+ }
}
while ((c = getopt_long (argc, argv,
- "abcdfgiklmnopqrstuw:xABCDFGI:LNQRST:UX178",
- long_options, (int *) 0)) != EOF)
+ "abcdfgiklmnopqrstuw:xABCDFGI:LNQRST:UX1",
+ long_options, NULL)) != -1)
{
switch (c)
{
if (format == long_format)
format = (isatty (1) ? many_per_line : one_per_line);
print_block_size = 0; /* disable -s */
+ print_with_color = 0; /* disable --color */
break;
case 'g':
numeric_users = 1;
break;
- case 'o':
- if (optarg)
- {
- i = argmatch (optarg, color_args);
- if (i < 0)
- {
- invalid_arg ("colorization criterion", optarg, i);
- usage (1);
- }
- i = color_types[i];
- }
- else
- i = color_yes; /* -o or --color -> do colorize */
-
- if (i == color_if_tty)
- print_with_color = isatty (1) ? 1 : 0;
- else
- print_with_color = i;
-
- if (print_with_color)
- {
- /* Don't use TAB characters in output. Some terminal
- emulators can't handle the combination of tabs and
- color codes on the same line. */
- tabsize = line_length;
- }
+ case 'o': /* Just like -l, but don't display group info. */
+ format = long_format;
+ inhibit_group = 1;
break;
case 'p':
break;
case 'w':
- line_length = atoi (optarg);
- if (line_length < 1)
- error (1, 0, "invalid line width: %s", optarg);
+ if (xstrtol (optarg, NULL, 0, &tmp_long, NULL) != LONGINT_OK
+ || tmp_long <= 0 || tmp_long > INT_MAX)
+ error (EXIT_FAILURE, 0, _("invalid line width: %s"), optarg);
+ line_length = (int) tmp_long;
break;
case 'x':
break;
case 'A':
+ really_all_files = 0;
all_files = 1;
break;
break;
case 'T':
- tabsize = atoi (optarg);
- if (tabsize < 1)
- error (1, 0, "invalid tab size: %s", optarg);
+ if (xstrtol (optarg, NULL, 0, &tmp_long, NULL) != LONGINT_OK
+ || tmp_long < 0 || tmp_long > INT_MAX)
+ error (EXIT_FAILURE, 0, _("invalid tab size: %s"), optarg);
+ tabsize = (int) tmp_long;
break;
case 'U':
format = one_per_line;
break;
- case '7':
- print_iso8859 = 0;
- break;
-
- case '8':
- print_iso8859 = 1;
- break;
-
case 10: /* --sort */
i = argmatch (optarg, sort_args);
if (i < 0)
{
- invalid_arg ("sort type", optarg, i);
- usage (1);
+ invalid_arg (_("sort type"), optarg, i);
+ usage (EXIT_FAILURE);
}
sort_type = sort_types[i];
break;
i = argmatch (optarg, time_args);
if (i < 0)
{
- invalid_arg ("time type", optarg, i);
- usage (1);
+ invalid_arg (_("time type"), optarg, i);
+ usage (EXIT_FAILURE);
}
time_type = time_types[i];
break;
i = argmatch (optarg, format_args);
if (i < 0)
{
- invalid_arg ("format type", optarg, i);
- usage (1);
+ invalid_arg (_("format type"), optarg, i);
+ usage (EXIT_FAILURE);
}
format = formats[i];
break;
+ case 13: /* --color */
+ if (optarg)
+ {
+ i = argmatch (optarg, color_args);
+ if (i < 0)
+ {
+ invalid_arg (_("colorization criterion"), optarg, i);
+ usage (EXIT_FAILURE);
+ }
+ i = color_types[i];
+ }
+ else
+ {
+ /* Using --color with no argument is equivalent to using
+ --color=always. */
+ i = color_always;
+ }
+
+ print_with_color = (i == color_always
+ || (i == color_if_tty
+ && isatty (STDOUT_FILENO)));
+
+ if (print_with_color)
+ {
+ /* Don't use TAB characters in output. Some terminal
+ emulators can't handle the combination of tabs and
+ color codes on the same line. */
+ tabsize = 0;
+ }
+ break;
+
default:
- usage (1);
+ usage (EXIT_FAILURE);
}
}
- if (print_with_color)
+ return optind;
+}
+
+/* Parse a string as part of the LS_COLORS variable; this may involve
+ decoding all kinds of escape characters. If equals_end is set an
+ unescaped equal sign ends the string, otherwise only a : or \0
+ does. Returns the number of characters output, or -1 on failure.
+
+ The resulting string is *not* null-terminated, but may contain
+ embedded nulls.
+
+ Note that both dest and src are char **; on return they point to
+ the first free byte after the array and the character that ended
+ the input string, respectively. */
+
+static int
+get_funky_string (char **dest, const char **src, int equals_end)
+{
+ int num; /* For numerical codes */
+ int count; /* Something to count with */
+ enum {
+ ST_GND, ST_BACKSLASH, ST_OCTAL, ST_HEX, ST_CARET, ST_END, ST_ERROR
+ } state;
+ const char *p;
+ char *q;
+
+ p = *src; /* We don't want to double-indirect */
+ q = *dest; /* the whole darn time. */
+
+ count = 0; /* No characters counted in yet. */
+ num = 0;
+
+ state = ST_GND; /* Start in ground state. */
+ while (state < ST_END)
{
- parse_ls_color ();
+ switch (state)
+ {
+ case ST_GND: /* Ground state (no escapes) */
+ switch (*p)
+ {
+ case ':':
+ case '\0':
+ state = ST_END; /* End of string */
+ break;
+ case '\\':
+ state = ST_BACKSLASH; /* Backslash scape sequence */
+ ++p;
+ break;
+ case '^':
+ state = ST_CARET; /* Caret escape */
+ ++p;
+ break;
+ case '=':
+ if (equals_end)
+ {
+ state = ST_END; /* End */
+ break;
+ }
+ /* else fall through */
+ default:
+ *(q++) = *(p++);
+ ++count;
+ break;
+ }
+ break;
+
+ case ST_BACKSLASH: /* Backslash escaped character */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ state = ST_OCTAL; /* Octal sequence */
+ num = *p - '0';
+ break;
+ case 'x':
+ case 'X':
+ state = ST_HEX; /* Hex sequence */
+ num = 0;
+ break;
+ case 'a': /* Bell */
+ num = 7; /* Not all C compilers know what \a means */
+ break;
+ case 'b': /* Backspace */
+ num = '\b';
+ break;
+ case 'e': /* Escape */
+ num = 27;
+ break;
+ case 'f': /* Form feed */
+ num = '\f';
+ break;
+ case 'n': /* Newline */
+ num = '\n';
+ break;
+ case 'r': /* Carriage return */
+ num = '\r';
+ break;
+ case 't': /* Tab */
+ num = '\t';
+ break;
+ case 'v': /* Vtab */
+ num = '\v';
+ break;
+ case '?': /* Delete */
+ num = 127;
+ break;
+ case '_': /* Space */
+ num = ' ';
+ break;
+ case '\0': /* End of string */
+ state = ST_ERROR; /* Error! */
+ break;
+ default: /* Escaped character like \ ^ : = */
+ num = *p;
+ break;
+ }
+ if (state == ST_BACKSLASH)
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ ++p;
+ break;
+
+ case ST_OCTAL: /* Octal sequence */
+ if (*p < '0' || *p > '7')
+ {
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ }
+ else
+ num = (num << 3) + (*(p++) - '0');
+ break;
+
+ case ST_HEX: /* Hex sequence */
+ switch (*p)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ num = (num << 4) + (*(p++) - '0');
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ num = (num << 4) + (*(p++) - 'a') + 10;
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ num = (num << 4) + (*(p++) - 'A') + 10;
+ break;
+ default:
+ *(q++) = num;
+ ++count;
+ state = ST_GND;
+ break;
+ }
+ break;
+
+ case ST_CARET: /* Caret escape */
+ state = ST_GND; /* Should be the next state... */
+ if (*p >= '@' && *p <= '~')
+ {
+ *(q++) = *(p++) & 037;
+ ++count;
+ }
+ else if ( *p == '?')
+ {
+ *(q++) = 127;
+ ++count;
+ }
+ else
+ state = ST_ERROR;
+ break;
+
+ default:
+ abort();
+ }
}
- return optind;
+ *dest = q;
+ *src = p;
+
+ return state == ST_ERROR ? -1 : count;
}
-\f
-/* Parse the LS_COLORS/LS_COLOURS variable */
static void
parse_ls_color (void)
{
- register char *p; /* Pointer to character being parsed */
- char *whichvar; /* LS_COLORS or LS_COLOURS? */
+ const char *p; /* Pointer to character being parsed */
+ char *buf; /* color_buf buffer pointer */
int state; /* State of parser */
int ind_no; /* Indicator number */
- int ccount; /* Character count */
- int num; /* Escape char numeral */
- char label[3] = "??"; /* Indicator label */
+ char label[3]; /* Indicator label */
+ struct col_ext_type *ext; /* Extension we are working on */
+ struct col_ext_type *ext2; /* Extra pointer */
+
+ if ((p = getenv ("LS_COLORS")) == NULL || *p == '\0')
+ return;
- if ((p = getenv (whichvar = "LS_COLORS")) ||
- (p = getenv (whichvar = "LS_COLOURS")))
+ ext = NULL;
+ strcpy (label, "??");
+
+ /* This is an overly conservative estimate, but any possible
+ LS_COLORS string will *not* generate a color_buf longer than
+ itself, so it is a safe way of allocating a buffer in
+ advance. */
+ buf = color_buf = xstrdup (p);
+
+ state = 1;
+ while (state > 0)
{
- state = 1;
- while (state > 0)
+ switch (state)
{
- switch (state)
+ case 1: /* First label character */
+ switch (*p)
{
- case 1: /* First label character */
- if (*p)
- {
- label[0] = *(p++);
- state = 2;
- }
- else
- state = 0; /* Done */
+ case ':':
+ ++p;
break;
- case 2: /* Second label character */
- if (*p)
- {
- label[1] = *(p++);
- state = 3;
- }
- else
- state = -1; /* Error */
- break;
+ case '*':
+ /* Allocate new extension block and add to head of
+ linked list (this way a later definition will
+ override an earlier one, which can be useful for
+ having terminal-specific defs override global). */
- case 3: /* Should be equal sign */
- if (*(p++) != '=')
- state = -1; /* Error state */
- else
- {
- ind_no = 0;
- state = -1; /* In case we fail */
- while (indicator_name[ind_no] != NULL)
- {
- if (strcmp (label, indicator_name[ind_no]) == 0)
- {
- state = 4; /* We found it */
- ccount = 0; /* Nothing stored yet */
- break;
- }
- else
- ind_no++;
- }
- }
- break;
+ ext = (struct col_ext_type *)
+ xmalloc (sizeof (struct col_ext_type));
+ ext->next = col_ext_list;
+ col_ext_list = ext;
- case 4: /* Character to store */
- switch (*p)
- {
- case ':':
- color_indicator[ind_no].len = ccount;
- state = 1;
- break;
- case '\\':
- /* The escape sequence will always generate a character,
- so enter error state if the buffer is full */
- state = (ccount >= MAXCOLORLEN) ? -1 : 5;
- break;
- case '^':
- /* Control character in the ^X notation */
- state = (ccount >= MAXCOLORLEN) ? -1 : 8;
- break;
- case '\0':
- color_indicator[ind_no].len = ccount;
- state = 0; /* Done */
- break;
- default:
- if (ccount >= MAXCOLORLEN)
- state = -1; /* Too long */
- else
- color_indicator[ind_no].string[ccount++] = *p;
- }
- p++;
+ ++p;
+ ext->ext.string = buf;
+
+ state = (ext->ext.len =
+ get_funky_string (&buf, &p, 1)) < 0 ? -1 : 4;
break;
- case 5: /* Escape character */
- num = -1;
- switch (*p)
- {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- state = 6; /* Octal numeral */
- num = *p - '0';
- break;
- case 'x':
- case 'X':
- state = 7; /* Hex numeral */
- num = 0;
- break;
- case 'a': /* Bell */
- num = 7;
- break;
- case 'b': /* Backspace */
- num = '\b';
- break;
- case 'e': /* Escape */
- num = 27;
- break;
- case 'f': /* Formfeed */
- num = '\f';
- break;
- case 'n': /* Newline */
- num = '\n';
- break;
- case 'r': /* Return */
- num = '\r';
- break;
- case 't': /* Tab */
- num = '\t';
- break;
- case 'v': /* Vtab */
- num = '\v';
- break;
- case '?': /* Delete */
- num = 127;
- break;
- case '\0': /* End of string */
- state = -1; /* Error */
- break;
- default: /* Escaped character */
- num = *p;
- break;
- }
- if (state == 5)
- {
- color_indicator[ind_no].string[ccount++] = num;
- state = 4;
- }
- p++;
+ case '\0':
+ state = 0; /* Done! */
break;
- case 6: /* Octal numeral */
- switch (*p)
- {
- case ':':
- case '\0':
- color_indicator[ind_no].string[ccount++] = num;
- color_indicator[ind_no].len = ccount;
- state = (*(p++) == ':') ? 1 : 0;
- p++;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- num = (num << 3) + (*(p++) - '0');
- break;
- default:
- color_indicator[ind_no].string[ccount++] = num;
- state = 4;
- break;
- }
+ default: /* Assume it is file type label */
+ label[0] = *(p++);
+ state = 2;
break;
+ }
+ break;
+
+ case 2: /* Second label character */
+ if (*p)
+ {
+ label[1] = *(p++);
+ state = 3;
+ }
+ else
+ state = -1; /* Error */
+ break;
- case 7: /* Hex numeral */
- switch (*p)
+ case 3: /* Equal sign after indicator label */
+ state = -1; /* Assume failure... */
+ if (*(p++) == '=')/* It *should* be... */
+ {
+ for (ind_no = 0; indicator_name[ind_no] != NULL; ++ind_no)
{
- case ':':
- case '\0':
- color_indicator[ind_no].string[ccount++] = num;
- color_indicator[ind_no].len = ccount;
- state = (*(p++) == ':') ? 1 : 0;
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- num = (num << 4) + (*(p++) - '0');
- break;
- case 'A':
- case 'B':
- case 'C':
- case 'D':
- case 'E':
- case 'F':
- num = (num << 4) + (*(p++) - 'A' + 10);
- break;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- num = (num << 4) + (*(p++) - 'a' + 10);
- break;
- default:
- color_indicator[ind_no].string[ccount++] = num;
- state = 4;
- break;
+ if (STREQ (label, indicator_name[ind_no]))
+ {
+ color_indicator[ind_no].string = buf;
+ state = ((color_indicator[ind_no].len =
+ get_funky_string (&buf, &p, 0)) < 0 ? -1 : 1);
+ break;
+ }
}
- break;
+ if (state == -1)
+ error (0, 0, _("unrecognized prefix: %s"), label);
+ }
+ break;
- case 8: /* ^ notation */
- state = 4; /* Usually the next state */
- if (*p >= '@' && *p <= '~')
- color_indicator[ind_no].string[ccount++] = *p & 037;
- else if (*p == '?')
- color_indicator[ind_no].string[ccount++] = 127;
- else
- state = -1; /* Error */
- p++;
- break;
+ case 4: /* Equal sign after *.ext */
+ if (*(p++) == '=')
+ {
+ ext->seq.string = buf;
+ state = (ext->seq.len =
+ get_funky_string (&buf, &p, 0)) < 0 ? -1 : 1;
}
+ else
+ state = -1;
+ break;
}
+ }
- if (state < 0)
+ if (state < 0)
+ {
+ struct col_ext_type *e;
+
+ error (0, 0,
+ _("unparsable value for LS_COLORS environment variable"));
+ free (color_buf);
+ for (e = col_ext_list; e != NULL ; /* empty */)
{
- error (0, 0, "bad %s variable\n", whichvar);
- print_with_color = 0;
+ ext2 = e;
+ e = e->next;
+ free (ext2);
}
+ print_with_color = 0;
}
}
-\f
+
/* Request that the directory named `name' have its contents listed later.
If `realname' is nonzero, it will be used instead of `name' when the
directory name is printed. This allows symbolic links to directories
real names. */
static void
-queue_directory (char *name, char *realname)
+queue_directory (const char *name, const char *realname)
{
struct pending *new;
if (pending_dirs)
PUTCHAR ('\n');
}
-\f
+
/* Add `pattern' to the list of patterns for which files that match are
not listed. */
static void
-add_ignore_pattern (char *pattern)
+add_ignore_pattern (const char *pattern)
{
register struct ignore_pattern *ignore;
/* Return nonzero if the file in `next' should be listed. */
static int
-file_interesting (register struct dirent *next)
+file_interesting (const struct dirent *next)
{
register struct ignore_pattern *ignore;
return 0;
}
-\f
+
/* Enter and remove entries in the table `files'. */
/* Empty the table of files. */
files[files_index].linkname = 0;
files[files_index].linkmode = 0;
+ files[files_index].linkok = 0;
if (explicit_arg || format_needs_stat)
{
#ifdef S_ISLNK
if (S_ISLNK (files[files_index].stat.st_mode)
- && (explicit_arg || format == long_format))
+ && (explicit_arg || format == long_format || print_with_color))
{
char *linkpath;
struct stat linkstats;
they won't be traced and when no indicator is needed. */
if (linkpath
&& ((explicit_arg && format != long_format)
- || indicator_style != none || print_with_color)
+ || indicator_style != none
+ || print_with_color)
&& stat (linkpath, &linkstats) == 0)
{
+ files[files_index].linkok = 1;
+
/* Symbolic links to directories that are mentioned on the
command line are automatically traced if not being
listed as files. */
files[files_index].stat = linkstats;
}
else
- /* Get the linked-to file's mode for the filetype indicator
- in long listings. */
- files[files_index].linkmode = linkstats.st_mode;
+ {
+ /* Get the linked-to file's mode for the filetype indicator
+ in long listings. */
+ files[files_index].linkmode = linkstats.st_mode;
+ files[files_index].linkok = 1;
+ }
}
if (linkpath)
free (linkpath);
into the `linkname' field of `f'. */
static void
-get_link_name (char *filename, struct fileinfo *f)
+get_link_name (const char *filename, struct fileinfo *f)
{
char *linkbuf;
register int linksize;
If `linkname' is zero, return zero. */
static char *
-make_link_path (char *path, char *linkname)
+make_link_path (const char *path, const char *linkname)
{
char *linkbuf;
int bufsiz;
files[j++] = files[i];
files_index = j;
}
-\f
-/* Return non-zero if `name' doesn't end in `.' or `..'
+
+/* Return nonzero if `name' doesn't end in `.' or `..'
This is so we don't try to recurse on `././././. ...' */
static int
-is_not_dot_or_dotdot (char *name)
+is_not_dot_or_dotdot (const char *name)
{
char *t;
return 1;
}
-\f
+
/* Sort the files now in the table. */
static void
/* Comparison routines for sorting the files. */
static int
-compare_ctime (struct fileinfo *file1, struct fileinfo *file2)
+compare_ctime (const struct fileinfo *file1, const struct fileinfo *file2)
{
return longdiff (file2->stat.st_ctime, file1->stat.st_ctime);
}
static int
-rev_cmp_ctime (struct fileinfo *file2, struct fileinfo *file1)
+rev_cmp_ctime (const struct fileinfo *file2, const struct fileinfo *file1)
{
return longdiff (file2->stat.st_ctime, file1->stat.st_ctime);
}
static int
-compare_mtime (struct fileinfo *file1, struct fileinfo *file2)
+compare_mtime (const struct fileinfo *file1, const struct fileinfo *file2)
{
return longdiff (file2->stat.st_mtime, file1->stat.st_mtime);
}
static int
-rev_cmp_mtime (struct fileinfo *file2, struct fileinfo *file1)
+rev_cmp_mtime (const struct fileinfo *file2, const struct fileinfo *file1)
{
return longdiff (file2->stat.st_mtime, file1->stat.st_mtime);
}
static int
-compare_atime (struct fileinfo *file1, struct fileinfo *file2)
+compare_atime (const struct fileinfo *file1, const struct fileinfo *file2)
{
return longdiff (file2->stat.st_atime, file1->stat.st_atime);
}
static int
-rev_cmp_atime (struct fileinfo *file2, struct fileinfo *file1)
+rev_cmp_atime (const struct fileinfo *file2, const struct fileinfo *file1)
{
return longdiff (file2->stat.st_atime, file1->stat.st_atime);
}
static int
-compare_size (struct fileinfo *file1, struct fileinfo *file2)
+compare_size (const struct fileinfo *file1, const struct fileinfo *file2)
{
return longdiff (file2->stat.st_size, file1->stat.st_size);
}
static int
-rev_cmp_size (struct fileinfo *file2, struct fileinfo *file1)
+rev_cmp_size (const struct fileinfo *file2, const struct fileinfo *file1)
{
return longdiff (file2->stat.st_size, file1->stat.st_size);
}
static int
-compare_name (struct fileinfo *file1, struct fileinfo *file2)
+compare_name (const struct fileinfo *file1, const struct fileinfo *file2)
{
return strcmp (file1->name, file2->name);
}
static int
-rev_cmp_name (struct fileinfo *file2, struct fileinfo *file1)
+rev_cmp_name (const struct fileinfo *file2, const struct fileinfo *file1)
{
return strcmp (file1->name, file2->name);
}
If extensions are the same, compare by filenames instead. */
static int
-compare_extension (struct fileinfo *file1, struct fileinfo *file2)
+compare_extension (const struct fileinfo *file1, const struct fileinfo *file2)
{
register char *base1, *base2;
register int cmp;
}
static int
-rev_cmp_extension (struct fileinfo *file2, struct fileinfo *file1)
+rev_cmp_extension (const struct fileinfo *file2, const struct fileinfo *file1)
{
register char *base1, *base2;
register int cmp;
return strcmp (file1->name, file2->name);
return cmp;
}
-\f
+
/* List all the files now in the table. */
static void
}
static void
-print_long_format (struct fileinfo *f)
+print_long_format (const struct fileinfo *f)
{
- char modebuf[20];
- char timebuf[40];
-
- /* 7 fields that may (worst case be 64-bit integral values) require 20 bytes,
- 10 character mode field,
- 24 characters for the time,
- 9 spaces following each of these fields,
- and 1 trailing NUL byte. */
- char bigbuf[7 * 20 + 10 + 24 + 9 + 1];
+ char modebuf[11];
+
+ /* 7 fields that may (worst case: 64-bit integral values) require 20 bytes,
+ 1 10-byte mode string,
+ 1 24-byte time string (may be longer in some locales -- see below),
+ 9 spaces, one following each of these fields, and
+ 1 trailing NUL byte. */
+ char init_bigbuf[7 * 20 + 10 + 24 + 9 + 1];
+ char *buf = init_bigbuf;
+ size_t bufsize = sizeof (init_bigbuf);
+ size_t s;
char *p;
time_t when;
+ const char *fmt;
+#ifdef HAVE_ST_DM_MODE
+ mode_string (f->stat.st_dm_mode, modebuf);
+#else
mode_string (f->stat.st_mode, modebuf);
+#endif
+
modebuf[10] = '\0';
switch (time_type)
break;
}
- strcpy (timebuf, ctime (&when));
-
if (full_time)
- timebuf[24] = '\0';
+ {
+ fmt = "%a %b %d %H:%M:%S %Y";
+ }
else
{
if (current_time > when + 6L * 30L * 24L * 60L * 60L /* Old. */
Allow a 1 hour slop factor for what is considered "the future",
to allow for NFS server/client clock disagreement.
Show the year instead of the time of day. */
- strcpy (timebuf + 11, timebuf + 19);
+ fmt = "%b %e %Y";
+ }
+ else
+ {
+ fmt = "%b %e %H:%M";
}
- timebuf[16] = 0;
}
- p = bigbuf;
+ p = buf;
if (print_inode)
{
sprintf (p, "%8lu ", (unsigned long) f->stat.st_size);
p += strlen (p);
- sprintf (p, "%s ", full_time ? timebuf : timebuf + 4);
- p += strlen (p);
+ /* Use strftime rather than ctime, because the former can produce
+ locale-dependent names for the weekday (%a) and month (%b). */
+
+ while (! (s = strftime (p, buf + bufsize - p - 1, fmt, localtime (&when))))
+ {
+ char *newbuf = (char *) alloca (bufsize *= 2);
+ memcpy (newbuf, buf, p - buf);
+ p = newbuf + (p - buf);
+ buf = newbuf;
+ }
+
+ p += s;
+ *p++ = ' ';
+
+ /* NUL-terminate the string -- fputs (via FPUTS) requires it. */
+ *p = '\0';
DIRED_INDENT ();
- FPUTS (bigbuf, stdout, p - bigbuf);
+ FPUTS (buf, stdout, p - buf);
PUSH_CURRENT_DIRED_POS (&dired_obstack);
- if (print_with_color)
- print_color_indicator (f->stat.st_mode);
- print_name_with_quoting (f->name);
- if (print_with_color)
- put_indicator (C_END);
+ print_name_with_quoting (f->name, f->stat.st_mode, f->linkok);
PUSH_CURRENT_DIRED_POS (&dired_obstack);
if (f->filetype == symbolic_link)
if (f->linkname)
{
FPUTS_LITERAL (" -> ", stdout);
- if (print_with_color)
- print_color_indicator (f->linkmode);
- print_name_with_quoting (f->linkname);
- if (print_with_color)
- put_indicator (C_END);
+ print_name_with_quoting (f->linkname, f->linkmode, f->linkok - 1);
if (indicator_style != none)
print_type_indicator (f->linkmode);
}
else if (indicator_style != none)
print_type_indicator (f->stat.st_mode);
}
-\f
+
/* Set QUOTED_LENGTH to strlen(P) and return NULL if P == quoted(P).
Otherwise, return xmalloc'd storage containing the quoted version
of P and set QUOTED_LENGTH to the length of the quoted P. */
break;
default:
- /* FIXME: why not just use the ISPRINT macro here? */
- if (!((c > 040 && c < 0177)
- || (print_iso8859 && c >= 0200 && c <= 0377)))
+ if (!ISGRAPH (c))
found_quotable = 1;
break;
}
}
else
{
- if (!((c >= 040 && c < 0177)
- || (print_iso8859 && c >= 0xA1 && c <= 0xFF))
- && qmark_funny_chars)
+ if (!ISPRINT (c) && qmark_funny_chars)
found_quotable = 1;
}
if (found_quotable)
break;
default:
- if ((c > 040 && c < 0177)
- || (print_iso8859 && c >= 0200 && c <= 0377))
+ if (ISGRAPH (c))
SAVECHAR (c);
else
{
}
else
{
- if ((c >= 040 && c < 0177)
- || (print_iso8859 && c >= 0200 && c <= 0377))
+ if (ISPRINT (c))
SAVECHAR (c);
else if (!qmark_funny_chars)
SAVECHAR (c);
}
static void
-print_name_with_quoting (register char *p)
+print_name_with_quoting (const char *p, unsigned int mode, int linkok)
{
char *quoted;
size_t quoted_length;
+ if (print_with_color)
+ print_color_indicator (p, mode, linkok);
+
quoted = quote_filename (p, "ed_length);
FPUTS (quoted != NULL ? quoted : p, stdout, quoted_length);
if (quoted)
free (quoted);
+
+ if (print_with_color)
+ prep_non_filename_text ();
}
-\f
+
+static void
+prep_non_filename_text (void)
+{
+ if (color_indicator[C_END].string != NULL)
+ put_indicator (&color_indicator[C_END]);
+ else
+ {
+ put_indicator (&color_indicator[C_LEFT]);
+ put_indicator (&color_indicator[C_NORM]);
+ put_indicator (&color_indicator[C_RIGHT]);
+ }
+}
+
/* Print the file name of `f' with appropriate quoting.
Also print file size, inode number, and filetype indicator character,
as requested by switches. */
static void
-print_file_name_and_frills (struct fileinfo *f)
+print_file_name_and_frills (const struct fileinfo *f)
{
if (print_inode)
printf ("%*lu ", INODE_DIGITS, (unsigned long) f->stat.st_ino);
(unsigned) convert_blocks (ST_NBLOCKS (f->stat),
kilobyte_blocks));
- if (print_with_color)
- print_color_indicator (f->stat.st_mode);
- print_name_with_quoting (f->name);
- if (print_with_color)
- put_indicator (C_END);
+ print_name_with_quoting (f->name, f->stat.st_mode, f->linkok);
if (indicator_style != none)
print_type_indicator (f->stat.st_mode);
#endif
if (S_ISREG (mode) && indicator_style == all
- && (mode & (S_IEXEC | S_IXGRP | S_IXOTH)))
+ && (mode & S_IXUGO))
PUTCHAR ('*');
}
static void
-print_color_indicator (unsigned int mode)
+print_color_indicator (const char *name, unsigned int mode, int linkok)
{
int type = C_FILE;
+ struct col_ext_type *ext; /* Color extension */
+ size_t len; /* Length of name */
- if (S_ISDIR (mode))
- type = C_DIR;
+ /* Is this a nonexistent file? If so, linkok == -1. */
+
+ if (linkok == -1 && color_indicator[C_MISSING].string != NULL)
+ {
+ ext = NULL;
+ type = C_MISSING;
+ }
+ else
+ {
+ if (S_ISDIR (mode))
+ type = C_DIR;
#ifdef S_ISLNK
- else if (S_ISLNK (mode))
- type = C_LINK;
+ else if (S_ISLNK (mode))
+ type = ((!linkok && color_indicator[C_ORPHAN].string)
+ ? C_ORPHAN : C_LINK);
#endif
#ifdef S_ISFIFO
- else if (S_ISFIFO (mode))
- type = C_FIFO;
+ else if (S_ISFIFO (mode))
+ type = C_FIFO;
#endif
#ifdef S_ISSOCK
- else if (S_ISSOCK (mode))
- type = C_SOCK;
+ else if (S_ISSOCK (mode))
+ type = C_SOCK;
#endif
#ifdef S_ISBLK
- else if (S_ISBLK (mode))
- type = C_BLK;
+ else if (S_ISBLK (mode))
+ type = C_BLK;
#endif
#ifdef S_ISCHR
- else if (S_ISCHR (mode))
- type = C_CHR;
+ else if (S_ISCHR (mode))
+ type = C_CHR;
#endif
- if (type == C_FILE && (mode & (S_IEXEC | S_IEXEC >> 3 | S_IEXEC >> 6)))
- type = C_EXEC;
+ if (type == C_FILE && (mode & S_IXUGO) != 0)
+ type = C_EXEC;
- put_indicator (C_LEFT);
- put_indicator (type);
- put_indicator (C_RIGHT);
+ /* Check the file's suffix only if still classified as C_FILE. */
+ ext = NULL;
+ if (type == C_FILE)
+ {
+ /* Test if NAME has a recognized suffix. */
+
+ len = strlen (name);
+ name += len; /* Pointer to final \0. */
+ for (ext = col_ext_list; ext != NULL; ext = ext->next)
+ {
+ if (ext->ext.len <= len
+ && strncmp (name - ext->ext.len, ext->ext.string,
+ ext->ext.len) == 0)
+ break;
+ }
+ }
+ }
+
+ put_indicator (&color_indicator[C_LEFT]);
+ put_indicator (ext ? &(ext->seq) : &color_indicator[type]);
+ put_indicator (&color_indicator[C_RIGHT]);
}
-/* Output a color indicator (which may contain nulls) */
+/* Output a color indicator (which may contain nulls). */
static void
-put_indicator (int n)
+put_indicator (const struct bin_str *ind)
{
register int i;
register char *p;
- p = color_indicator[n].string;
+ p = ind->string;
- for (i = color_indicator[n].len; i; i--)
- {
- putchar (*(p++));
- }
+ for (i = ind->len; i > 0; --i)
+ putchar (*(p++));
}
static int
-length_of_file_name_and_frills (struct fileinfo *f)
+length_of_file_name_and_frills (const struct fileinfo *f)
{
register char *p = f->name;
register unsigned char c;
break;
default:
- if ((c >= 040 && c < 0177)
- || (print_iso8859 && c >= 0200 && c <= 0377))
+ if (ISPRINT (c))
len += 1;
else
len += 4;
if (S_ISREG (filetype))
{
if (indicator_style == all
- && (f->stat.st_mode & (S_IEXEC | S_IEXEC >> 3 | S_IEXEC >> 6)))
+ && (f->stat.st_mode & S_IXUGO))
len += 1;
}
else if (S_ISDIR (filetype)
return len;
}
-\f
+
static void
print_many_per_line (void)
{
putchar ('\n');
}
}
-\f
+
static void
print_horizontal (void)
{
}
putchar ('\n');
}
-\f
+
static void
print_with_commas (void)
{
}
putchar ('\n');
}
-\f
+
/* Assuming cursor is at position FROM, indent up to position TO.
Use a TAB character instead of two or more spaces whenever possible. */
{
while (from < to)
{
- if (to / tabsize > (from + 1) / tabsize)
+ if (tabsize > 0 && to / tabsize > (from + 1) / tabsize)
{
putchar ('\t');
from += tabsize - from % tabsize;
usage (int status)
{
if (status != 0)
- fprintf (stderr, "Try `%s --help' for more information.\n",
+ fprintf (stderr, _("Try `%s --help' for more information.\n"),
program_name);
else
{
- printf ("Usage: %s [OPTION]... [FILE]...\n", program_name);
- printf ("\
+ printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);
+ printf (_("\
List information about the FILEs (the current directory by default).\n\
Sort entries alphabetically if none of -cftuSUX nor --sort.\n\
\n\
- -A, --almost-all do not list implied . and ..\n\
-a, --all do not hide entries starting with .\n\
- -B, --ignore-backups do not list implied entries ending with ~\n\
+ -A, --almost-all do not list implied . and ..\n\
-b, --escape print octal escapes for nongraphic characters\n\
- -C list entries by columns\n\
+ -B, --ignore-backups do not list implied entries ending with ~\n\
-c sort by change time; with -l: show ctime\n\
- -D, --dired generate output well suited to Emacs' dired mode\n\
+ -C list entries by columns\n\
+ --color[=WHEN] control whether color is used to distinguish file\n\
+ types. WHEN may be `never', `always', or `auto'\n\
-d, --directory list directory entries instead of contents\n\
- -F, --classify append a character for typing each entry\n\
+ -D, --dired generate output designed for Emacs' dired mode\n\
-f do not sort, enable -aU, disable -lst\n\
+ -F, --classify append a character for typing each entry\n\
--format=WORD across -x, commas -m, horizontal -x, long -l,\n\
single-column -1, verbose -l, vertical -C\n\
- --full-time list both full date and full time\n");
+ --full-time list both full date and full time\n"));
- printf ("\
- -G, --no-group inhibit display of group information\n\
+ printf (_("\
-g (ignored)\n\
- -I, --ignore=PATTERN do not list implied entries matching shell PATTERN\n\
+ -G, --no-group inhibit display of group information\n\
-i, --inode print index number of each file\n\
+ -I, --ignore=PATTERN do not list implied entries matching shell PATTERN\n\
-k, --kilobytes use 1024 blocks, not 512 despite POSIXLY_CORRECT\n\
- -L, --dereference list entries pointed to by symbolic links\n\
-l use a long listing format\n\
+ -L, --dereference list entries pointed to by symbolic links\n\
-m fill width with a comma separated list of entries\n\
- -N, --literal do not quote entry names\n\
-n, --numeric-uid-gid list numeric UIDs and GIDs instead of names\n\
- -o, --color, --colour colorize entries according to type\n\
- --colo(u)r=WORD yes -o, no, tty (if output is a terminal)\n\
+ -N, --literal print raw entry names (don't treat e.g. control\n\
+ characters specially)\n\
+ -o use long listing format without group info\n\
-p append a character for typing each entry\n\
- -Q, --quote-name enclose entry names in double quotes\n\
-q, --hide-control-chars print ? instead of non graphic characters\n\
- -R, --recursive list subdirectories recursively\n\
+ -Q, --quote-name enclose entry names in double quotes\n\
-r, --reverse reverse order while sorting\n\
- -S sort by file size\n");
+ -R, --recursive list subdirectories recursively\n\
+ -s, --size print size of each file, in blocks\n"));
- printf ("\
- -s, --size print block size of each file\n\
+ printf (_("\
+ -S sort by file size\n\
--sort=WORD ctime -c, extension -X, none -U, size -S,\n\
- status -c, time -t\n\
- --time=WORD atime -u, access -u, use -u\n\
- -T, --tabsize=COLS assume tab stops at each COLS instead of 8\n\
+ status -c, time -t, atime -u, access -u, use -u\n\
+ --time=WORD show time as WORD instead of modification time:\n\
+ atime, access, use, ctime or status\n\
-t sort by modification time; with -l: show mtime\n\
- -U do not sort; list entries in directory order\n\
+ -T, --tabsize=COLS assume tab stops at each COLS instead of 8\n\
-u sort by last access time; with -l: show atime\n\
+ -U do not sort; list entries in directory order\n\
-w, --width=COLS assume screen width instead of current value\n\
-x list entries by lines instead of by columns\n\
-X sort alphabetically by entry extension\n\
-1 list one file per line\n\
- -7, --7bit allow only 7-bit ASCII characters to be printed\n\
- -8, --8bit allow 8-bit ISO 8859 characters to be printed\n\
--help display this help and exit\n\
- --version output version information and exit");
+ --version output version information and exit\n\
+\n\
+By default, color is not used to distinguish types of files. That is\n\
+equivalent to using --color=none. Using the --color option without the\n\
+optional WHEN argument is equivalent to using --color=always. With\n\
+--color=auto, color codes are output only if standard output is connected\n\
+to a terminal (tty).\n\
+"));
+ puts (_("\nReport bugs to <fileutils-bugs@gnu.ai.mit.edu>."));
}
exit (status);
}