aboutsummaryrefslogtreecommitdiff
path: root/findutils
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2012-03-22 13:15:08 +0000
committerRon Yorston <rmy@pobox.com>2012-03-22 13:15:08 +0000
commitc0d4367d6b581eb5989c02815880cf0fa2851ae8 (patch)
tree868c266e627e2d7f65ba5a4d5f98a1c421453181 /findutils
parentf6bad5ef766b0447158e3de2f55c35f1f6cecb58 (diff)
parentda4441c44f6efccb6f7b7588404d9c6bfb7b6af8 (diff)
downloadbusybox-w32-c0d4367d6b581eb5989c02815880cf0fa2851ae8.tar.gz
busybox-w32-c0d4367d6b581eb5989c02815880cf0fa2851ae8.tar.bz2
busybox-w32-c0d4367d6b581eb5989c02815880cf0fa2851ae8.zip
Merge commit 'da4441c44f6efccb6f7b7588404d9c6bfb7b6af8' into merge
Conflicts: libbb/vfork_daemon_rexec.c networking/wget.c procps/ps.c
Diffstat (limited to 'findutils')
-rw-r--r--findutils/find.c419
1 files changed, 197 insertions, 222 deletions
diff --git a/findutils/find.c b/findutils/find.c
index f85381b47..9ae84fa0d 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -53,10 +53,6 @@
53 * diff -u /tmp/std_find /tmp/bb_find && echo Identical 53 * diff -u /tmp/std_find /tmp/bb_find && echo Identical
54 */ 54 */
55 55
56//applet:IF_FIND(APPLET_NOEXEC(find, find, BB_DIR_USR_BIN, BB_SUID_DROP, find))
57
58//kbuild:lib-$(CONFIG_FIND) += find.o
59
60//config:config FIND 56//config:config FIND
61//config: bool "find" 57//config: bool "find"
62//config: default y 58//config: default y
@@ -112,11 +108,11 @@
112//config: This option allows find to restrict searches to a single filesystem. 108//config: This option allows find to restrict searches to a single filesystem.
113//config: 109//config:
114//config:config FEATURE_FIND_MAXDEPTH 110//config:config FEATURE_FIND_MAXDEPTH
115//config: bool "Enable -maxdepth N" 111//config: bool "Enable -mindepth N and -maxdepth N"
116//config: default y 112//config: default y
117//config: depends on FIND 113//config: depends on FIND
118//config: help 114//config: help
119//config: This option enables -maxdepth N option. 115//config: This option enables -mindepth N and -maxdepth N option.
120//config: 116//config:
121//config:config FEATURE_FIND_NEWER 117//config:config FEATURE_FIND_NEWER
122//config: bool "Enable -newer: compare file modification times" 118//config: bool "Enable -newer: compare file modification times"
@@ -124,7 +120,7 @@
124//config: depends on FIND 120//config: depends on FIND
125//config: help 121//config: help
126//config: Support the 'find -newer' option for finding any files which have 122//config: Support the 'find -newer' option for finding any files which have
127//config: a modified time that is more recent than the specified FILE. 123//config: modification time that is more recent than the specified FILE.
128//config: 124//config:
129//config:config FEATURE_FIND_INUM 125//config:config FEATURE_FIND_INUM
130//config: bool "Enable -inum: inode number matching" 126//config: bool "Enable -inum: inode number matching"
@@ -230,6 +226,106 @@
230//config: help 226//config: help
231//config: Support the 'find -links' option for matching number of links. 227//config: Support the 'find -links' option for matching number of links.
232 228
229//applet:IF_FIND(APPLET_NOEXEC(find, find, BB_DIR_USR_BIN, BB_SUID_DROP, find))
230
231//kbuild:lib-$(CONFIG_FIND) += find.o
232
233//usage:#define find_trivial_usage
234//usage: "[PATH]... [OPTIONS] [ACTIONS]"
235//usage:#define find_full_usage "\n\n"
236//usage: "Search for files and perform actions on them.\n"
237//usage: "First failed action stops processing of current file.\n"
238//usage: "Defaults: PATH is current directory, action is '-print'\n"
239//usage: "\nOptions:"
240//usage: "\n -follow Follow symlinks"
241//usage: IF_FEATURE_FIND_XDEV(
242//usage: "\n -xdev Don't descend directories on other filesystems"
243//usage: )
244//usage: IF_FEATURE_FIND_MAXDEPTH(
245//usage: "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies"
246//usage: "\n actions to command line arguments only"
247//usage: "\n -mindepth N Don't act on first N levels"
248//usage: )
249//usage: IF_FEATURE_FIND_DEPTH(
250//usage: "\n -depth Act on directory *after* traversing it"
251//usage: )
252//usage: "\n"
253//usage: "\nActions:"
254//usage: IF_FEATURE_FIND_PAREN(
255//usage: "\n ( ACTIONS ) Group actions for -o / -a"
256//usage: )
257//usage: IF_FEATURE_FIND_NOT(
258//usage: "\n ! ACT Invert ACT's success/failure"
259//usage: )
260//usage: "\n ACT1 [-a] ACT2 If ACT1 fails, stop, else do ACT2"
261//usage: "\n ACT1 -o ACT2 If ACT1 succeeds, stop, else do ACT2"
262//usage: "\n Note: -a has higher priority than -o"
263//usage: "\n -name PATTERN Match file name (w/o directory name) to PATTERN"
264//usage: "\n -iname PATTERN Case insensitive -name"
265//usage: IF_FEATURE_FIND_PATH(
266//usage: "\n -path PATTERN Match path to PATTERN"
267//usage: )
268//usage: IF_FEATURE_FIND_REGEX(
269//usage: "\n -regex PATTERN Match path to regex PATTERN"
270//usage: )
271//usage: IF_FEATURE_FIND_TYPE(
272//usage: "\n -type X File type is X (one of: f,d,l,b,c,...)"
273//usage: )
274//usage: IF_FEATURE_FIND_PERM(
275//usage: "\n -perm MASK At least one mask bit (+MASK), all bits (-MASK),"
276//usage: "\n or exactly MASK bits are set in file's mode"
277//usage: )
278//usage: IF_FEATURE_FIND_MTIME(
279//usage: "\n -mtime DAYS mtime is greater than (+N), less than (-N),"
280//usage: "\n or exactly N days in the past"
281//usage: )
282//usage: IF_FEATURE_FIND_MMIN(
283//usage: "\n -mmin MINS mtime is greater than (+N), less than (-N),"
284//usage: "\n or exactly N minutes in the past"
285//usage: )
286//usage: IF_FEATURE_FIND_NEWER(
287//usage: "\n -newer FILE mtime is more recent than FILE's"
288//usage: )
289//usage: IF_FEATURE_FIND_INUM(
290//usage: "\n -inum N File has inode number N"
291//usage: )
292//usage: IF_FEATURE_FIND_USER(
293//usage: "\n -user NAME/ID File is owned by given user"
294//usage: )
295//usage: IF_FEATURE_FIND_GROUP(
296//usage: "\n -group NAME/ID File is owned by given group"
297//usage: )
298//usage: IF_FEATURE_FIND_SIZE(
299//usage: "\n -size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
300//usage: "\n +/-N: file size is bigger/smaller than N"
301//usage: )
302//usage: IF_FEATURE_FIND_LINKS(
303//usage: "\n -links N Number of links is greater than (+N), less than (-N),"
304//usage: "\n or exactly N"
305//usage: )
306//usage: IF_FEATURE_FIND_CONTEXT(
307//usage: "\n -context CTX File has specified security context"
308//usage: )
309//usage: IF_FEATURE_FIND_PRUNE(
310//usage: "\n -prune If current file is directory, don't descend into it"
311//usage: )
312//usage: "\nIf none of the following actions is specified, -print is assumed"
313//usage: "\n -print Print file name"
314//usage: IF_FEATURE_FIND_PRINT0(
315//usage: "\n -print0 Print file name, NUL terminated"
316//usage: )
317//usage: IF_FEATURE_FIND_EXEC(
318//usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by"
319//usage: "\n file name. Fails if CMD exits with nonzero"
320//usage: )
321//usage: IF_FEATURE_FIND_DELETE(
322//usage: "\n -delete Delete current file/directory. Turns on -depth option"
323//usage: )
324//usage:
325//usage:#define find_example_usage
326//usage: "$ find / -name passwd\n"
327//usage: "/etc/passwd\n"
328
233#include <fnmatch.h> 329#include <fnmatch.h>
234#include "libbb.h" 330#include "libbb.h"
235#if ENABLE_FEATURE_FIND_REGEX 331#if ENABLE_FEATURE_FIND_REGEX
@@ -278,8 +374,12 @@ IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;))
278struct globals { 374struct globals {
279 IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;) 375 IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;)
280 IF_FEATURE_FIND_XDEV(int xdev_count;) 376 IF_FEATURE_FIND_XDEV(int xdev_count;)
377#if ENABLE_FEATURE_FIND_MAXDEPTH
378 int minmaxdepth[2];
379#endif
281 action ***actions; 380 action ***actions;
282 bool need_print; 381 smallint need_print;
382 smallint xdev_on;
283 recurse_flags_t recurse_flags; 383 recurse_flags_t recurse_flags;
284} FIX_ALIASING; 384} FIX_ALIASING;
285#define G (*(struct globals*)&bb_common_bufsiz1) 385#define G (*(struct globals*)&bb_common_bufsiz1)
@@ -288,7 +388,8 @@ struct globals {
288 char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ 388 char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
289 }; \ 389 }; \
290 /* we have to zero it out because of NOEXEC */ \ 390 /* we have to zero it out because of NOEXEC */ \
291 memset(&G, 0, offsetof(struct globals, need_print)); \ 391 memset(&G, 0, sizeof(G)); \
392 IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \
292 G.need_print = 1; \ 393 G.need_print = 1; \
293 G.recurse_flags = ACTION_RECURSE; \ 394 G.recurse_flags = ACTION_RECURSE; \
294} while (0) 395} while (0)
@@ -586,16 +687,15 @@ ACTF(links)
586 687
587static int FAST_FUNC fileAction(const char *fileName, 688static int FAST_FUNC fileAction(const char *fileName,
588 struct stat *statbuf, 689 struct stat *statbuf,
589 void *userData IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM), 690 void *userData UNUSED_PARAM,
590 int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM)) 691 int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM))
591{ 692{
592 int r; 693 int r;
593#if ENABLE_FEATURE_FIND_MAXDEPTH
594#define minmaxdepth ((int*)userData)
595 694
596 if (depth < minmaxdepth[0]) 695#if ENABLE_FEATURE_FIND_MAXDEPTH
696 if (depth < G.minmaxdepth[0])
597 return TRUE; /* skip this, continue recursing */ 697 return TRUE; /* skip this, continue recursing */
598 if (depth > minmaxdepth[1]) 698 if (depth > G.minmaxdepth[1])
599 return SKIP; /* stop recursing */ 699 return SKIP; /* stop recursing */
600#endif 700#endif
601 701
@@ -606,7 +706,7 @@ static int FAST_FUNC fileAction(const char *fileName,
606 706
607#if ENABLE_FEATURE_FIND_MAXDEPTH 707#if ENABLE_FEATURE_FIND_MAXDEPTH
608 if (S_ISDIR(statbuf->st_mode)) { 708 if (S_ISDIR(statbuf->st_mode)) {
609 if (depth == minmaxdepth[1]) 709 if (depth == G.minmaxdepth[1])
610 return SKIP; 710 return SKIP;
611 } 711 }
612#endif 712#endif
@@ -629,7 +729,6 @@ static int FAST_FUNC fileAction(const char *fileName,
629 /* Cannot return 0: our caller, recursive_action(), 729 /* Cannot return 0: our caller, recursive_action(),
630 * will perror() and skip dirs (if called on dir) */ 730 * will perror() and skip dirs (if called on dir) */
631 return (r & SKIP) ? SKIP : TRUE; 731 return (r & SKIP) ? SKIP : TRUE;
632#undef minmaxdepth
633} 732}
634 733
635 734
@@ -674,6 +773,9 @@ static const char* plus_minus_num(const char* str)
674static action*** parse_params(char **argv) 773static action*** parse_params(char **argv)
675{ 774{
676 enum { 775 enum {
776 OPT_FOLLOW ,
777 IF_FEATURE_FIND_XDEV( OPT_XDEV ,)
778 IF_FEATURE_FIND_DEPTH( OPT_DEPTH ,)
677 PARM_a , 779 PARM_a ,
678 PARM_o , 780 PARM_o ,
679 IF_FEATURE_FIND_NOT( PARM_char_not ,) 781 IF_FEATURE_FIND_NOT( PARM_char_not ,)
@@ -684,12 +786,11 @@ static action*** parse_params(char **argv)
684#endif 786#endif
685 PARM_print , 787 PARM_print ,
686 IF_FEATURE_FIND_PRINT0( PARM_print0 ,) 788 IF_FEATURE_FIND_PRINT0( PARM_print0 ,)
687 IF_FEATURE_FIND_DEPTH( PARM_depth ,)
688 IF_FEATURE_FIND_PRUNE( PARM_prune ,) 789 IF_FEATURE_FIND_PRUNE( PARM_prune ,)
689 IF_FEATURE_FIND_DELETE( PARM_delete ,) 790 IF_FEATURE_FIND_DELETE( PARM_delete ,)
690 IF_FEATURE_FIND_EXEC( PARM_exec ,) 791 IF_FEATURE_FIND_EXEC( PARM_exec ,)
691 IF_FEATURE_FIND_PAREN( PARM_char_brace,) 792 IF_FEATURE_FIND_PAREN( PARM_char_brace,)
692 /* All options starting from here require argument */ 793 /* All options/actions starting from here require argument */
693 PARM_name , 794 PARM_name ,
694 PARM_iname , 795 PARM_iname ,
695 IF_FEATURE_FIND_PATH( PARM_path ,) 796 IF_FEATURE_FIND_PATH( PARM_path ,)
@@ -705,25 +806,28 @@ static action*** parse_params(char **argv)
705 IF_FEATURE_FIND_SIZE( PARM_size ,) 806 IF_FEATURE_FIND_SIZE( PARM_size ,)
706 IF_FEATURE_FIND_CONTEXT(PARM_context ,) 807 IF_FEATURE_FIND_CONTEXT(PARM_context ,)
707 IF_FEATURE_FIND_LINKS( PARM_links ,) 808 IF_FEATURE_FIND_LINKS( PARM_links ,)
809 IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,OPT_MAXDEPTH,)
708 }; 810 };
709 811
710 static const char params[] ALIGN1 = 812 static const char params[] ALIGN1 =
711 "-a\0" 813 "-follow\0"
712 "-o\0" 814 IF_FEATURE_FIND_XDEV( "-xdev\0" )
815 IF_FEATURE_FIND_DEPTH( "-depth\0" )
816 "-a\0"
817 "-o\0"
713 IF_FEATURE_FIND_NOT( "!\0" ) 818 IF_FEATURE_FIND_NOT( "!\0" )
714#if ENABLE_DESKTOP 819#if ENABLE_DESKTOP
715 "-and\0" 820 "-and\0"
716 "-or\0" 821 "-or\0"
717 IF_FEATURE_FIND_NOT( "-not\0" ) 822 IF_FEATURE_FIND_NOT( "-not\0" )
718#endif 823#endif
719 "-print\0" 824 "-print\0"
720 IF_FEATURE_FIND_PRINT0( "-print0\0" ) 825 IF_FEATURE_FIND_PRINT0( "-print0\0" )
721 IF_FEATURE_FIND_DEPTH( "-depth\0" )
722 IF_FEATURE_FIND_PRUNE( "-prune\0" ) 826 IF_FEATURE_FIND_PRUNE( "-prune\0" )
723 IF_FEATURE_FIND_DELETE( "-delete\0" ) 827 IF_FEATURE_FIND_DELETE( "-delete\0" )
724 IF_FEATURE_FIND_EXEC( "-exec\0" ) 828 IF_FEATURE_FIND_EXEC( "-exec\0" )
725 IF_FEATURE_FIND_PAREN( "(\0" ) 829 IF_FEATURE_FIND_PAREN( "(\0" )
726 /* All options starting from here require argument */ 830 /* All options/actions starting from here require argument */
727 "-name\0" 831 "-name\0"
728 "-iname\0" 832 "-iname\0"
729 IF_FEATURE_FIND_PATH( "-path\0" ) 833 IF_FEATURE_FIND_PATH( "-path\0" )
@@ -739,7 +843,8 @@ static action*** parse_params(char **argv)
739 IF_FEATURE_FIND_SIZE( "-size\0" ) 843 IF_FEATURE_FIND_SIZE( "-size\0" )
740 IF_FEATURE_FIND_CONTEXT("-context\0") 844 IF_FEATURE_FIND_CONTEXT("-context\0")
741 IF_FEATURE_FIND_LINKS( "-links\0" ) 845 IF_FEATURE_FIND_LINKS( "-links\0" )
742 ; 846 IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
847 ;
743 848
744 action*** appp; 849 action*** appp;
745 unsigned cur_group = 0; 850 unsigned cur_group = 0;
@@ -766,27 +871,13 @@ static action*** parse_params(char **argv)
766 871
767 appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */ 872 appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */
768 873
769/* Actions have side effects and return a true or false value
770 * We implement: -print, -print0, -exec
771 *
772 * The rest are tests.
773 *
774 * Tests and actions are grouped by operators
775 * ( expr ) Force precedence
776 * ! expr True if expr is false
777 * -not expr Same as ! expr
778 * expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false
779 * expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true
780 * expr1 , expr2 List; both expr1 and expr2 are always evaluated
781 * We implement: (), -a, -o
782 */
783 while (*argv) { 874 while (*argv) {
784 const char *arg = argv[0]; 875 const char *arg = argv[0];
785 int parm = index_in_strings(params, arg); 876 int parm = index_in_strings(params, arg);
786 const char *arg1 = argv[1]; 877 const char *arg1 = argv[1];
787 878
788 if (parm >= PARM_name) { 879 if (parm >= PARM_name) {
789 /* All options starting from -name require argument */ 880 /* All options/actions starting from -name require argument */
790 if (!arg1) 881 if (!arg1)
791 bb_error_msg_and_die(bb_msg_requires_arg, arg); 882 bb_error_msg_and_die(bb_msg_requires_arg, arg);
792 argv++; 883 argv++;
@@ -795,8 +886,37 @@ static action*** parse_params(char **argv)
795 /* We can use big switch() here, but on i386 886 /* We can use big switch() here, but on i386
796 * it doesn't give smaller code. Other arches? */ 887 * it doesn't give smaller code. Other arches? */
797 888
798 /* --- Operators --- */ 889/* Options always return true. They always take effect
799 if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) { 890 * rather than being processed only when their place in the
891 * expression is reached.
892 */
893 /* Options */
894#if ENABLE_FEATURE_FIND_XDEV
895 if (parm == OPT_XDEV) {
896 G.xdev_on = 1;
897 }
898#endif
899#if ENABLE_FEATURE_FIND_MAXDEPTH
900 else if (parm == OPT_MINDEPTH || parm == OPT_MINDEPTH + 1) {
901 G.minmaxdepth[parm - OPT_MINDEPTH] = xatoi_positive(arg1);
902 }
903#endif
904#if ENABLE_FEATURE_FIND_DEPTH
905 else if (parm == OPT_DEPTH) {
906 G.recurse_flags |= ACTION_DEPTHFIRST;
907 }
908#endif
909/* Actions are grouped by operators
910 * ( expr ) Force precedence
911 * ! expr True if expr is false
912 * -not expr Same as ! expr
913 * expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false
914 * expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true
915 * expr1 , expr2 List; both expr1 and expr2 are always evaluated
916 * We implement: (), -a, -o
917 */
918 /* Operators */
919 else if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
800 /* no further special handling required */ 920 /* no further special handling required */
801 } 921 }
802 else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) { 922 else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
@@ -813,29 +933,19 @@ static action*** parse_params(char **argv)
813 invert_flag ^= 1; 933 invert_flag ^= 1;
814 } 934 }
815#endif 935#endif
816 936 /* Actions */
817 /* --- Tests and actions --- */
818 else if (parm == PARM_print) { 937 else if (parm == PARM_print) {
819 G.need_print = 0; 938 G.need_print = 0;
820 /* GNU find ignores '!' here: "find ! -print" */
821 IF_FEATURE_FIND_NOT( invert_flag = 0; )
822 (void) ALLOC_ACTION(print); 939 (void) ALLOC_ACTION(print);
823 } 940 }
824#if ENABLE_FEATURE_FIND_PRINT0 941#if ENABLE_FEATURE_FIND_PRINT0
825 else if (parm == PARM_print0) { 942 else if (parm == PARM_print0) {
826 G.need_print = 0; 943 G.need_print = 0;
827 IF_FEATURE_FIND_NOT( invert_flag = 0; )
828 (void) ALLOC_ACTION(print0); 944 (void) ALLOC_ACTION(print0);
829 } 945 }
830#endif 946#endif
831#if ENABLE_FEATURE_FIND_DEPTH
832 else if (parm == PARM_depth) {
833 G.recurse_flags |= ACTION_DEPTHFIRST;
834 }
835#endif
836#if ENABLE_FEATURE_FIND_PRUNE 947#if ENABLE_FEATURE_FIND_PRUNE
837 else if (parm == PARM_prune) { 948 else if (parm == PARM_prune) {
838 IF_FEATURE_FIND_NOT( invert_flag = 0; )
839 (void) ALLOC_ACTION(prune); 949 (void) ALLOC_ACTION(prune);
840 } 950 }
841#endif 951#endif
@@ -851,7 +961,6 @@ static action*** parse_params(char **argv)
851 int i; 961 int i;
852 action_exec *ap; 962 action_exec *ap;
853 G.need_print = 0; 963 G.need_print = 0;
854 IF_FEATURE_FIND_NOT( invert_flag = 0; )
855 ap = ALLOC_ACTION(exec); 964 ap = ALLOC_ACTION(exec);
856 ap->exec_argv = ++argv; /* first arg after -exec */ 965 ap->exec_argv = ++argv; /* first arg after -exec */
857 /*ap->exec_argc = 0; - ALLOC_ACTION did it */ 966 /*ap->exec_argc = 0; - ALLOC_ACTION did it */
@@ -859,9 +968,9 @@ static action*** parse_params(char **argv)
859 if (!*argv) /* did not see ';' or '+' until end */ 968 if (!*argv) /* did not see ';' or '+' until end */
860 bb_error_msg_and_die(bb_msg_requires_arg, "-exec"); 969 bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
861 // find -exec echo Foo ">{}<" ";" 970 // find -exec echo Foo ">{}<" ";"
862 // executes "echo Foo <filename>", 971 // executes "echo Foo >FILENAME<",
863 // find -exec echo Foo ">{}<" "+" 972 // find -exec echo Foo ">{}<" "+"
864 // executes "echo Foo <filename1> <filename2> <filename3>...". 973 // executes "echo Foo FILENAME1 FILENAME2 FILENAME3...".
865 // TODO (so far we treat "+" just like ";") 974 // TODO (so far we treat "+" just like ";")
866 if ((argv[0][0] == ';' || argv[0][0] == '+') 975 if ((argv[0][0] == ';' || argv[0][0] == '+')
867 && argv[0][1] == '\0' 976 && argv[0][1] == '\0'
@@ -930,10 +1039,10 @@ static action*** parse_params(char **argv)
930 } 1039 }
931#endif 1040#endif
932#if ENABLE_FEATURE_FIND_PERM 1041#if ENABLE_FEATURE_FIND_PERM
933/* -perm mode File's permission bits are exactly mode (octal or symbolic). 1042/* -perm BITS File's mode bits are exactly BITS (octal or symbolic).
934 * Symbolic modes use mode 0 as a point of departure. 1043 * Symbolic modes use mode 0 as a point of departure.
935 * -perm -mode All of the permission bits mode are set for the file. 1044 * -perm -BITS All of the BITS are set in file's mode.
936 * -perm +mode Any of the permission bits mode are set for the file. 1045 * -perm +BITS At least one of the BITS is set in file's mode.
937 */ 1046 */
938 else if (parm == PARM_perm) { 1047 else if (parm == PARM_perm) {
939 action_perm *ap; 1048 action_perm *ap;
@@ -1050,193 +1159,59 @@ static action*** parse_params(char **argv)
1050#undef ALLOC_ACTION 1159#undef ALLOC_ACTION
1051} 1160}
1052 1161
1053//usage:#define find_trivial_usage
1054//usage: "[PATH]... [EXPRESSION]"
1055//usage:#define find_full_usage "\n\n"
1056//usage: "Search for files. The default PATH is the current directory,\n"
1057//usage: "default EXPRESSION is '-print'\n"
1058//usage: "\nEXPRESSION may consist of:"
1059//usage: "\n -follow Follow symlinks"
1060//usage: IF_FEATURE_FIND_XDEV(
1061//usage: "\n -xdev Don't descend directories on other filesystems"
1062//usage: )
1063//usage: IF_FEATURE_FIND_MAXDEPTH(
1064//usage: "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies"
1065//usage: "\n tests/actions to command line arguments only"
1066//usage: )
1067//usage: "\n -mindepth N Don't act on first N levels"
1068//usage: "\n -name PATTERN File name (w/o directory name) matches PATTERN"
1069//usage: "\n -iname PATTERN Case insensitive -name"
1070//usage: IF_FEATURE_FIND_PATH(
1071//usage: "\n -path PATTERN Path matches PATTERN"
1072//usage: )
1073//usage: IF_FEATURE_FIND_REGEX(
1074//usage: "\n -regex PATTERN Path matches regex PATTERN"
1075//usage: )
1076//usage: IF_FEATURE_FIND_TYPE(
1077//usage: "\n -type X File type is X (X is one of: f,d,l,b,c,...)"
1078//usage: )
1079//usage: IF_FEATURE_FIND_PERM(
1080//usage: "\n -perm NNN Permissions match any of (+NNN), all of (-NNN),"
1081//usage: "\n or exactly NNN"
1082//usage: )
1083//usage: IF_FEATURE_FIND_MTIME(
1084//usage: "\n -mtime DAYS Modified time is greater than (+N), less than (-N),"
1085//usage: "\n or exactly N days"
1086//usage: )
1087//usage: IF_FEATURE_FIND_MMIN(
1088//usage: "\n -mmin MINS Modified time is greater than (+N), less than (-N),"
1089//usage: "\n or exactly N minutes"
1090//usage: )
1091//usage: IF_FEATURE_FIND_NEWER(
1092//usage: "\n -newer FILE Modified time is more recent than FILE's"
1093//usage: )
1094//usage: IF_FEATURE_FIND_INUM(
1095//usage: "\n -inum N File has inode number N"
1096//usage: )
1097//usage: IF_FEATURE_FIND_USER(
1098//usage: "\n -user NAME File is owned by user NAME (numeric user ID allowed)"
1099//usage: )
1100//usage: IF_FEATURE_FIND_GROUP(
1101//usage: "\n -group NAME File belongs to group NAME (numeric group ID allowed)"
1102//usage: )
1103//usage: IF_FEATURE_FIND_DEPTH(
1104//usage: "\n -depth Process directory name after traversing it"
1105//usage: )
1106//usage: IF_FEATURE_FIND_SIZE(
1107//usage: "\n -size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
1108//usage: "\n +/-N: file size is bigger/smaller than N"
1109//usage: )
1110//usage: IF_FEATURE_FIND_LINKS(
1111//usage: "\n -links N Number of links is greater than (+N), less than (-N),"
1112//usage: "\n or exactly N"
1113//usage: )
1114//usage: "\n -print Print (default and assumed)"
1115//usage: IF_FEATURE_FIND_PRINT0(
1116//usage: "\n -print0 Delimit output with null characters rather than"
1117//usage: "\n newlines"
1118//usage: )
1119//usage: IF_FEATURE_FIND_CONTEXT(
1120//usage: "\n -context File has specified security context"
1121//usage: )
1122//usage: IF_FEATURE_FIND_EXEC(
1123//usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by the"
1124//usage: "\n matching files"
1125//usage: )
1126//usage: IF_FEATURE_FIND_PRUNE(
1127//usage: "\n -prune Stop traversing current subtree"
1128//usage: )
1129//usage: IF_FEATURE_FIND_DELETE(
1130//usage: "\n -delete Delete files, turns on -depth option"
1131//usage: )
1132//usage: IF_FEATURE_FIND_PAREN(
1133//usage: "\n (EXPR) Group an expression"
1134//usage: )
1135//usage:
1136//usage:#define find_example_usage
1137//usage: "$ find / -name passwd\n"
1138//usage: "/etc/passwd\n"
1139
1140int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1162int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1141int find_main(int argc UNUSED_PARAM, char **argv) 1163int find_main(int argc UNUSED_PARAM, char **argv)
1142{ 1164{
1143 static const char options[] ALIGN1 =
1144 "-follow\0"
1145IF_FEATURE_FIND_XDEV( "-xdev\0" )
1146IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
1147 ;
1148 enum {
1149 OPT_FOLLOW,
1150IF_FEATURE_FIND_XDEV( OPT_XDEV ,)
1151IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,)
1152 };
1153
1154 char *arg;
1155 char **argp;
1156 int i, firstopt, status = EXIT_SUCCESS; 1165 int i, firstopt, status = EXIT_SUCCESS;
1157#if ENABLE_FEATURE_FIND_MAXDEPTH
1158 int minmaxdepth[2] = { 0, INT_MAX };
1159#else
1160#define minmaxdepth NULL
1161#endif
1162 1166
1163 INIT_G(); 1167 INIT_G();
1164 1168
1165 for (firstopt = 1; argv[firstopt]; firstopt++) { 1169 argv++;
1170 for (firstopt = 0; argv[firstopt]; firstopt++) {
1166 if (argv[firstopt][0] == '-') 1171 if (argv[firstopt][0] == '-')
1167 break; 1172 break;
1168 if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!')) 1173 if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!'))
1169 break; 1174 break;
1170#if ENABLE_FEATURE_FIND_PAREN 1175 if (ENABLE_FEATURE_FIND_PAREN && LONE_CHAR(argv[firstopt], '('))
1171 if (LONE_CHAR(argv[firstopt], '('))
1172 break; 1176 break;
1173#endif
1174 } 1177 }
1175 if (firstopt == 1) { 1178 if (firstopt == 0) {
1176 argv[0] = (char*)"."; 1179 *--argv = (char*)".";
1177 argv--;
1178 firstopt++; 1180 firstopt++;
1179 } 1181 }
1180 1182
1181/* All options always return true. They always take effect 1183 G.actions = parse_params(&argv[firstopt]);
1182 * rather than being processed only when their place in the 1184 argv[firstopt] = NULL;
1183 * expression is reached. 1185
1184 * We implement: -follow, -xdev, -maxdepth
1185 */
1186 /* Process options, and replace then with -a */
1187 /* (-a will be ignored by recursive parser later) */
1188 argp = &argv[firstopt];
1189 while ((arg = argp[0])) {
1190 int opt = index_in_strings(options, arg);
1191 if (opt == OPT_FOLLOW) {
1192 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
1193 argp[0] = (char*)"-a";
1194 }
1195#if ENABLE_FEATURE_FIND_XDEV 1186#if ENABLE_FEATURE_FIND_XDEV
1196 if (opt == OPT_XDEV) { 1187 if (G.xdev_on) {
1197 struct stat stbuf; 1188 struct stat stbuf;
1198 if (!G.xdev_count) { 1189
1199 G.xdev_count = firstopt - 1; 1190 G.xdev_count = firstopt;
1200 G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0])); 1191 G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
1201 for (i = 1; i < firstopt; i++) { 1192 for (i = 0; argv[i]; i++) {
1202 /* not xstat(): shouldn't bomb out on 1193 /* not xstat(): shouldn't bomb out on
1203 * "find not_exist exist -xdev" */ 1194 * "find not_exist exist -xdev" */
1204 if (stat(argv[i], &stbuf) == 0) 1195 if (stat(argv[i], &stbuf) == 0)
1205 G.xdev_dev[i-1] = stbuf.st_dev; 1196 G.xdev_dev[i] = stbuf.st_dev;
1206 /* else G.xdev_dev[i-1] stays 0 and 1197 /* else G.xdev_dev[i] stays 0 and
1207 * won't match any real device dev_t */ 1198 * won't match any real device dev_t
1208 } 1199 */
1209 }
1210 argp[0] = (char*)"-a";
1211 }
1212#endif
1213#if ENABLE_FEATURE_FIND_MAXDEPTH
1214 if (opt == OPT_MINDEPTH || opt == OPT_MINDEPTH + 1) {
1215 if (!argp[1])
1216 bb_show_usage();
1217 minmaxdepth[opt - OPT_MINDEPTH] = xatoi_positive(argp[1]);
1218 argp[0] = (char*)"-a";
1219 argp[1] = (char*)"-a";
1220 argp++;
1221 } 1200 }
1222#endif
1223 argp++;
1224 } 1201 }
1202#endif
1225 1203
1226 G.actions = parse_params(&argv[firstopt]); 1204 for (i = 0; argv[i]; i++) {
1227
1228 for (i = 1; i < firstopt; i++) {
1229 if (!recursive_action(argv[i], 1205 if (!recursive_action(argv[i],
1230 G.recurse_flags,/* flags */ 1206 G.recurse_flags,/* flags */
1231 fileAction, /* file action */ 1207 fileAction, /* file action */
1232 fileAction, /* dir action */ 1208 fileAction, /* dir action */
1233#if ENABLE_FEATURE_FIND_MAXDEPTH
1234 minmaxdepth, /* user data */
1235#else
1236 NULL, /* user data */ 1209 NULL, /* user data */
1237#endif 1210 0) /* depth */
1238 0)) /* depth */ 1211 ) {
1239 status = EXIT_FAILURE; 1212 status = EXIT_FAILURE;
1213 }
1240 } 1214 }
1215
1241 return status; 1216 return status;
1242} 1217}