--- coreutils-cvs-20051215.orig/src/ls.c Thu Nov 17 13:28:34 2005 +++ coreutils-cvs-20051215/src/ls.c Thu Dec 15 20:12:11 2005 @@ -232,6 +232,9 @@ static int decode_switches (int argc, ch static bool file_ignored (char const *name); static uintmax_t gobble_file (char const *name, enum filetype type, bool command_line_arg, char const *dirname); +static char valid_filetypes (const char *types); +static int is_selected_filetype (mode_t mode, const char * types); +static void strip_dirs (void); static void print_color_indicator (const char *name, mode_t mode, int linkok); static void put_indicator (const struct bin_str *ind); static void add_ignore_pattern (const char *pattern); @@ -494,6 +497,13 @@ static enum indicator_style const indica }; ARGMATCH_VERIFY (indicator_style_args, indicator_style_types); +/* -P/--select-file-type argument, selected filetypes as string. */ +static const char *selected_filetype = NULL; +/* When -R or a directory is given as argument, allow recursive + scanning even if selected_filetype does not contain 'd' (don't + display directories). */ +static bool dir_not_selected = 0; + /* True 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. */ @@ -764,6 +774,7 @@ static struct option const long_options[ {"ignore-backups", no_argument, NULL, 'B'}, {"classify", no_argument, NULL, 'F'}, {"file-type", no_argument, NULL, 'p'}, + {"select-file-type", required_argument, NULL, 'P'}, {"si", no_argument, NULL, SI_OPTION}, {"dereference-command-line", no_argument, NULL, 'H'}, {"dereference-command-line-symlink-to-dir", no_argument, NULL, @@ -1260,6 +1271,9 @@ main (int argc, char **argv) if (!immediate_dirs) extract_dirs_from_files (NULL, true); /* `files_index' might be zero now. */ + + if (dir_not_selected) + strip_dirs (); } /* In the following if/else blocks, it is sufficient to test `pending_dirs' @@ -1489,7 +1503,7 @@ decode_switches (int argc, char **argv) } while ((c = getopt_long (argc, argv, - "abcdfghiklmnopqrstuvw:xABCDFGHI:LNQRST:UX1", + "abcdfghiklmnopqrstuvw:xABCDFGHI:LNP:QRST:UX1", long_options, NULL)) != -1) { switch (c) @@ -1654,6 +1668,22 @@ decode_switches (int argc, char **argv) set_quoting_style (NULL, literal_quoting_style); break; + case 'P': + if (! *optarg) + error (LS_FAILURE, 0, + _("invalid (empty) argument for --select-file-type/-P")); + else + { + char erropt = valid_filetypes (optarg); + if (erropt) + error (LS_FAILURE, 0, _("invalid filetype: %c"), erropt); + selected_filetype = optarg; + dir_not_selected = (((strchr (selected_filetype, 'd') == NULL) + && (strchr (selected_filetype, 'a') == NULL)) + ? true : false); + } + break; + case 'Q': set_quoting_style (NULL, c_quoting_style); break; @@ -2276,6 +2306,33 @@ queue_directory (char const *name, char pending_dirs = new; } +/* Delete the directories from the table, compacting all the remaining + entries. */ + +static void +strip_dirs (void) +{ + register int i, j; + + for (i=0, j=0; i < files_index; i++) + { + if (files[i].filetype == directory || files[i].filetype == arg_directory) + { + if (files[i].name); + free (files[i].name); + if (files[i].linkname) + free (files[i].linkname); + } + else + { + if (j < i) + files[j] = files[i]; + ++j; + } + } + files_index = j; +} + /* Read directory NAME, and list the files in it. If REALNAME is nonzero, print its name instead of NAME; this is used for symbolic links to directories. @@ -2350,7 +2407,32 @@ print_dir (char const *name, char const || next->d_type == DT_REG || next->d_type == DT_SOCK) type = next->d_type; -#endif + +# ifdef DTTOIF + /* We have d_type and DTTOIF, let speed-up filetype checking. */ + if (selected_filetype) + { + switch (next->d_type) + { + case DT_BLK: case DT_CHR: + case DT_FIFO: case DT_SOCK: + case DT_REG: + if (! is_selected_filetype(DTTOIF (next->d_type), + selected_filetype)) + continue; + case DT_DIR: /* go ahead, allow recursive scanning */ + case DT_LNK: /* check for dereference */ + case DT_UNKNOWN: + /* d_type not implemented (reiserfs, xfs, jfs, + minix,...), need further investigation. */ + default: /* ??? */ + break; + } + } +# endif /* DTTOIF */ + +#endif /* HAVE_STRUCT_DIRENT_D_TYPE */ + total_blocks += gobble_file (next->d_name, type, false, name); } } @@ -2379,6 +2461,9 @@ print_dir (char const *name, char const if (recursive) extract_dirs_from_files (name, command_line_arg); + if (files_index && dir_not_selected) + strip_dirs (); + if (recursive | print_dir_name) { if (!first) @@ -2451,6 +2536,138 @@ file_ignored (char const *name) || patterns_match (ignore_patterns, name)); } +/* Check -P/--select-file-type argument. + Return zero for recognized types, return the type (nonzero) otherwise. */ + +static char +valid_filetypes (const char *types) +{ + const char *c; + for (c = types; c && *c; c++) + { + switch (*c) + { + case 'f': /* file */ + case 'd': /* directory */ + case 'c': /* character special */ + case 'b': /* block special */ + case 'p': /* named pipe */ + case 'l': /* symbolic link */ + case 's': /* socket */ + case 'D': /* door */ + case 'a': /* all */ + break; + default: /* unknown/unsupported */ + return *c; + } + } + return 0; +} + +/* Return nonzero if the file matches any selected type. + Return 1 if the file is a directory and 'd' is not a selected + filetype. This allows recursive scanning even if directories + should not be considered (they will be remove later). + Return 0 if: + - no match is found; + - an invalid type is given (should never happen). */ +/* +** -P fdcbplsD(a) +** +** f DT_REG || S_ISREG(m) regular file? +** d DT_DIR || S_ISDIR(m) directory? +** c DT_CHR || S_ISCHR(m) character device? +** b DT_BLK || S_ISBLK(m) block device? +** p DT_FIFO || S_ISFIFO(m) fifo? +** l DT_LNK || S_ISLNK(m) symbolic link? +** s DT_SOCK || S_ISSOCK(m) socket? +** D ? || S_ISDOOR(m) door? +** +** a everything's good (merely useless, same as no -P given; +** it's here "just in case..." and for debugging). +*/ +static int +is_selected_filetype (mode_t mode, const char * types) +{ +#define _is_file_ (S_ISREG (mode) || ((mode & S_IFMT) == 0)) +#define _is_dir_ (S_ISDIR (mode)) +#define _is_char_ (S_ISCHR (mode)) +#define _is_block_ (S_ISBLK (mode)) +#define _is_link_ (S_ISLNK (mode)) +#ifdef S_ISFIFO +# define _is_fifo_ (S_ISFIFO (mode)) +#else +# define _is_fifo_ (0) +#endif +#ifdef S_ISSOCK +# define _is_socket_ (S_ISSOCK (mode)) +#else +# define _is_socket_ (0) +#endif +#ifdef S_ISDOOR +# define _is_door_ (S_ISDOOR (mode)) +#else +# define _is_door_ (0) +#endif + const char * opt ; + + if (dir_not_selected && _is_dir_) + return 1; /* we must allow recursive scanning, dirs will be removed later */ + + for (opt = types; opt && *opt; opt++) + { + switch (*opt) + { + case 'f': /* File is a regular file? */ + if (_is_file_) + return (int)*opt; + break; + + case 'd': /* File is a directory? */ + if (_is_dir_) + return (int)*opt; + break; + + case 'c': /* File is character special? */ + if (_is_char_) + return (int)*opt; + break; + + case 'b': /* File is block special? */ + if (_is_block_) + return (int)*opt; + break; + + case 'p': /* File is a named pipe? */ + if (_is_fifo_) + return (int)*opt; + break; + + case 'l': /* File is a symbolic link? */ + if (_is_link_) + return (int)*opt; + break; + + case 's': /* File is a socket? */ + if (_is_socket_) + return (int)*opt; + break; + + case 'D': /* File is a door? */ + if (_is_door_) + return (int)*opt; + break; + + case 'a': /* all */ + return (int)*opt; + + default: + return 0; + } + } + return 0; +} + /* POSIX requires that a file size be printed without a sign, even when negative. Assume the typical case where negative sizes are actually positive values that have wrapped around. */ @@ -2514,6 +2731,7 @@ gobble_file (char const *name, enum file f->linkok = false; if (command_line_arg + || selected_filetype || format_needs_stat || (format_needs_type && (type == unknown @@ -2588,6 +2806,23 @@ gobble_file (char const *name, enum file return 0; } + if (selected_filetype) + { + int selected = is_selected_filetype (f->stat.st_mode, selected_filetype); + switch (selected) + { + case 0: /* not selected */ + return 0; /* no blocks */ + case 1: /* directory (temporarily) allowed for recursive scanning */ + f->filetype = directory; + f->name = xstrdup (name); + files_index++; + return 0; /* no blocks */ + default: /* selected */ + break; + } + } + #if HAVE_ACL if (format == long_format) { @@ -4182,6 +4417,13 @@ Mandatory arguments to long options are append / indicator to directories\n\ "), stdout); fputs (_("\ + -P, --select-file-type=TYPES\n\ + select file-type family/ies to display (fdcbplsD):\n\ + regular file f, directory d, character device c,\n\ + block device b, named pipe/fifo p, symbolic link\n\ + l, named socket s, door D\n\ +"), stdout); + fputs (_("\ -q, --hide-control-chars print ? instead of non graphic characters\n\ --show-control-chars show non graphic characters as-is (default\n\ unless program is `ls' and output is a terminal)\n\