>From 3f7852c7ad1aa7dad7ed8d1095212739834420c0 Mon Sep 17 00:00:00 2001 From: Young Mo Kang
Date: Wed, 6 Apr 2016 10:09:40 +0200 Subject: [PATCH] find: support list of file types for -type and -xtype * find/defs.h (enum file_type): Add enumeration for all (supported) file types. (struct predicate): Replace 'type' by 'types' as an array of bool for the above enum file_type. * find/parser.c (insert_type): Treat the argument to -type and -xtype as a comma-separated list of type letters: instead of storing the single type of the predicate, save the searched type letters in the above 'types' array. * find/pred.c (pred_type): Change the comparison against the saved file type predicate: now check if the type of the actual file is in the array of searched 'types'. * find/testsuite/test_type-list.sh: Add test. * find/testsuite/Makefile.am: Reference the test. * find/find.1: Document the new feature. * doc/find.texi: Likewise. * NEWS: Likewise. Co-authored-by: Bernhard Voelker. --- NEWS | 5 + doc/find.texi | 4 + find/defs.h | 23 ++- find/find.1 | 19 +++ find/parser.c | 252 +++++++++++++++++------------ find/pred.c | 77 +++++++-- find/testsuite/Makefile.am | 1 + find/testsuite/test_type-list.sh | 340 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 598 insertions(+), 123 deletions(-) create mode 100755 find/testsuite/test_type-list.sh diff --git a/NEWS b/NEWS index 556eab8..59f6cb9 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,11 @@ the --help option. Previously, when the user passed invalid options or arguments, the user's attention to the corresponding error diagnostic was distracted by that lengthy text. +find now accepts multiple file type arguments to the -type and -xtype +options separated by comma ','. For example, to search for symbolic +links and directories simply provide the shorter '-type l,d' instead +of the - yet more portable - '( -type l -o -type d )'. + ** Bug Fixes #46784: frcode drops last char if no final newline diff --git a/doc/find.texi b/doc/find.texi index 634e229..93c67c3 100644 --- a/doc/find.texi +++ b/doc/find.texi @@ -1081,6 +1081,10 @@ socket @item D door (Solaris) @end table + +As a GNU extension, multiple file types can be provided as a combined list +separated by comma @samp{,}. For example, @samp{-type f,d,l} is logically +interpreted as @samp{( -type f -o -type d -o -type l )}. @end deffn @deffn Test -xtype c diff --git a/find/defs.h b/find/defs.h index 9311565..52e522f 100644 --- a/find/defs.h +++ b/find/defs.h @@ -163,6 +163,27 @@ struct size_val uintmax_t size; }; +/* Supported file types for the -type/-xtype options. */ +enum file_type + { + FTYPE_BLK, + FTYPE_CHR, + FTYPE_DIR, + FTYPE_REG, +#ifdef S_IFLNK + FTYPE_LNK, +#endif +#ifdef S_IFIFO + FTYPE_FIFO, +#endif +#ifdef S_IFSOCK + FTYPE_SOCK, +#endif +#ifdef S_IFDOOR + FTYPE_DOOR, +#endif + FTYPE_COUNT + }; enum xval { @@ -305,7 +326,7 @@ struct predicate struct time_val reftime; /* newer newerXY anewer cnewer mtime atime ctime mmin amin cmin */ struct perm_val perm; /* perm */ struct samefile_file_id samefileid; /* samefile */ - mode_t type; /* type */ + bool types[FTYPE_COUNT]; /* file type(s) */ struct format_val printf_vec; /* printf fprintf fprint ls fls print0 fprint0 print */ security_context_t scontext; /* security context */ } args; diff --git a/find/find.1 b/find/find.1 index c948b4b..7b626b5 100644 --- a/find/find.1 +++ b/find/find.1 @@ -954,6 +954,9 @@ socket .IP D door (Solaris) .RE +.IP +To search for more than one type at once, you can supply the combined list of +type letters separated by a comma `,' (GNU extension). .IP "\-uid \fIn\fR" File's numeric user ID is \fIn\fR. @@ -1653,6 +1656,8 @@ previous versions of findutils. .IP \fB\-type\fR Supported. POSIX specifies `b', `c', `d', `l', `p', `f' and `s'. GNU find also supports `D', representing a Door, where the OS provides these. +Furthermore, GNU find allows multiple types to be specified at once in a +comma-separated list. .IP \fB\-ok\fR Supported. @@ -2087,6 +2092,20 @@ discovered (for example we do not search project3/src because we already found project3/.svn), but ensures sibling directories (project2 and project3) are found. +.P +.nf +.B find /tmp -type f,d,l +.fi + +Search for files, directories, and symbolic links in the directory +.B /tmp +passing these types as a comma-separated list (GNU extension), +which is otherwise equivalent to the longer, yet more portable: + +.nf +.B find /tmp \e( -type f -o -type d -o -type l \e) +.fi + .SH EXIT STATUS .PP .B find diff --git a/find/parser.c b/find/parser.c index 57fb296..6ea6e05 100644 --- a/find/parser.c +++ b/find/parser.c @@ -2679,131 +2679,171 @@ insert_type (char **argv, int *arg_ptr, PRED_FUNC which_pred) { struct predicate *our_pred; - float rate = 0.01; + float type_rate = 0.0; const char *typeletter; + const char *pred_string = which_pred == pred_xtype ? "-xtype" : "-type"; - if (collect_arg (argv, arg_ptr, &typeletter)) + if (! collect_arg (argv, arg_ptr, &typeletter)) + return false; + + if (!*typeletter) { - if (strlen (typeletter) != 1u) - { - error (EXIT_FAILURE, 0, - _("Arguments to -type should contain only one letter")); - /*NOTREACHED*/ - return false; - } + error (EXIT_FAILURE, 0, + _("Arguments to %s should contain at least one letter"), + pred_string); + /*NOTREACHED*/ + return false; + } - /* From a real system here are the counts of files by type: - Type Count Fraction - f 4410884 0.875 - d 464722 0.0922 - l 156662 0.0311 - b 4476 0.000888 - c 2233 0.000443 - s 80 1.59e-05 - p 38 7.54e-06 - */ - { - mode_t type_cell; + our_pred = insert_primary_withpred (entry, which_pred, typeletter); - switch (typeletter[0]) - { - case 'b': /* block special */ - type_cell = S_IFBLK; - rate = 0.000888f; - break; - case 'c': /* character special */ - type_cell = S_IFCHR; - rate = 0.000443f; - break; - case 'd': /* directory */ - type_cell = S_IFDIR; - rate = 0.0922f; - break; - case 'f': /* regular file */ - type_cell = S_IFREG; - rate = 0.875f; - break; - case 'l': /* symbolic link */ + /* Figure out if we will need to stat the file, because if we don't + * need to follow symlinks, we can avoid a stat call by using + * struct dirent.d_type. + */ + if (which_pred == pred_xtype) + { + our_pred->need_stat = true; + our_pred->need_type = false; + } + else + { + our_pred->need_stat = false; /* struct dirent is enough */ + our_pred->need_type = true; + } + + /* From a real system here are the counts of files by type: + Type Count Fraction + f 4410884 0.875 + d 464722 0.0922 + l 156662 0.0311 + b 4476 0.000888 + c 2233 0.000443 + s 80 1.59e-05 + p 38 7.54e-06 + */ + + for (; *typeletter; ) + { + unsigned int type_cell; + float rate = 0.01; + + switch (*typeletter) + { + case 'b': /* block special */ + type_cell = FTYPE_BLK; + rate = 0.000888f; + break; + case 'c': /* character special */ + type_cell = FTYPE_CHR; + rate = 0.000443f; + break; + case 'd': /* directory */ + type_cell = FTYPE_DIR; + rate = 0.0922f; + break; + case 'f': /* regular file */ + type_cell = FTYPE_REG; + rate = 0.875f; + break; + case 'l': /* symbolic link */ #ifdef S_IFLNK - type_cell = S_IFLNK; - rate = 0.0311f; + type_cell = FTYPE_LNK; + rate = 0.0311f; #else - type_cell = 0; - error (EXIT_FAILURE, 0, - _("-type %c is not supported because symbolic links " - "are not supported on the platform find was compiled on."), - (*typeletter)); + type_cell = 0; + error (EXIT_FAILURE, 0, + _("%s %c is not supported because symbolic links " + "are not supported on the platform find was compiled on."), + pred_string, (*typeletter)); #endif - break; - case 'p': /* pipe */ + break; + case 'p': /* pipe */ #ifdef S_IFIFO - type_cell = S_IFIFO; - rate = 7.554e-6f; + type_cell = FTYPE_FIFO; + rate = 7.554e-6f; #else - type_cell = 0; - error (EXIT_FAILURE, 0, - _("-type %c is not supported because FIFOs " - "are not supported on the platform find was compiled on."), - (*typeletter)); + type_cell = 0; + error (EXIT_FAILURE, 0, + _("%s %c is not supported because FIFOs " + "are not supported on the platform find was compiled on."), + pred_string, (*typeletter)); #endif - break; - case 's': /* socket */ + break; + case 's': /* socket */ #ifdef S_IFSOCK - type_cell = S_IFSOCK; - rate = 1.59e-5f; + type_cell = FTYPE_SOCK; + rate = 1.59e-5f; #else - type_cell = 0; - error (EXIT_FAILURE, 0, - _("-type %c is not supported because named sockets " - "are not supported on the platform find was compiled on."), - (*typeletter)); + type_cell = 0; + error (EXIT_FAILURE, 0, + _("%s %c is not supported because named sockets " + "are not supported on the platform find was compiled on."), + pred_string, (*typeletter)); #endif - break; - case 'D': /* Solaris door */ + break; + case 'D': /* Solaris door */ #ifdef S_IFDOOR - type_cell = S_IFDOOR; - /* There are no Solaris doors on the example system surveyed - * above, but if someone uses -type D, they are presumably - * expecting to find a non-zero number. We guess at a - * rate. */ - rate = 1.0e-5f; + type_cell = FTYPE_DOOR; + /* There are no Solaris doors on the example system surveyed + * above, but if someone uses -type D, they are presumably + * expecting to find a non-zero number. We guess at a + * rate. */ + rate = 1.0e-5f; #else - type_cell = 0; - error (EXIT_FAILURE, 0, - _("-type %c is not supported because Solaris doors " - "are not supported on the platform find was compiled on."), - (*typeletter)); + type_cell = 0; + error (EXIT_FAILURE, 0, + _("%s %c is not supported because Solaris doors " + "are not supported on the platform find was compiled on."), + pred_string, (*typeletter)); #endif - break; - default: /* None of the above ... nuke 'em. */ - type_cell = 0; - error (EXIT_FAILURE, 0, - _("Unknown argument to -type: %c"), (*typeletter)); - /*NOTREACHED*/ - return false; - } - our_pred = insert_primary_withpred (entry, which_pred, typeletter); - our_pred->est_success_rate = rate; - - /* Figure out if we will need to stat the file, because if we don't - * need to follow symlinks, we can avoid a stat call by using - * struct dirent.d_type. - */ - if (which_pred == pred_xtype) - { - our_pred->need_stat = true; - our_pred->need_type = false; - } - else - { - our_pred->need_stat = false; /* struct dirent is enough */ - our_pred->need_type = true; - } - our_pred->args.type = type_cell; + break; + default: /* None of the above ... nuke 'em. */ + type_cell = 0; + error (EXIT_FAILURE, 0, + _("Unknown argument to %s: %c"), pred_string, (*typeletter)); + /*NOTREACHED*/ + return false; } - return true; + + if (our_pred->args.types[type_cell]) + { + error (EXIT_FAILURE, 0, + _("Duplicate file type '%c' in the argument list to %s."), + (*typeletter), pred_string); + } + + our_pred->est_success_rate += rate; + our_pred->args.types[type_cell] = true; + + /* Advance. + * Currently, only 1-character file types separated by ',' are supported. + */ + typeletter++; + if (*typeletter) + { + if (*typeletter != ',') + { + error (EXIT_FAILURE, 0, + _("Must separate multiple arguments to %s using: ','"), + pred_string); + /*NOTREACHED*/ + return false; + } + typeletter++; + if (!*typeletter) + { + error (EXIT_FAILURE, 0, + _("Last file type in list argument to %s " + "is missing, i.e., list is ending on: ','"), + pred_string); + /*NOTREACHED*/ + return false; + } + } } - return false; + + return true; } diff --git a/find/pred.c b/find/pred.c index d633ab9..f7e9b59 100644 --- a/find/pred.c +++ b/find/pred.c @@ -1032,7 +1032,7 @@ bool pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr) { mode_t mode; - mode_t type = pred_ptr->args.type; + enum file_type type = FTYPE_COUNT; assert (state.have_type); @@ -1052,31 +1052,76 @@ pred_type (const char *pathname, struct stat *stat_buf, struct predicate *pred_p mode = state.type; #ifndef S_IFMT - /* POSIX system; check `mode' the slow way. */ - if ((S_ISBLK (mode) && type == S_IFBLK) - || (S_ISCHR (mode) && type == S_IFCHR) - || (S_ISDIR (mode) && type == S_IFDIR) - || (S_ISREG (mode) && type == S_IFREG) + /* POSIX system; check `mode' the slow way. + * Search in the order of probability (f,d,l,b,c,s,p,D). + */ + if (S_ISREG (mode)) + type = FTYPE_REG; + else if (S_ISDIR (mode)) + type = FTYPE_DIR; #ifdef S_IFLNK - || (S_ISLNK (mode) && type == S_IFLNK) -#endif -#ifdef S_IFIFO - || (S_ISFIFO (mode) && type == S_IFIFO) + else if (S_ISLNK (mode)) + type = FTYPE_LNK; #endif + else if (S_ISBLK (mode)) + type = FTYPE_BLK; + else if (S_ISCHR (mode)) + type = FTYPE_CHR; #ifdef S_IFSOCK - || (S_ISSOCK (mode) && type == S_IFSOCK) + else if (S_ISSOCK (mode)) + type = FTYPE_SOCK; +#endif +#ifdef S_IFIFO + else if (S_ISFIFO (mode)) + type = FTYPE_FIFO; #endif #ifdef S_IFDOOR - || (S_ISDOOR (mode) && type == S_IFDOOR) + else if (S_ISDOOR (mode)) + type = FTYPE_DOOR; #endif - ) #else /* S_IFMT */ /* Unix system; check `mode' the fast way. */ - if ((mode & S_IFMT) == type) + switch (mode & S_IFMT) + { + case S_IFREG: + type = FTYPE_REG; + break; + case S_IFDIR: + type = FTYPE_DIR; + break; +#ifdef S_IFLNK + case S_IFLNK: + type = FTYPE_LNK; + break; +#endif + case S_IFBLK: + type = FTYPE_BLK; + break; + case S_IFCHR: + type = FTYPE_CHR; + break; +#ifdef S_IFSOCK + case S_IFSOCK: + type = FTYPE_SOCK; + break; +#endif +#ifdef S_IFIFO + case S_IFIFO: + type = FTYPE_FIFO; + break; +#endif +#ifdef S_IFDOOR + case S_IFDOOR: + type = FTYPE_DOOR; + break; +#endif + } #endif /* S_IFMT */ - return (true); + + if ((type != FTYPE_COUNT) && pred_ptr->args.types[type]) + return true; else - return (false); + return false; } bool diff --git a/find/testsuite/Makefile.am b/find/testsuite/Makefile.am index 228957f..65d5d32 100644 --- a/find/testsuite/Makefile.am +++ b/find/testsuite/Makefile.am @@ -257,6 +257,7 @@ sv-bug-32043.sh \ test_escapechars.sh \ test_escape_c.sh \ test_inode.sh \ +test_type-list.sh \ sv-34079.sh \ sv-34976-execdir-fd-leak.sh diff --git a/find/testsuite/test_type-list.sh b/find/testsuite/test_type-list.sh new file mode 100755 index 0000000..c500b02 --- /dev/null +++ b/find/testsuite/test_type-list.sh @@ -0,0 +1,340 @@ +#! /bin/sh +# Copyright (C) 2016 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 3 of the License, 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, see