Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/findutils/find.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1123 - (hide annotations) (download)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 35712 byte(s)
-updated to busybox-1.17.1
1 niro 532 /* vi: set sw=4 ts=4: */
2     /*
3     * Mini find implementation for busybox
4     *
5     * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6     *
7     * Reworked by David Douthitt <n9ubh@callsign.net> and
8     * Matt Kraai <kraai@alumni.carnegiemellon.edu>.
9     *
10     * Licensed under the GPL version 2, see the file LICENSE in this tarball.
11     */
12    
13     /* findutils-4.1.20:
14     *
15     * # find file.txt -exec 'echo {}' '{} {}' ';'
16     * find: echo file.txt: No such file or directory
17     * # find file.txt -exec 'echo' '{} {}' '; '
18     * find: missing argument to `-exec'
19     * # find file.txt -exec 'echo {}' '{} {}' ';' junk
20     * find: paths must precede expression
21     * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';'
22     * find: paths must precede expression
23     * # find file.txt -exec 'echo' '{} {}' ';'
24     * file.txt file.txt
25     * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ]))
26     * # find file.txt -exec 'echo' '{} {}' ';' -print -exec pwd ';'
27     * file.txt file.txt
28     * file.txt
29     * /tmp
30     * # find -name '*.c' -o -name '*.h'
31     * [shows files, *.c and *.h intermixed]
32     * # find file.txt -name '*f*' -o -name '*t*'
33     * file.txt
34     * # find file.txt -name '*z*' -o -name '*t*'
35     * file.txt
36     * # find file.txt -name '*f*' -o -name '*z*'
37     * file.txt
38     *
39     * # find t z -name '*t*' -print -o -name '*z*'
40     * t
41     * # find t z t z -name '*t*' -o -name '*z*' -print
42     * z
43     * z
44     * # find t z t z '(' -name '*t*' -o -name '*z*' ')' -o -print
45     * (no output)
46     */
47    
48 niro 816 /* Testing script
49     * ./busybox find "$@" | tee /tmp/bb_find
50     * echo ==================
51     * /path/to/gnu/find "$@" | tee /tmp/std_find
52     * echo ==================
53     * diff -u /tmp/std_find /tmp/bb_find && echo Identical
54     */
55    
56 niro 1123 //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
61     //config: bool "find"
62     //config: default y
63     //config: help
64     //config: find is used to search your system to find specified files.
65     //config:
66     //config:config FEATURE_FIND_PRINT0
67     //config: bool "Enable -print0: NUL-terminated output"
68     //config: default y
69     //config: depends on FIND
70     //config: help
71     //config: Causes output names to be separated by a NUL character
72     //config: rather than a newline. This allows names that contain
73     //config: newlines and other whitespace to be more easily
74     //config: interpreted by other programs.
75     //config:
76     //config:config FEATURE_FIND_MTIME
77     //config: bool "Enable -mtime: modified time matching"
78     //config: default y
79     //config: depends on FIND
80     //config: help
81     //config: Allow searching based on the modification time of
82     //config: files, in days.
83     //config:
84     //config:config FEATURE_FIND_MMIN
85     //config: bool "Enable -mmin: modified time matching by minutes"
86     //config: default y
87     //config: depends on FIND
88     //config: help
89     //config: Allow searching based on the modification time of
90     //config: files, in minutes.
91     //config:
92     //config:config FEATURE_FIND_PERM
93     //config: bool "Enable -perm: permissions matching"
94     //config: default y
95     //config: depends on FIND
96     //config: help
97     //config: Enable searching based on file permissions.
98     //config:
99     //config:config FEATURE_FIND_TYPE
100     //config: bool "Enable -type: file type matching (file/dir/link/...)"
101     //config: default y
102     //config: depends on FIND
103     //config: help
104     //config: Enable searching based on file type (file,
105     //config: directory, socket, device, etc.).
106     //config:
107     //config:config FEATURE_FIND_XDEV
108     //config: bool "Enable -xdev: 'stay in filesystem'"
109     //config: default y
110     //config: depends on FIND
111     //config: help
112     //config: This option allows find to restrict searches to a single filesystem.
113     //config:
114     //config:config FEATURE_FIND_MAXDEPTH
115     //config: bool "Enable -maxdepth N"
116     //config: default y
117     //config: depends on FIND
118     //config: help
119     //config: This option enables -maxdepth N option.
120     //config:
121     //config:config FEATURE_FIND_NEWER
122     //config: bool "Enable -newer: compare file modification times"
123     //config: default y
124     //config: depends on FIND
125     //config: help
126     //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.
128     //config:
129     //config:config FEATURE_FIND_INUM
130     //config: bool "Enable -inum: inode number matching"
131     //config: default y
132     //config: depends on FIND
133     //config: help
134     //config: Support the 'find -inum' option for searching by inode number.
135     //config:
136     //config:config FEATURE_FIND_EXEC
137     //config: bool "Enable -exec: execute commands"
138     //config: default y
139     //config: depends on FIND
140     //config: help
141     //config: Support the 'find -exec' option for executing commands based upon
142     //config: the files matched.
143     //config:
144     //config:config FEATURE_FIND_USER
145     //config: bool "Enable -user: username/uid matching"
146     //config: default y
147     //config: depends on FIND
148     //config: help
149     //config: Support the 'find -user' option for searching by username or uid.
150     //config:
151     //config:config FEATURE_FIND_GROUP
152     //config: bool "Enable -group: group/gid matching"
153     //config: default y
154     //config: depends on FIND
155     //config: help
156     //config: Support the 'find -group' option for searching by group name or gid.
157     //config:
158     //config:config FEATURE_FIND_NOT
159     //config: bool "Enable the 'not' (!) operator"
160     //config: default y
161     //config: depends on FIND
162     //config: help
163     //config: Support the '!' operator to invert the test results.
164     //config: If 'Enable full-blown desktop' is enabled, then will also support
165     //config: the non-POSIX notation '-not'.
166     //config:
167     //config:config FEATURE_FIND_DEPTH
168     //config: bool "Enable -depth"
169     //config: default y
170     //config: depends on FIND
171     //config: help
172     //config: Process each directory's contents before the directory itself.
173     //config:
174     //config:config FEATURE_FIND_PAREN
175     //config: bool "Enable parens in options"
176     //config: default y
177     //config: depends on FIND
178     //config: help
179     //config: Enable usage of parens '(' to specify logical order of arguments.
180     //config:
181     //config:config FEATURE_FIND_SIZE
182     //config: bool "Enable -size: file size matching"
183     //config: default y
184     //config: depends on FIND
185     //config: help
186     //config: Support the 'find -size' option for searching by file size.
187     //config:
188     //config:config FEATURE_FIND_PRUNE
189     //config: bool "Enable -prune: exclude subdirectories"
190     //config: default y
191     //config: depends on FIND
192     //config: help
193     //config: If the file is a directory, dont descend into it. Useful for
194     //config: exclusion .svn and CVS directories.
195     //config:
196     //config:config FEATURE_FIND_DELETE
197     //config: bool "Enable -delete: delete files/dirs"
198     //config: default y
199     //config: depends on FIND && FEATURE_FIND_DEPTH
200     //config: help
201     //config: Support the 'find -delete' option for deleting files and directories.
202     //config: WARNING: This option can do much harm if used wrong. Busybox will not
203     //config: try to protect the user from doing stupid things. Use with care.
204     //config:
205     //config:config FEATURE_FIND_PATH
206     //config: bool "Enable -path: match pathname with shell pattern"
207     //config: default y
208     //config: depends on FIND
209     //config: help
210     //config: The -path option matches whole pathname instead of just filename.
211     //config:
212     //config:config FEATURE_FIND_REGEX
213     //config: bool "Enable -regex: match pathname with regex"
214     //config: default y
215     //config: depends on FIND
216     //config: help
217     //config: The -regex option matches whole pathname against regular expression.
218     //config:
219     //config:config FEATURE_FIND_CONTEXT
220     //config: bool "Enable -context: security context matching"
221     //config: default n
222     //config: depends on FIND && SELINUX
223     //config: help
224     //config: Support the 'find -context' option for matching security context.
225     //config:
226     //config:config FEATURE_FIND_LINKS
227     //config: bool "Enable -links: link count matching"
228     //config: default y
229     //config: depends on FIND
230     //config: help
231     //config: Support the 'find -links' option for matching number of links.
232    
233 niro 532 #include <fnmatch.h>
234 niro 816 #include "libbb.h"
235     #if ENABLE_FEATURE_FIND_REGEX
236     #include "xregex.h"
237     #endif
238 niro 532
239 niro 816 /* This is a NOEXEC applet. Be very careful! */
240    
241    
242 niro 984 typedef int (*action_fp)(const char *fileName, const struct stat *statbuf, void *) FAST_FUNC;
243 niro 532
244     typedef struct {
245     action_fp f;
246 niro 816 #if ENABLE_FEATURE_FIND_NOT
247     bool invert;
248     #endif
249 niro 532 } action;
250    
251 niro 984 #define ACTS(name, ...) typedef struct { action a; __VA_ARGS__ } action_##name;
252     #define ACTF(name) \
253     static int FAST_FUNC func_##name(const char *fileName UNUSED_PARAM, \
254     const struct stat *statbuf UNUSED_PARAM, \
255     action_##name* ap UNUSED_PARAM)
256 niro 532
257 niro 984 ACTS(print)
258     ACTS(name, const char *pattern; bool iname;)
259     IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern;))
260     IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;))
261     IF_FEATURE_FIND_PRINT0( ACTS(print0))
262     IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;))
263     IF_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;))
264     IF_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; unsigned mtime_days;))
265     IF_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; unsigned mmin_mins;))
266     IF_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;))
267     IF_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;))
268     IF_FEATURE_FIND_USER( ACTS(user, uid_t uid;))
269     IF_FEATURE_FIND_SIZE( ACTS(size, char size_char; off_t size;))
270     IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
271     IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;))
272     IF_FEATURE_FIND_PRUNE( ACTS(prune))
273     IF_FEATURE_FIND_DELETE( ACTS(delete))
274     IF_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned *subst_count; int exec_argc;))
275     IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;))
276     IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;))
277    
278     struct globals {
279     IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;)
280     IF_FEATURE_FIND_XDEV(int xdev_count;)
281     action ***actions;
282     bool need_print;
283     recurse_flags_t recurse_flags;
284 niro 1123 } FIX_ALIASING;
285 niro 984 #define G (*(struct globals*)&bb_common_bufsiz1)
286     #define INIT_G() do { \
287     struct G_sizecheck { \
288     char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
289     }; \
290     /* we have to zero it out because of NOEXEC */ \
291     memset(&G, 0, offsetof(struct globals, need_print)); \
292     G.need_print = 1; \
293     G.recurse_flags = ACTION_RECURSE; \
294     } while (0)
295    
296 niro 532 #if ENABLE_FEATURE_FIND_EXEC
297 niro 816 static unsigned count_subst(const char *str)
298 niro 532 {
299 niro 816 unsigned count = 0;
300     while ((str = strstr(str, "{}")) != NULL) {
301 niro 532 count++;
302     str++;
303     }
304     return count;
305     }
306    
307    
308 niro 816 static char* subst(const char *src, unsigned count, const char* filename)
309 niro 532 {
310     char *buf, *dst, *end;
311 niro 816 size_t flen = strlen(filename);
312 niro 532 /* we replace each '{}' with filename: growth by strlen-2 */
313     buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1);
314     while ((end = strstr(src, "{}"))) {
315     memcpy(dst, src, end - src);
316     dst += end - src;
317     src = end + 2;
318     memcpy(dst, filename, flen);
319     dst += flen;
320     }
321     strcpy(dst, src);
322     return buf;
323     }
324     #endif
325    
326 niro 816 /* Return values of ACTFs ('action functions') are a bit mask:
327     * bit 1=1: prune (use SKIP constant for setting it)
328     * bit 0=1: matched successfully (TRUE)
329     */
330 niro 532
331 niro 984 static int exec_actions(action ***appp, const char *fileName, const struct stat *statbuf)
332 niro 532 {
333     int cur_group;
334     int cur_action;
335 niro 816 int rc = 0;
336 niro 532 action **app, *ap;
337    
338 niro 816 /* "action group" is a set of actions ANDed together.
339     * groups are ORed together.
340     * We simply evaluate each group until we find one in which all actions
341     * succeed. */
342    
343     /* -prune is special: if it is encountered, then we won't
344     * descend into current directory. It doesn't matter whether
345     * action group (in which -prune sits) will succeed or not:
346     * find * -prune -name 'f*' -o -name 'm*' -- prunes every dir
347     * find * -name 'f*' -o -prune -name 'm*' -- prunes all dirs
348     * not starting with 'f' */
349    
350     /* We invert TRUE bit (bit 0). Now 1 there means 'failure'.
351     * and bitwise OR in "rc |= TRUE ^ ap->f()" will:
352     * (1) make SKIP (-prune) bit stick; and (2) detect 'failure'.
353     * On return, bit is restored. */
354    
355 niro 532 cur_group = -1;
356 niro 984 while ((app = appp[++cur_group]) != NULL) {
357 niro 816 rc &= ~TRUE; /* 'success' so far, clear TRUE bit */
358 niro 532 cur_action = -1;
359 niro 816 while (1) {
360 niro 532 ap = app[++cur_action];
361 niro 816 if (!ap) /* all actions in group were successful */
362     return rc ^ TRUE; /* restore TRUE bit */
363     rc |= TRUE ^ ap->f(fileName, statbuf, ap);
364     #if ENABLE_FEATURE_FIND_NOT
365     if (ap->invert) rc ^= TRUE;
366     #endif
367     if (rc & TRUE) /* current group failed, try next */
368     break;
369 niro 532 }
370     }
371 niro 816 return rc ^ TRUE; /* restore TRUE bit */
372 niro 532 }
373    
374    
375     ACTF(name)
376     {
377 niro 816 const char *tmp = bb_basename(fileName);
378 niro 1123 if (tmp != fileName && *tmp == '\0') {
379     /* "foo/bar/". Oh no... go back to 'b' */
380 niro 816 tmp--;
381     while (tmp != fileName && *--tmp != '/')
382     continue;
383     if (*tmp == '/')
384     tmp++;
385     }
386 niro 1123 /* Was using FNM_PERIOD flag too,
387     * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it.
388     * find -name '*foo' should match .foo too:
389     */
390     return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0;
391 niro 532 }
392 niro 816
393     #if ENABLE_FEATURE_FIND_PATH
394     ACTF(path)
395     {
396     return fnmatch(ap->pattern, fileName, 0) == 0;
397     }
398     #endif
399     #if ENABLE_FEATURE_FIND_REGEX
400     ACTF(regex)
401     {
402     regmatch_t match;
403     if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 /*eflags*/))
404     return 0; /* no match */
405     if (match.rm_so)
406     return 0; /* match doesn't start at pos 0 */
407     if (fileName[match.rm_eo])
408     return 0; /* match doesn't end exactly at end of pathname */
409     return 1;
410     }
411     #endif
412 niro 532 #if ENABLE_FEATURE_FIND_TYPE
413     ACTF(type)
414     {
415     return ((statbuf->st_mode & S_IFMT) == ap->type_mask);
416     }
417     #endif
418     #if ENABLE_FEATURE_FIND_PERM
419     ACTF(perm)
420     {
421 niro 816 /* -perm +mode: at least one of perm_mask bits are set */
422     if (ap->perm_char == '+')
423     return (statbuf->st_mode & ap->perm_mask) != 0;
424     /* -perm -mode: all of perm_mask are set */
425     if (ap->perm_char == '-')
426     return (statbuf->st_mode & ap->perm_mask) == ap->perm_mask;
427     /* -perm mode: file mode must match perm_mask */
428     return (statbuf->st_mode & 07777) == ap->perm_mask;
429 niro 532 }
430     #endif
431     #if ENABLE_FEATURE_FIND_MTIME
432     ACTF(mtime)
433     {
434     time_t file_age = time(NULL) - statbuf->st_mtime;
435 niro 816 time_t mtime_secs = ap->mtime_days * 24*60*60;
436     if (ap->mtime_char == '+')
437     return file_age >= mtime_secs + 24*60*60;
438     if (ap->mtime_char == '-')
439     return file_age < mtime_secs;
440     /* just numeric mtime */
441     return file_age >= mtime_secs && file_age < (mtime_secs + 24*60*60);
442 niro 532 }
443     #endif
444     #if ENABLE_FEATURE_FIND_MMIN
445     ACTF(mmin)
446     {
447     time_t file_age = time(NULL) - statbuf->st_mtime;
448     time_t mmin_secs = ap->mmin_mins * 60;
449 niro 816 if (ap->mmin_char == '+')
450     return file_age >= mmin_secs + 60;
451     if (ap->mmin_char == '-')
452     return file_age < mmin_secs;
453     /* just numeric mmin */
454     return file_age >= mmin_secs && file_age < (mmin_secs + 60);
455 niro 532 }
456     #endif
457     #if ENABLE_FEATURE_FIND_NEWER
458     ACTF(newer)
459     {
460 niro 816 return (ap->newer_mtime < statbuf->st_mtime);
461 niro 532 }
462     #endif
463     #if ENABLE_FEATURE_FIND_INUM
464     ACTF(inum)
465     {
466 niro 816 return (statbuf->st_ino == ap->inode_num);
467 niro 532 }
468     #endif
469     #if ENABLE_FEATURE_FIND_EXEC
470     ACTF(exec)
471     {
472     int i, rc;
473 niro 984 #if ENABLE_USE_PORTABLE_CODE
474     char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1));
475     #else /* gcc 4.3.1 generates smaller code: */
476 niro 816 char *argv[ap->exec_argc + 1];
477 niro 984 #endif
478 niro 532 for (i = 0; i < ap->exec_argc; i++)
479     argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName);
480     argv[i] = NULL; /* terminate the list */
481 niro 816
482     rc = spawn_and_wait(argv);
483     if (rc < 0)
484     bb_simple_perror_msg(argv[0]);
485    
486     i = 0;
487     while (argv[i])
488     free(argv[i++]);
489     return rc == 0; /* return 1 if exitcode 0 */
490 niro 532 }
491     #endif
492 niro 816 #if ENABLE_FEATURE_FIND_USER
493     ACTF(user)
494     {
495     return (statbuf->st_uid == ap->uid);
496     }
497     #endif
498     #if ENABLE_FEATURE_FIND_GROUP
499     ACTF(group)
500     {
501     return (statbuf->st_gid == ap->gid);
502     }
503     #endif
504 niro 532 #if ENABLE_FEATURE_FIND_PRINT0
505     ACTF(print0)
506     {
507     printf("%s%c", fileName, '\0');
508     return TRUE;
509     }
510     #endif
511     ACTF(print)
512     {
513     puts(fileName);
514     return TRUE;
515     }
516 niro 816 #if ENABLE_FEATURE_FIND_PAREN
517 niro 532 ACTF(paren)
518     {
519     return exec_actions(ap->subexpr, fileName, statbuf);
520     }
521 niro 816 #endif
522     #if ENABLE_FEATURE_FIND_SIZE
523     ACTF(size)
524     {
525     if (ap->size_char == '+')
526     return statbuf->st_size > ap->size;
527     if (ap->size_char == '-')
528     return statbuf->st_size < ap->size;
529     return statbuf->st_size == ap->size;
530     }
531     #endif
532     #if ENABLE_FEATURE_FIND_PRUNE
533 niro 532 /*
534     * -prune: if -depth is not given, return true and do not descend
535     * current dir; if -depth is given, return false with no effect.
536     * Example:
537     * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print
538     */
539     ACTF(prune)
540     {
541 niro 816 return SKIP + TRUE;
542 niro 532 }
543 niro 816 #endif
544     #if ENABLE_FEATURE_FIND_DELETE
545     ACTF(delete)
546 niro 532 {
547 niro 816 int rc;
548     if (S_ISDIR(statbuf->st_mode)) {
549     rc = rmdir(fileName);
550     } else {
551     rc = unlink(fileName);
552     }
553     if (rc < 0)
554     bb_simple_perror_msg(fileName);
555     return TRUE;
556 niro 532 }
557     #endif
558 niro 816 #if ENABLE_FEATURE_FIND_CONTEXT
559     ACTF(context)
560     {
561     security_context_t con;
562     int rc;
563 niro 532
564 niro 984 if (G.recurse_flags & ACTION_FOLLOWLINKS) {
565 niro 816 rc = getfilecon(fileName, &con);
566     } else {
567     rc = lgetfilecon(fileName, &con);
568     }
569     if (rc < 0)
570     return FALSE;
571     rc = strcmp(ap->context, con);
572     freecon(con);
573     return rc == 0;
574     }
575     #endif
576 niro 984 #if ENABLE_FEATURE_FIND_LINKS
577     ACTF(links)
578     {
579     switch(ap->links_char) {
580     case '-' : return (statbuf->st_nlink < ap->links_count);
581     case '+' : return (statbuf->st_nlink > ap->links_count);
582     default: return (statbuf->st_nlink == ap->links_count);
583     }
584     }
585     #endif
586 niro 532
587 niro 816 static int FAST_FUNC fileAction(const char *fileName,
588     struct stat *statbuf,
589 niro 984 void *userData IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM),
590     int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM))
591 niro 532 {
592 niro 984 int r;
593 niro 816 #if ENABLE_FEATURE_FIND_MAXDEPTH
594 niro 984 #define minmaxdepth ((int*)userData)
595 niro 816
596 niro 984 if (depth < minmaxdepth[0])
597     return TRUE; /* skip this, continue recursing */
598     if (depth > minmaxdepth[1])
599     return SKIP; /* stop recursing */
600 niro 816 #endif
601    
602 niro 984 r = exec_actions(G.actions, fileName, statbuf);
603     /* Had no explicit -print[0] or -exec? then print */
604     if ((r & TRUE) && G.need_print)
605     puts(fileName);
606    
607     #if ENABLE_FEATURE_FIND_MAXDEPTH
608     if (S_ISDIR(statbuf->st_mode)) {
609     if (depth == minmaxdepth[1])
610     return SKIP;
611     }
612     #endif
613 niro 816 #if ENABLE_FEATURE_FIND_XDEV
614 niro 984 /* -xdev stops on mountpoints, but AFTER mountpoit itself
615     * is processed as usual */
616     if (S_ISDIR(statbuf->st_mode)) {
617     if (G.xdev_count) {
618     int i;
619     for (i = 0; i < G.xdev_count; i++) {
620     if (G.xdev_dev[i] == statbuf->st_dev)
621     goto found;
622     }
623     return SKIP;
624     found: ;
625 niro 532 }
626     }
627     #endif
628 niro 984
629 niro 532 /* Cannot return 0: our caller, recursive_action(),
630     * will perror() and skip dirs (if called on dir) */
631 niro 984 return (r & SKIP) ? SKIP : TRUE;
632     #undef minmaxdepth
633 niro 532 }
634    
635    
636     #if ENABLE_FEATURE_FIND_TYPE
637 niro 816 static int find_type(const char *type)
638 niro 532 {
639     int mask = 0;
640    
641 niro 816 if (*type == 'b')
642 niro 532 mask = S_IFBLK;
643 niro 816 else if (*type == 'c')
644 niro 532 mask = S_IFCHR;
645 niro 816 else if (*type == 'd')
646 niro 532 mask = S_IFDIR;
647 niro 816 else if (*type == 'p')
648 niro 532 mask = S_IFIFO;
649 niro 816 else if (*type == 'f')
650 niro 532 mask = S_IFREG;
651 niro 816 else if (*type == 'l')
652 niro 532 mask = S_IFLNK;
653 niro 816 else if (*type == 's')
654 niro 532 mask = S_IFSOCK;
655    
656 niro 984 if (mask == 0 || type[1] != '\0')
657 niro 532 bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
658    
659     return mask;
660     }
661     #endif
662    
663 niro 816 #if ENABLE_FEATURE_FIND_PERM \
664     || ENABLE_FEATURE_FIND_MTIME || ENABLE_FEATURE_FIND_MMIN \
665 niro 984 || ENABLE_FEATURE_FIND_SIZE || ENABLE_FEATURE_FIND_LINKS
666 niro 816 static const char* plus_minus_num(const char* str)
667 niro 532 {
668 niro 816 if (*str == '-' || *str == '+')
669     str++;
670     return str;
671     }
672     #endif
673    
674     static action*** parse_params(char **argv)
675     {
676     enum {
677 niro 984 PARM_a ,
678     PARM_o ,
679     IF_FEATURE_FIND_NOT( PARM_char_not ,)
680 niro 816 #if ENABLE_DESKTOP
681 niro 984 PARM_and ,
682     PARM_or ,
683     IF_FEATURE_FIND_NOT( PARM_not ,)
684 niro 816 #endif
685 niro 984 PARM_print ,
686     IF_FEATURE_FIND_PRINT0( PARM_print0 ,)
687     IF_FEATURE_FIND_DEPTH( PARM_depth ,)
688     IF_FEATURE_FIND_PRUNE( PARM_prune ,)
689     IF_FEATURE_FIND_DELETE( PARM_delete ,)
690     IF_FEATURE_FIND_EXEC( PARM_exec ,)
691     IF_FEATURE_FIND_PAREN( PARM_char_brace,)
692 niro 816 /* All options starting from here require argument */
693 niro 984 PARM_name ,
694     PARM_iname ,
695     IF_FEATURE_FIND_PATH( PARM_path ,)
696     IF_FEATURE_FIND_REGEX( PARM_regex ,)
697     IF_FEATURE_FIND_TYPE( PARM_type ,)
698     IF_FEATURE_FIND_PERM( PARM_perm ,)
699     IF_FEATURE_FIND_MTIME( PARM_mtime ,)
700     IF_FEATURE_FIND_MMIN( PARM_mmin ,)
701     IF_FEATURE_FIND_NEWER( PARM_newer ,)
702     IF_FEATURE_FIND_INUM( PARM_inum ,)
703     IF_FEATURE_FIND_USER( PARM_user ,)
704     IF_FEATURE_FIND_GROUP( PARM_group ,)
705     IF_FEATURE_FIND_SIZE( PARM_size ,)
706     IF_FEATURE_FIND_CONTEXT(PARM_context ,)
707     IF_FEATURE_FIND_LINKS( PARM_links ,)
708 niro 816 };
709    
710     static const char params[] ALIGN1 =
711     "-a\0"
712     "-o\0"
713 niro 984 IF_FEATURE_FIND_NOT( "!\0" )
714 niro 816 #if ENABLE_DESKTOP
715     "-and\0"
716     "-or\0"
717 niro 984 IF_FEATURE_FIND_NOT( "-not\0" )
718 niro 816 #endif
719     "-print\0"
720 niro 984 IF_FEATURE_FIND_PRINT0( "-print0\0" )
721     IF_FEATURE_FIND_DEPTH( "-depth\0" )
722     IF_FEATURE_FIND_PRUNE( "-prune\0" )
723     IF_FEATURE_FIND_DELETE( "-delete\0" )
724     IF_FEATURE_FIND_EXEC( "-exec\0" )
725     IF_FEATURE_FIND_PAREN( "(\0" )
726 niro 816 /* All options starting from here require argument */
727     "-name\0"
728     "-iname\0"
729 niro 984 IF_FEATURE_FIND_PATH( "-path\0" )
730     IF_FEATURE_FIND_REGEX( "-regex\0" )
731     IF_FEATURE_FIND_TYPE( "-type\0" )
732     IF_FEATURE_FIND_PERM( "-perm\0" )
733     IF_FEATURE_FIND_MTIME( "-mtime\0" )
734     IF_FEATURE_FIND_MMIN( "-mmin\0" )
735     IF_FEATURE_FIND_NEWER( "-newer\0" )
736     IF_FEATURE_FIND_INUM( "-inum\0" )
737     IF_FEATURE_FIND_USER( "-user\0" )
738     IF_FEATURE_FIND_GROUP( "-group\0" )
739     IF_FEATURE_FIND_SIZE( "-size\0" )
740     IF_FEATURE_FIND_CONTEXT("-context\0")
741     IF_FEATURE_FIND_LINKS( "-links\0" )
742 niro 816 ;
743    
744 niro 532 action*** appp;
745 niro 816 unsigned cur_group = 0;
746     unsigned cur_action = 0;
747 niro 984 IF_FEATURE_FIND_NOT( bool invert_flag = 0; )
748 niro 532
749 niro 816 /* This is the only place in busybox where we use nested function.
750     * So far more standard alternatives were bigger. */
751     /* Suppress a warning "func without a prototype" */
752     auto action* alloc_action(int sizeof_struct, action_fp f);
753 niro 532 action* alloc_action(int sizeof_struct, action_fp f)
754     {
755     action *ap;
756     appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(*appp));
757     appp[cur_group][cur_action++] = ap = xmalloc(sizeof_struct);
758     appp[cur_group][cur_action] = NULL;
759     ap->f = f;
760 niro 984 IF_FEATURE_FIND_NOT( ap->invert = invert_flag; )
761     IF_FEATURE_FIND_NOT( invert_flag = 0; )
762 niro 532 return ap;
763     }
764 niro 816
765 niro 532 #define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name)
766    
767 niro 816 appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */
768 niro 532
769 niro 816 /* 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) {
784     const char *arg = argv[0];
785     int parm = index_in_strings(params, arg);
786     const char *arg1 = argv[1];
787 niro 532
788 niro 816 if (parm >= PARM_name) {
789     /* All options starting from -name require argument */
790     if (!arg1)
791     bb_error_msg_and_die(bb_msg_requires_arg, arg);
792     argv++;
793     }
794 niro 532
795 niro 816 /* We can use big switch() here, but on i386
796     * it doesn't give smaller code. Other arches? */
797 niro 532
798     /* --- Operators --- */
799 niro 984 if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
800 niro 816 /* no further special handling required */
801 niro 532 }
802 niro 984 else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
803 niro 532 /* start new OR group */
804     cur_group++;
805     appp = xrealloc(appp, (cur_group+2) * sizeof(*appp));
806 niro 816 /*appp[cur_group] = NULL; - already NULL */
807 niro 532 appp[cur_group+1] = NULL;
808     cur_action = 0;
809     }
810 niro 816 #if ENABLE_FEATURE_FIND_NOT
811 niro 984 else if (parm == PARM_char_not IF_DESKTOP(|| parm == PARM_not)) {
812 niro 816 /* also handles "find ! ! -name 'foo*'" */
813     invert_flag ^= 1;
814     }
815     #endif
816 niro 532
817     /* --- Tests and actions --- */
818 niro 816 else if (parm == PARM_print) {
819 niro 984 G.need_print = 0;
820 niro 816 /* GNU find ignores '!' here: "find ! -print" */
821 niro 984 IF_FEATURE_FIND_NOT( invert_flag = 0; )
822 niro 532 (void) ALLOC_ACTION(print);
823     }
824     #if ENABLE_FEATURE_FIND_PRINT0
825 niro 816 else if (parm == PARM_print0) {
826 niro 984 G.need_print = 0;
827     IF_FEATURE_FIND_NOT( invert_flag = 0; )
828 niro 532 (void) ALLOC_ACTION(print0);
829     }
830     #endif
831 niro 816 #if ENABLE_FEATURE_FIND_DEPTH
832     else if (parm == PARM_depth) {
833 niro 984 G.recurse_flags |= ACTION_DEPTHFIRST;
834 niro 816 }
835     #endif
836     #if ENABLE_FEATURE_FIND_PRUNE
837     else if (parm == PARM_prune) {
838 niro 984 IF_FEATURE_FIND_NOT( invert_flag = 0; )
839 niro 816 (void) ALLOC_ACTION(prune);
840     }
841     #endif
842     #if ENABLE_FEATURE_FIND_DELETE
843     else if (parm == PARM_delete) {
844 niro 984 G.need_print = 0;
845     G.recurse_flags |= ACTION_DEPTHFIRST;
846 niro 816 (void) ALLOC_ACTION(delete);
847     }
848     #endif
849     #if ENABLE_FEATURE_FIND_EXEC
850     else if (parm == PARM_exec) {
851     int i;
852     action_exec *ap;
853 niro 984 G.need_print = 0;
854     IF_FEATURE_FIND_NOT( invert_flag = 0; )
855 niro 816 ap = ALLOC_ACTION(exec);
856     ap->exec_argv = ++argv; /* first arg after -exec */
857     ap->exec_argc = 0;
858     while (1) {
859 niro 1123 if (!*argv) /* did not see ';' or '+' until end */
860     bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
861 niro 816 if (LONE_CHAR(argv[0], ';'))
862     break;
863 niro 1123 //TODO: implement {} + (like xargs)
864     // See:
865     // find findutils/ -exec echo ">"{}"<" \;
866     // find findutils/ -exec echo ">"{}"<" +
867 niro 816 argv++;
868     ap->exec_argc++;
869     }
870     if (ap->exec_argc == 0)
871     bb_error_msg_and_die(bb_msg_requires_arg, arg);
872     ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
873     i = ap->exec_argc;
874     while (i--)
875     ap->subst_count[i] = count_subst(ap->exec_argv[i]);
876     }
877     #endif
878     #if ENABLE_FEATURE_FIND_PAREN
879     else if (parm == PARM_char_brace) {
880     action_paren *ap;
881     char **endarg;
882     unsigned nested = 1;
883    
884     endarg = argv;
885     while (1) {
886     if (!*++endarg)
887     bb_error_msg_and_die("unpaired '('");
888     if (LONE_CHAR(*endarg, '('))
889     nested++;
890     else if (LONE_CHAR(*endarg, ')') && !--nested) {
891     *endarg = NULL;
892     break;
893     }
894     }
895     ap = ALLOC_ACTION(paren);
896     ap->subexpr = parse_params(argv + 1);
897     *endarg = (char*) ")"; /* restore NULLed parameter */
898     argv = endarg;
899     }
900     #endif
901     else if (parm == PARM_name || parm == PARM_iname) {
902 niro 532 action_name *ap;
903     ap = ALLOC_ACTION(name);
904     ap->pattern = arg1;
905 niro 816 ap->iname = (parm == PARM_iname);
906 niro 532 }
907 niro 816 #if ENABLE_FEATURE_FIND_PATH
908     else if (parm == PARM_path) {
909     action_path *ap;
910     ap = ALLOC_ACTION(path);
911     ap->pattern = arg1;
912     }
913     #endif
914     #if ENABLE_FEATURE_FIND_REGEX
915     else if (parm == PARM_regex) {
916     action_regex *ap;
917     ap = ALLOC_ACTION(regex);
918     xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/);
919     }
920     #endif
921 niro 532 #if ENABLE_FEATURE_FIND_TYPE
922 niro 816 else if (parm == PARM_type) {
923 niro 532 action_type *ap;
924     ap = ALLOC_ACTION(type);
925     ap->type_mask = find_type(arg1);
926     }
927     #endif
928     #if ENABLE_FEATURE_FIND_PERM
929 niro 816 /* -perm mode File's permission bits are exactly mode (octal or symbolic).
930 niro 532 * Symbolic modes use mode 0 as a point of departure.
931     * -perm -mode All of the permission bits mode are set for the file.
932     * -perm +mode Any of the permission bits mode are set for the file.
933     */
934 niro 816 else if (parm == PARM_perm) {
935 niro 532 action_perm *ap;
936     ap = ALLOC_ACTION(perm);
937     ap->perm_char = arg1[0];
938 niro 816 arg1 = plus_minus_num(arg1);
939     ap->perm_mask = 0;
940     if (!bb_parse_mode(arg1, &ap->perm_mask))
941 niro 1123 bb_error_msg_and_die("invalid mode '%s'", arg1);
942 niro 532 }
943     #endif
944     #if ENABLE_FEATURE_FIND_MTIME
945 niro 816 else if (parm == PARM_mtime) {
946 niro 532 action_mtime *ap;
947     ap = ALLOC_ACTION(mtime);
948     ap->mtime_char = arg1[0];
949 niro 816 ap->mtime_days = xatoul(plus_minus_num(arg1));
950 niro 532 }
951     #endif
952     #if ENABLE_FEATURE_FIND_MMIN
953 niro 816 else if (parm == PARM_mmin) {
954 niro 532 action_mmin *ap;
955     ap = ALLOC_ACTION(mmin);
956     ap->mmin_char = arg1[0];
957 niro 816 ap->mmin_mins = xatoul(plus_minus_num(arg1));
958 niro 532 }
959     #endif
960     #if ENABLE_FEATURE_FIND_NEWER
961 niro 816 else if (parm == PARM_newer) {
962     struct stat stat_newer;
963 niro 532 action_newer *ap;
964 niro 816 ap = ALLOC_ACTION(newer);
965 niro 532 xstat(arg1, &stat_newer);
966     ap->newer_mtime = stat_newer.st_mtime;
967     }
968     #endif
969     #if ENABLE_FEATURE_FIND_INUM
970 niro 816 else if (parm == PARM_inum) {
971 niro 532 action_inum *ap;
972     ap = ALLOC_ACTION(inum);
973     ap->inode_num = xatoul(arg1);
974     }
975     #endif
976 niro 816 #if ENABLE_FEATURE_FIND_USER
977     else if (parm == PARM_user) {
978     action_user *ap;
979     ap = ALLOC_ACTION(user);
980     ap->uid = bb_strtou(arg1, NULL, 10);
981     if (errno)
982     ap->uid = xuname2uid(arg1);
983 niro 532 }
984     #endif
985 niro 816 #if ENABLE_FEATURE_FIND_GROUP
986     else if (parm == PARM_group) {
987     action_group *ap;
988     ap = ALLOC_ACTION(group);
989     ap->gid = bb_strtou(arg1, NULL, 10);
990     if (errno)
991     ap->gid = xgroup2gid(arg1);
992 niro 532 }
993 niro 816 #endif
994     #if ENABLE_FEATURE_FIND_SIZE
995     else if (parm == PARM_size) {
996     /* -size n[bckw]: file uses n units of space
997     * b (default): units are 512-byte blocks
998     * c: 1 byte
999     * k: kilobytes
1000     * w: 2-byte words
1001     */
1002     #if ENABLE_LFS
1003     #define XATOU_SFX xatoull_sfx
1004     #else
1005     #define XATOU_SFX xatoul_sfx
1006     #endif
1007     static const struct suffix_mult find_suffixes[] = {
1008     { "c", 1 },
1009     { "w", 2 },
1010     { "", 512 },
1011     { "b", 512 },
1012     { "k", 1024 },
1013 niro 984 { "", 0 }
1014 niro 816 };
1015 niro 532 action_size *ap;
1016     ap = ALLOC_ACTION(size);
1017 niro 816 ap->size_char = arg1[0];
1018     ap->size = XATOU_SFX(plus_minus_num(arg1), find_suffixes);
1019 niro 532 }
1020     #endif
1021 niro 816 #if ENABLE_FEATURE_FIND_CONTEXT
1022     else if (parm == PARM_context) {
1023     action_context *ap;
1024     ap = ALLOC_ACTION(context);
1025     ap->context = NULL;
1026     /* SELinux headers erroneously declare non-const parameter */
1027     if (selinux_raw_to_trans_context((char*)arg1, &ap->context))
1028     bb_simple_perror_msg(arg1);
1029     }
1030     #endif
1031 niro 984 #if ENABLE_FEATURE_FIND_LINKS
1032     else if (parm == PARM_links) {
1033     action_links *ap;
1034     ap = ALLOC_ACTION(links);
1035     ap->links_char = arg1[0];
1036     ap->links_count = xatoul(plus_minus_num(arg1));
1037     }
1038     #endif
1039 niro 816 else {
1040     bb_error_msg("unrecognized: %s", arg);
1041 niro 532 bb_show_usage();
1042 niro 816 }
1043 niro 532 argv++;
1044     }
1045     return appp;
1046     #undef ALLOC_ACTION
1047     }
1048    
1049 niro 1123 //usage:#define find_trivial_usage
1050     //usage: "[PATH]... [EXPRESSION]"
1051     //usage:#define find_full_usage "\n\n"
1052     //usage: "Search for files. The default PATH is the current directory,\n"
1053     //usage: "default EXPRESSION is '-print'\n"
1054     //usage: "\nEXPRESSION may consist of:"
1055     //usage: "\n -follow Follow symlinks"
1056     //usage: IF_FEATURE_FIND_XDEV(
1057     //usage: "\n -xdev Don't descend directories on other filesystems"
1058     //usage: )
1059     //usage: IF_FEATURE_FIND_MAXDEPTH(
1060     //usage: "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies"
1061     //usage: "\n tests/actions to command line arguments only"
1062     //usage: )
1063     //usage: "\n -mindepth N Don't act on first N levels"
1064     //usage: "\n -name PATTERN File name (w/o directory name) matches PATTERN"
1065     //usage: "\n -iname PATTERN Case insensitive -name"
1066     //usage: IF_FEATURE_FIND_PATH(
1067     //usage: "\n -path PATTERN Path matches PATTERN"
1068     //usage: )
1069     //usage: IF_FEATURE_FIND_REGEX(
1070     //usage: "\n -regex PATTERN Path matches regex PATTERN"
1071     //usage: )
1072     //usage: IF_FEATURE_FIND_TYPE(
1073     //usage: "\n -type X File type is X (X is one of: f,d,l,b,c,...)"
1074     //usage: )
1075     //usage: IF_FEATURE_FIND_PERM(
1076     //usage: "\n -perm NNN Permissions match any of (+NNN), all of (-NNN),"
1077     //usage: "\n or exactly NNN"
1078     //usage: )
1079     //usage: IF_FEATURE_FIND_MTIME(
1080     //usage: "\n -mtime DAYS Modified time is greater than (+N), less than (-N),"
1081     //usage: "\n or exactly N days"
1082     //usage: )
1083     //usage: IF_FEATURE_FIND_MMIN(
1084     //usage: "\n -mmin MINS Modified time is greater than (+N), less than (-N),"
1085     //usage: "\n or exactly N minutes"
1086     //usage: )
1087     //usage: IF_FEATURE_FIND_NEWER(
1088     //usage: "\n -newer FILE Modified time is more recent than FILE's"
1089     //usage: )
1090     //usage: IF_FEATURE_FIND_INUM(
1091     //usage: "\n -inum N File has inode number N"
1092     //usage: )
1093     //usage: IF_FEATURE_FIND_USER(
1094     //usage: "\n -user NAME File is owned by user NAME (numeric user ID allowed)"
1095     //usage: )
1096     //usage: IF_FEATURE_FIND_GROUP(
1097     //usage: "\n -group NAME File belongs to group NAME (numeric group ID allowed)"
1098     //usage: )
1099     //usage: IF_FEATURE_FIND_DEPTH(
1100     //usage: "\n -depth Process directory name after traversing it"
1101     //usage: )
1102     //usage: IF_FEATURE_FIND_SIZE(
1103     //usage: "\n -size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
1104     //usage: "\n +/-N: file size is bigger/smaller than N"
1105     //usage: )
1106     //usage: IF_FEATURE_FIND_LINKS(
1107     //usage: "\n -links N Number of links is greater than (+N), less than (-N),"
1108     //usage: "\n or exactly N"
1109     //usage: )
1110     //usage: "\n -print Print (default and assumed)"
1111     //usage: IF_FEATURE_FIND_PRINT0(
1112     //usage: "\n -print0 Delimit output with null characters rather than"
1113     //usage: "\n newlines"
1114     //usage: )
1115     //usage: IF_FEATURE_FIND_CONTEXT(
1116     //usage: "\n -context File has specified security context"
1117     //usage: )
1118     //usage: IF_FEATURE_FIND_EXEC(
1119     //usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by the"
1120     //usage: "\n matching files"
1121     //usage: )
1122     //usage: IF_FEATURE_FIND_PRUNE(
1123     //usage: "\n -prune Stop traversing current subtree"
1124     //usage: )
1125     //usage: IF_FEATURE_FIND_DELETE(
1126     //usage: "\n -delete Delete files, turns on -depth option"
1127     //usage: )
1128     //usage: IF_FEATURE_FIND_PAREN(
1129     //usage: "\n (EXPR) Group an expression"
1130     //usage: )
1131     //usage:
1132     //usage:#define find_example_usage
1133     //usage: "$ find / -name passwd\n"
1134     //usage: "/etc/passwd\n"
1135 niro 532
1136 niro 816 int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1137 niro 984 int find_main(int argc UNUSED_PARAM, char **argv)
1138 niro 532 {
1139 niro 816 static const char options[] ALIGN1 =
1140     "-follow\0"
1141 niro 984 IF_FEATURE_FIND_XDEV( "-xdev\0" )
1142     IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
1143 niro 816 ;
1144     enum {
1145     OPT_FOLLOW,
1146 niro 984 IF_FEATURE_FIND_XDEV( OPT_XDEV ,)
1147     IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,)
1148 niro 816 };
1149    
1150 niro 532 char *arg;
1151     char **argp;
1152     int i, firstopt, status = EXIT_SUCCESS;
1153 niro 816 #if ENABLE_FEATURE_FIND_MAXDEPTH
1154 niro 984 int minmaxdepth[2] = { 0, INT_MAX };
1155     #else
1156     #define minmaxdepth NULL
1157 niro 816 #endif
1158 niro 532
1159 niro 984 INIT_G();
1160    
1161     for (firstopt = 1; argv[firstopt]; firstopt++) {
1162 niro 532 if (argv[firstopt][0] == '-')
1163     break;
1164 niro 816 if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!'))
1165     break;
1166     #if ENABLE_FEATURE_FIND_PAREN
1167 niro 532 if (LONE_CHAR(argv[firstopt], '('))
1168     break;
1169     #endif
1170     }
1171     if (firstopt == 1) {
1172 niro 816 argv[0] = (char*)".";
1173 niro 532 argv--;
1174     firstopt++;
1175     }
1176    
1177 niro 816 /* All options always return true. They always take effect
1178     * rather than being processed only when their place in the
1179     * expression is reached.
1180     * We implement: -follow, -xdev, -maxdepth
1181     */
1182 niro 532 /* Process options, and replace then with -a */
1183     /* (-a will be ignored by recursive parser later) */
1184     argp = &argv[firstopt];
1185     while ((arg = argp[0])) {
1186 niro 816 int opt = index_in_strings(options, arg);
1187     if (opt == OPT_FOLLOW) {
1188 niro 984 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
1189 niro 816 argp[0] = (char*)"-a";
1190 niro 532 }
1191     #if ENABLE_FEATURE_FIND_XDEV
1192 niro 816 if (opt == OPT_XDEV) {
1193 niro 532 struct stat stbuf;
1194 niro 984 if (!G.xdev_count) {
1195     G.xdev_count = firstopt - 1;
1196     G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
1197 niro 532 for (i = 1; i < firstopt; i++) {
1198     /* not xstat(): shouldn't bomb out on
1199     * "find not_exist exist -xdev" */
1200 niro 984 if (stat(argv[i], &stbuf) == 0)
1201     G.xdev_dev[i-1] = stbuf.st_dev;
1202     /* else G.xdev_dev[i-1] stays 0 and
1203     * won't match any real device dev_t */
1204 niro 532 }
1205     }
1206 niro 816 argp[0] = (char*)"-a";
1207 niro 532 }
1208     #endif
1209 niro 816 #if ENABLE_FEATURE_FIND_MAXDEPTH
1210 niro 984 if (opt == OPT_MINDEPTH || opt == OPT_MINDEPTH + 1) {
1211 niro 816 if (!argp[1])
1212     bb_show_usage();
1213 niro 984 minmaxdepth[opt - OPT_MINDEPTH] = xatoi_u(argp[1]);
1214 niro 816 argp[0] = (char*)"-a";
1215     argp[1] = (char*)"-a";
1216     argp++;
1217     }
1218     #endif
1219 niro 532 argp++;
1220     }
1221    
1222 niro 984 G.actions = parse_params(&argv[firstopt]);
1223 niro 532
1224     for (i = 1; i < firstopt; i++) {
1225     if (!recursive_action(argv[i],
1226 niro 984 G.recurse_flags,/* flags */
1227 niro 816 fileAction, /* file action */
1228     fileAction, /* dir action */
1229     #if ENABLE_FEATURE_FIND_MAXDEPTH
1230 niro 984 minmaxdepth, /* user data */
1231 niro 816 #else
1232     NULL, /* user data */
1233     #endif
1234     0)) /* depth */
1235 niro 532 status = EXIT_FAILURE;
1236     }
1237     return status;
1238     }