Magellan Linux

Annotation of /tags/mkinitrd-6_3_1/busybox/findutils/find.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 984 - (hide annotations) (download)
Sun May 30 11:32:42 2010 UTC (14 years, 3 months ago) by niro
Original Path: trunk/mkinitrd-magellan/busybox/findutils/find.c
File MIME type: text/plain
File size: 25896 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
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 532 #include <fnmatch.h>
57 niro 816 #include "libbb.h"
58     #if ENABLE_FEATURE_FIND_REGEX
59     #include "xregex.h"
60     #endif
61 niro 532
62 niro 816 /* This is a NOEXEC applet. Be very careful! */
63    
64    
65 niro 984 typedef int (*action_fp)(const char *fileName, const struct stat *statbuf, void *) FAST_FUNC;
66 niro 532
67     typedef struct {
68     action_fp f;
69 niro 816 #if ENABLE_FEATURE_FIND_NOT
70     bool invert;
71     #endif
72 niro 532 } action;
73    
74 niro 984 #define ACTS(name, ...) typedef struct { action a; __VA_ARGS__ } action_##name;
75     #define ACTF(name) \
76     static int FAST_FUNC func_##name(const char *fileName UNUSED_PARAM, \
77     const struct stat *statbuf UNUSED_PARAM, \
78     action_##name* ap UNUSED_PARAM)
79 niro 532
80 niro 984 ACTS(print)
81     ACTS(name, const char *pattern; bool iname;)
82     IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern;))
83     IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;))
84     IF_FEATURE_FIND_PRINT0( ACTS(print0))
85     IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;))
86     IF_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;))
87     IF_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; unsigned mtime_days;))
88     IF_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; unsigned mmin_mins;))
89     IF_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;))
90     IF_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;))
91     IF_FEATURE_FIND_USER( ACTS(user, uid_t uid;))
92     IF_FEATURE_FIND_SIZE( ACTS(size, char size_char; off_t size;))
93     IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
94     IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;))
95     IF_FEATURE_FIND_PRUNE( ACTS(prune))
96     IF_FEATURE_FIND_DELETE( ACTS(delete))
97     IF_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; unsigned *subst_count; int exec_argc;))
98     IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;))
99     IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;))
100    
101     struct globals {
102     IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;)
103     IF_FEATURE_FIND_XDEV(int xdev_count;)
104     action ***actions;
105     bool need_print;
106     recurse_flags_t recurse_flags;
107     };
108     #define G (*(struct globals*)&bb_common_bufsiz1)
109     #define INIT_G() do { \
110     struct G_sizecheck { \
111     char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
112     }; \
113     /* we have to zero it out because of NOEXEC */ \
114     memset(&G, 0, offsetof(struct globals, need_print)); \
115     G.need_print = 1; \
116     G.recurse_flags = ACTION_RECURSE; \
117     } while (0)
118    
119 niro 532 #if ENABLE_FEATURE_FIND_EXEC
120 niro 816 static unsigned count_subst(const char *str)
121 niro 532 {
122 niro 816 unsigned count = 0;
123     while ((str = strstr(str, "{}")) != NULL) {
124 niro 532 count++;
125     str++;
126     }
127     return count;
128     }
129    
130    
131 niro 816 static char* subst(const char *src, unsigned count, const char* filename)
132 niro 532 {
133     char *buf, *dst, *end;
134 niro 816 size_t flen = strlen(filename);
135 niro 532 /* we replace each '{}' with filename: growth by strlen-2 */
136     buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1);
137     while ((end = strstr(src, "{}"))) {
138     memcpy(dst, src, end - src);
139     dst += end - src;
140     src = end + 2;
141     memcpy(dst, filename, flen);
142     dst += flen;
143     }
144     strcpy(dst, src);
145     return buf;
146     }
147     #endif
148    
149 niro 816 /* Return values of ACTFs ('action functions') are a bit mask:
150     * bit 1=1: prune (use SKIP constant for setting it)
151     * bit 0=1: matched successfully (TRUE)
152     */
153 niro 532
154 niro 984 static int exec_actions(action ***appp, const char *fileName, const struct stat *statbuf)
155 niro 532 {
156     int cur_group;
157     int cur_action;
158 niro 816 int rc = 0;
159 niro 532 action **app, *ap;
160    
161 niro 816 /* "action group" is a set of actions ANDed together.
162     * groups are ORed together.
163     * We simply evaluate each group until we find one in which all actions
164     * succeed. */
165    
166     /* -prune is special: if it is encountered, then we won't
167     * descend into current directory. It doesn't matter whether
168     * action group (in which -prune sits) will succeed or not:
169     * find * -prune -name 'f*' -o -name 'm*' -- prunes every dir
170     * find * -name 'f*' -o -prune -name 'm*' -- prunes all dirs
171     * not starting with 'f' */
172    
173     /* We invert TRUE bit (bit 0). Now 1 there means 'failure'.
174     * and bitwise OR in "rc |= TRUE ^ ap->f()" will:
175     * (1) make SKIP (-prune) bit stick; and (2) detect 'failure'.
176     * On return, bit is restored. */
177    
178 niro 532 cur_group = -1;
179 niro 984 while ((app = appp[++cur_group]) != NULL) {
180 niro 816 rc &= ~TRUE; /* 'success' so far, clear TRUE bit */
181 niro 532 cur_action = -1;
182 niro 816 while (1) {
183 niro 532 ap = app[++cur_action];
184 niro 816 if (!ap) /* all actions in group were successful */
185     return rc ^ TRUE; /* restore TRUE bit */
186     rc |= TRUE ^ ap->f(fileName, statbuf, ap);
187     #if ENABLE_FEATURE_FIND_NOT
188     if (ap->invert) rc ^= TRUE;
189     #endif
190     if (rc & TRUE) /* current group failed, try next */
191     break;
192 niro 532 }
193     }
194 niro 816 return rc ^ TRUE; /* restore TRUE bit */
195 niro 532 }
196    
197    
198     ACTF(name)
199     {
200 niro 816 const char *tmp = bb_basename(fileName);
201     if (tmp != fileName && !*tmp) { /* "foo/bar/". Oh no... go back to 'b' */
202     tmp--;
203     while (tmp != fileName && *--tmp != '/')
204     continue;
205     if (*tmp == '/')
206     tmp++;
207     }
208     return fnmatch(ap->pattern, tmp, FNM_PERIOD | (ap->iname ? FNM_CASEFOLD : 0)) == 0;
209 niro 532 }
210 niro 816
211     #if ENABLE_FEATURE_FIND_PATH
212     ACTF(path)
213     {
214     return fnmatch(ap->pattern, fileName, 0) == 0;
215     }
216     #endif
217     #if ENABLE_FEATURE_FIND_REGEX
218     ACTF(regex)
219     {
220     regmatch_t match;
221     if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 /*eflags*/))
222     return 0; /* no match */
223     if (match.rm_so)
224     return 0; /* match doesn't start at pos 0 */
225     if (fileName[match.rm_eo])
226     return 0; /* match doesn't end exactly at end of pathname */
227     return 1;
228     }
229     #endif
230 niro 532 #if ENABLE_FEATURE_FIND_TYPE
231     ACTF(type)
232     {
233     return ((statbuf->st_mode & S_IFMT) == ap->type_mask);
234     }
235     #endif
236     #if ENABLE_FEATURE_FIND_PERM
237     ACTF(perm)
238     {
239 niro 816 /* -perm +mode: at least one of perm_mask bits are set */
240     if (ap->perm_char == '+')
241     return (statbuf->st_mode & ap->perm_mask) != 0;
242     /* -perm -mode: all of perm_mask are set */
243     if (ap->perm_char == '-')
244     return (statbuf->st_mode & ap->perm_mask) == ap->perm_mask;
245     /* -perm mode: file mode must match perm_mask */
246     return (statbuf->st_mode & 07777) == ap->perm_mask;
247 niro 532 }
248     #endif
249     #if ENABLE_FEATURE_FIND_MTIME
250     ACTF(mtime)
251     {
252     time_t file_age = time(NULL) - statbuf->st_mtime;
253 niro 816 time_t mtime_secs = ap->mtime_days * 24*60*60;
254     if (ap->mtime_char == '+')
255     return file_age >= mtime_secs + 24*60*60;
256     if (ap->mtime_char == '-')
257     return file_age < mtime_secs;
258     /* just numeric mtime */
259     return file_age >= mtime_secs && file_age < (mtime_secs + 24*60*60);
260 niro 532 }
261     #endif
262     #if ENABLE_FEATURE_FIND_MMIN
263     ACTF(mmin)
264     {
265     time_t file_age = time(NULL) - statbuf->st_mtime;
266     time_t mmin_secs = ap->mmin_mins * 60;
267 niro 816 if (ap->mmin_char == '+')
268     return file_age >= mmin_secs + 60;
269     if (ap->mmin_char == '-')
270     return file_age < mmin_secs;
271     /* just numeric mmin */
272     return file_age >= mmin_secs && file_age < (mmin_secs + 60);
273 niro 532 }
274     #endif
275     #if ENABLE_FEATURE_FIND_NEWER
276     ACTF(newer)
277     {
278 niro 816 return (ap->newer_mtime < statbuf->st_mtime);
279 niro 532 }
280     #endif
281     #if ENABLE_FEATURE_FIND_INUM
282     ACTF(inum)
283     {
284 niro 816 return (statbuf->st_ino == ap->inode_num);
285 niro 532 }
286     #endif
287     #if ENABLE_FEATURE_FIND_EXEC
288     ACTF(exec)
289     {
290     int i, rc;
291 niro 984 #if ENABLE_USE_PORTABLE_CODE
292     char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1));
293     #else /* gcc 4.3.1 generates smaller code: */
294 niro 816 char *argv[ap->exec_argc + 1];
295 niro 984 #endif
296 niro 532 for (i = 0; i < ap->exec_argc; i++)
297     argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName);
298     argv[i] = NULL; /* terminate the list */
299 niro 816
300     rc = spawn_and_wait(argv);
301     if (rc < 0)
302     bb_simple_perror_msg(argv[0]);
303    
304     i = 0;
305     while (argv[i])
306     free(argv[i++]);
307     return rc == 0; /* return 1 if exitcode 0 */
308 niro 532 }
309     #endif
310 niro 816 #if ENABLE_FEATURE_FIND_USER
311     ACTF(user)
312     {
313     return (statbuf->st_uid == ap->uid);
314     }
315     #endif
316     #if ENABLE_FEATURE_FIND_GROUP
317     ACTF(group)
318     {
319     return (statbuf->st_gid == ap->gid);
320     }
321     #endif
322 niro 532 #if ENABLE_FEATURE_FIND_PRINT0
323     ACTF(print0)
324     {
325     printf("%s%c", fileName, '\0');
326     return TRUE;
327     }
328     #endif
329     ACTF(print)
330     {
331     puts(fileName);
332     return TRUE;
333     }
334 niro 816 #if ENABLE_FEATURE_FIND_PAREN
335 niro 532 ACTF(paren)
336     {
337     return exec_actions(ap->subexpr, fileName, statbuf);
338     }
339 niro 816 #endif
340     #if ENABLE_FEATURE_FIND_SIZE
341     ACTF(size)
342     {
343     if (ap->size_char == '+')
344     return statbuf->st_size > ap->size;
345     if (ap->size_char == '-')
346     return statbuf->st_size < ap->size;
347     return statbuf->st_size == ap->size;
348     }
349     #endif
350     #if ENABLE_FEATURE_FIND_PRUNE
351 niro 532 /*
352     * -prune: if -depth is not given, return true and do not descend
353     * current dir; if -depth is given, return false with no effect.
354     * Example:
355     * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print
356     */
357     ACTF(prune)
358     {
359 niro 816 return SKIP + TRUE;
360 niro 532 }
361 niro 816 #endif
362     #if ENABLE_FEATURE_FIND_DELETE
363     ACTF(delete)
364 niro 532 {
365 niro 816 int rc;
366     if (S_ISDIR(statbuf->st_mode)) {
367     rc = rmdir(fileName);
368     } else {
369     rc = unlink(fileName);
370     }
371     if (rc < 0)
372     bb_simple_perror_msg(fileName);
373     return TRUE;
374 niro 532 }
375     #endif
376 niro 816 #if ENABLE_FEATURE_FIND_CONTEXT
377     ACTF(context)
378     {
379     security_context_t con;
380     int rc;
381 niro 532
382 niro 984 if (G.recurse_flags & ACTION_FOLLOWLINKS) {
383 niro 816 rc = getfilecon(fileName, &con);
384     } else {
385     rc = lgetfilecon(fileName, &con);
386     }
387     if (rc < 0)
388     return FALSE;
389     rc = strcmp(ap->context, con);
390     freecon(con);
391     return rc == 0;
392     }
393     #endif
394 niro 984 #if ENABLE_FEATURE_FIND_LINKS
395     ACTF(links)
396     {
397     switch(ap->links_char) {
398     case '-' : return (statbuf->st_nlink < ap->links_count);
399     case '+' : return (statbuf->st_nlink > ap->links_count);
400     default: return (statbuf->st_nlink == ap->links_count);
401     }
402     }
403     #endif
404 niro 532
405 niro 816 static int FAST_FUNC fileAction(const char *fileName,
406     struct stat *statbuf,
407 niro 984 void *userData IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM),
408     int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM))
409 niro 532 {
410 niro 984 int r;
411 niro 816 #if ENABLE_FEATURE_FIND_MAXDEPTH
412 niro 984 #define minmaxdepth ((int*)userData)
413 niro 816
414 niro 984 if (depth < minmaxdepth[0])
415     return TRUE; /* skip this, continue recursing */
416     if (depth > minmaxdepth[1])
417     return SKIP; /* stop recursing */
418 niro 816 #endif
419    
420 niro 984 r = exec_actions(G.actions, fileName, statbuf);
421     /* Had no explicit -print[0] or -exec? then print */
422     if ((r & TRUE) && G.need_print)
423     puts(fileName);
424    
425     #if ENABLE_FEATURE_FIND_MAXDEPTH
426     if (S_ISDIR(statbuf->st_mode)) {
427     if (depth == minmaxdepth[1])
428     return SKIP;
429     }
430     #endif
431 niro 816 #if ENABLE_FEATURE_FIND_XDEV
432 niro 984 /* -xdev stops on mountpoints, but AFTER mountpoit itself
433     * is processed as usual */
434     if (S_ISDIR(statbuf->st_mode)) {
435     if (G.xdev_count) {
436     int i;
437     for (i = 0; i < G.xdev_count; i++) {
438     if (G.xdev_dev[i] == statbuf->st_dev)
439     goto found;
440     }
441     return SKIP;
442     found: ;
443 niro 532 }
444     }
445     #endif
446 niro 984
447 niro 532 /* Cannot return 0: our caller, recursive_action(),
448     * will perror() and skip dirs (if called on dir) */
449 niro 984 return (r & SKIP) ? SKIP : TRUE;
450     #undef minmaxdepth
451 niro 532 }
452    
453    
454     #if ENABLE_FEATURE_FIND_TYPE
455 niro 816 static int find_type(const char *type)
456 niro 532 {
457     int mask = 0;
458    
459 niro 816 if (*type == 'b')
460 niro 532 mask = S_IFBLK;
461 niro 816 else if (*type == 'c')
462 niro 532 mask = S_IFCHR;
463 niro 816 else if (*type == 'd')
464 niro 532 mask = S_IFDIR;
465 niro 816 else if (*type == 'p')
466 niro 532 mask = S_IFIFO;
467 niro 816 else if (*type == 'f')
468 niro 532 mask = S_IFREG;
469 niro 816 else if (*type == 'l')
470 niro 532 mask = S_IFLNK;
471 niro 816 else if (*type == 's')
472 niro 532 mask = S_IFSOCK;
473    
474 niro 984 if (mask == 0 || type[1] != '\0')
475 niro 532 bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
476    
477     return mask;
478     }
479     #endif
480    
481 niro 816 #if ENABLE_FEATURE_FIND_PERM \
482     || ENABLE_FEATURE_FIND_MTIME || ENABLE_FEATURE_FIND_MMIN \
483 niro 984 || ENABLE_FEATURE_FIND_SIZE || ENABLE_FEATURE_FIND_LINKS
484 niro 816 static const char* plus_minus_num(const char* str)
485 niro 532 {
486 niro 816 if (*str == '-' || *str == '+')
487     str++;
488     return str;
489     }
490     #endif
491    
492     static action*** parse_params(char **argv)
493     {
494     enum {
495 niro 984 PARM_a ,
496     PARM_o ,
497     IF_FEATURE_FIND_NOT( PARM_char_not ,)
498 niro 816 #if ENABLE_DESKTOP
499 niro 984 PARM_and ,
500     PARM_or ,
501     IF_FEATURE_FIND_NOT( PARM_not ,)
502 niro 816 #endif
503 niro 984 PARM_print ,
504     IF_FEATURE_FIND_PRINT0( PARM_print0 ,)
505     IF_FEATURE_FIND_DEPTH( PARM_depth ,)
506     IF_FEATURE_FIND_PRUNE( PARM_prune ,)
507     IF_FEATURE_FIND_DELETE( PARM_delete ,)
508     IF_FEATURE_FIND_EXEC( PARM_exec ,)
509     IF_FEATURE_FIND_PAREN( PARM_char_brace,)
510 niro 816 /* All options starting from here require argument */
511 niro 984 PARM_name ,
512     PARM_iname ,
513     IF_FEATURE_FIND_PATH( PARM_path ,)
514     IF_FEATURE_FIND_REGEX( PARM_regex ,)
515     IF_FEATURE_FIND_TYPE( PARM_type ,)
516     IF_FEATURE_FIND_PERM( PARM_perm ,)
517     IF_FEATURE_FIND_MTIME( PARM_mtime ,)
518     IF_FEATURE_FIND_MMIN( PARM_mmin ,)
519     IF_FEATURE_FIND_NEWER( PARM_newer ,)
520     IF_FEATURE_FIND_INUM( PARM_inum ,)
521     IF_FEATURE_FIND_USER( PARM_user ,)
522     IF_FEATURE_FIND_GROUP( PARM_group ,)
523     IF_FEATURE_FIND_SIZE( PARM_size ,)
524     IF_FEATURE_FIND_CONTEXT(PARM_context ,)
525     IF_FEATURE_FIND_LINKS( PARM_links ,)
526 niro 816 };
527    
528     static const char params[] ALIGN1 =
529     "-a\0"
530     "-o\0"
531 niro 984 IF_FEATURE_FIND_NOT( "!\0" )
532 niro 816 #if ENABLE_DESKTOP
533     "-and\0"
534     "-or\0"
535 niro 984 IF_FEATURE_FIND_NOT( "-not\0" )
536 niro 816 #endif
537     "-print\0"
538 niro 984 IF_FEATURE_FIND_PRINT0( "-print0\0" )
539     IF_FEATURE_FIND_DEPTH( "-depth\0" )
540     IF_FEATURE_FIND_PRUNE( "-prune\0" )
541     IF_FEATURE_FIND_DELETE( "-delete\0" )
542     IF_FEATURE_FIND_EXEC( "-exec\0" )
543     IF_FEATURE_FIND_PAREN( "(\0" )
544 niro 816 /* All options starting from here require argument */
545     "-name\0"
546     "-iname\0"
547 niro 984 IF_FEATURE_FIND_PATH( "-path\0" )
548     IF_FEATURE_FIND_REGEX( "-regex\0" )
549     IF_FEATURE_FIND_TYPE( "-type\0" )
550     IF_FEATURE_FIND_PERM( "-perm\0" )
551     IF_FEATURE_FIND_MTIME( "-mtime\0" )
552     IF_FEATURE_FIND_MMIN( "-mmin\0" )
553     IF_FEATURE_FIND_NEWER( "-newer\0" )
554     IF_FEATURE_FIND_INUM( "-inum\0" )
555     IF_FEATURE_FIND_USER( "-user\0" )
556     IF_FEATURE_FIND_GROUP( "-group\0" )
557     IF_FEATURE_FIND_SIZE( "-size\0" )
558     IF_FEATURE_FIND_CONTEXT("-context\0")
559     IF_FEATURE_FIND_LINKS( "-links\0" )
560 niro 816 ;
561    
562 niro 532 action*** appp;
563 niro 816 unsigned cur_group = 0;
564     unsigned cur_action = 0;
565 niro 984 IF_FEATURE_FIND_NOT( bool invert_flag = 0; )
566 niro 532
567 niro 816 /* This is the only place in busybox where we use nested function.
568     * So far more standard alternatives were bigger. */
569     /* Suppress a warning "func without a prototype" */
570     auto action* alloc_action(int sizeof_struct, action_fp f);
571 niro 532 action* alloc_action(int sizeof_struct, action_fp f)
572     {
573     action *ap;
574     appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(*appp));
575     appp[cur_group][cur_action++] = ap = xmalloc(sizeof_struct);
576     appp[cur_group][cur_action] = NULL;
577     ap->f = f;
578 niro 984 IF_FEATURE_FIND_NOT( ap->invert = invert_flag; )
579     IF_FEATURE_FIND_NOT( invert_flag = 0; )
580 niro 532 return ap;
581     }
582 niro 816
583 niro 532 #define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name)
584    
585 niro 816 appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */
586 niro 532
587 niro 816 /* Actions have side effects and return a true or false value
588     * We implement: -print, -print0, -exec
589     *
590     * The rest are tests.
591     *
592     * Tests and actions are grouped by operators
593     * ( expr ) Force precedence
594     * ! expr True if expr is false
595     * -not expr Same as ! expr
596     * expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false
597     * expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true
598     * expr1 , expr2 List; both expr1 and expr2 are always evaluated
599     * We implement: (), -a, -o
600     */
601     while (*argv) {
602     const char *arg = argv[0];
603     int parm = index_in_strings(params, arg);
604     const char *arg1 = argv[1];
605 niro 532
606 niro 816 if (parm >= PARM_name) {
607     /* All options starting from -name require argument */
608     if (!arg1)
609     bb_error_msg_and_die(bb_msg_requires_arg, arg);
610     argv++;
611     }
612 niro 532
613 niro 816 /* We can use big switch() here, but on i386
614     * it doesn't give smaller code. Other arches? */
615 niro 532
616     /* --- Operators --- */
617 niro 984 if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
618 niro 816 /* no further special handling required */
619 niro 532 }
620 niro 984 else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
621 niro 532 /* start new OR group */
622     cur_group++;
623     appp = xrealloc(appp, (cur_group+2) * sizeof(*appp));
624 niro 816 /*appp[cur_group] = NULL; - already NULL */
625 niro 532 appp[cur_group+1] = NULL;
626     cur_action = 0;
627     }
628 niro 816 #if ENABLE_FEATURE_FIND_NOT
629 niro 984 else if (parm == PARM_char_not IF_DESKTOP(|| parm == PARM_not)) {
630 niro 816 /* also handles "find ! ! -name 'foo*'" */
631     invert_flag ^= 1;
632     }
633     #endif
634 niro 532
635     /* --- Tests and actions --- */
636 niro 816 else if (parm == PARM_print) {
637 niro 984 G.need_print = 0;
638 niro 816 /* GNU find ignores '!' here: "find ! -print" */
639 niro 984 IF_FEATURE_FIND_NOT( invert_flag = 0; )
640 niro 532 (void) ALLOC_ACTION(print);
641     }
642     #if ENABLE_FEATURE_FIND_PRINT0
643 niro 816 else if (parm == PARM_print0) {
644 niro 984 G.need_print = 0;
645     IF_FEATURE_FIND_NOT( invert_flag = 0; )
646 niro 532 (void) ALLOC_ACTION(print0);
647     }
648     #endif
649 niro 816 #if ENABLE_FEATURE_FIND_DEPTH
650     else if (parm == PARM_depth) {
651 niro 984 G.recurse_flags |= ACTION_DEPTHFIRST;
652 niro 816 }
653     #endif
654     #if ENABLE_FEATURE_FIND_PRUNE
655     else if (parm == PARM_prune) {
656 niro 984 IF_FEATURE_FIND_NOT( invert_flag = 0; )
657 niro 816 (void) ALLOC_ACTION(prune);
658     }
659     #endif
660     #if ENABLE_FEATURE_FIND_DELETE
661     else if (parm == PARM_delete) {
662 niro 984 G.need_print = 0;
663     G.recurse_flags |= ACTION_DEPTHFIRST;
664 niro 816 (void) ALLOC_ACTION(delete);
665     }
666     #endif
667     #if ENABLE_FEATURE_FIND_EXEC
668     else if (parm == PARM_exec) {
669     int i;
670     action_exec *ap;
671 niro 984 G.need_print = 0;
672     IF_FEATURE_FIND_NOT( invert_flag = 0; )
673 niro 816 ap = ALLOC_ACTION(exec);
674     ap->exec_argv = ++argv; /* first arg after -exec */
675     ap->exec_argc = 0;
676     while (1) {
677     if (!*argv) /* did not see ';' until end */
678     bb_error_msg_and_die("-exec CMD must end by ';'");
679     if (LONE_CHAR(argv[0], ';'))
680     break;
681     argv++;
682     ap->exec_argc++;
683     }
684     if (ap->exec_argc == 0)
685     bb_error_msg_and_die(bb_msg_requires_arg, arg);
686     ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
687     i = ap->exec_argc;
688     while (i--)
689     ap->subst_count[i] = count_subst(ap->exec_argv[i]);
690     }
691     #endif
692     #if ENABLE_FEATURE_FIND_PAREN
693     else if (parm == PARM_char_brace) {
694     action_paren *ap;
695     char **endarg;
696     unsigned nested = 1;
697    
698     endarg = argv;
699     while (1) {
700     if (!*++endarg)
701     bb_error_msg_and_die("unpaired '('");
702     if (LONE_CHAR(*endarg, '('))
703     nested++;
704     else if (LONE_CHAR(*endarg, ')') && !--nested) {
705     *endarg = NULL;
706     break;
707     }
708     }
709     ap = ALLOC_ACTION(paren);
710     ap->subexpr = parse_params(argv + 1);
711     *endarg = (char*) ")"; /* restore NULLed parameter */
712     argv = endarg;
713     }
714     #endif
715     else if (parm == PARM_name || parm == PARM_iname) {
716 niro 532 action_name *ap;
717     ap = ALLOC_ACTION(name);
718     ap->pattern = arg1;
719 niro 816 ap->iname = (parm == PARM_iname);
720 niro 532 }
721 niro 816 #if ENABLE_FEATURE_FIND_PATH
722     else if (parm == PARM_path) {
723     action_path *ap;
724     ap = ALLOC_ACTION(path);
725     ap->pattern = arg1;
726     }
727     #endif
728     #if ENABLE_FEATURE_FIND_REGEX
729     else if (parm == PARM_regex) {
730     action_regex *ap;
731     ap = ALLOC_ACTION(regex);
732     xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/);
733     }
734     #endif
735 niro 532 #if ENABLE_FEATURE_FIND_TYPE
736 niro 816 else if (parm == PARM_type) {
737 niro 532 action_type *ap;
738     ap = ALLOC_ACTION(type);
739     ap->type_mask = find_type(arg1);
740     }
741     #endif
742     #if ENABLE_FEATURE_FIND_PERM
743 niro 816 /* -perm mode File's permission bits are exactly mode (octal or symbolic).
744 niro 532 * Symbolic modes use mode 0 as a point of departure.
745     * -perm -mode All of the permission bits mode are set for the file.
746     * -perm +mode Any of the permission bits mode are set for the file.
747     */
748 niro 816 else if (parm == PARM_perm) {
749 niro 532 action_perm *ap;
750     ap = ALLOC_ACTION(perm);
751     ap->perm_char = arg1[0];
752 niro 816 arg1 = plus_minus_num(arg1);
753     ap->perm_mask = 0;
754     if (!bb_parse_mode(arg1, &ap->perm_mask))
755     bb_error_msg_and_die("invalid mode: %s", arg1);
756 niro 532 }
757     #endif
758     #if ENABLE_FEATURE_FIND_MTIME
759 niro 816 else if (parm == PARM_mtime) {
760 niro 532 action_mtime *ap;
761     ap = ALLOC_ACTION(mtime);
762     ap->mtime_char = arg1[0];
763 niro 816 ap->mtime_days = xatoul(plus_minus_num(arg1));
764 niro 532 }
765     #endif
766     #if ENABLE_FEATURE_FIND_MMIN
767 niro 816 else if (parm == PARM_mmin) {
768 niro 532 action_mmin *ap;
769     ap = ALLOC_ACTION(mmin);
770     ap->mmin_char = arg1[0];
771 niro 816 ap->mmin_mins = xatoul(plus_minus_num(arg1));
772 niro 532 }
773     #endif
774     #if ENABLE_FEATURE_FIND_NEWER
775 niro 816 else if (parm == PARM_newer) {
776     struct stat stat_newer;
777 niro 532 action_newer *ap;
778 niro 816 ap = ALLOC_ACTION(newer);
779 niro 532 xstat(arg1, &stat_newer);
780     ap->newer_mtime = stat_newer.st_mtime;
781     }
782     #endif
783     #if ENABLE_FEATURE_FIND_INUM
784 niro 816 else if (parm == PARM_inum) {
785 niro 532 action_inum *ap;
786     ap = ALLOC_ACTION(inum);
787     ap->inode_num = xatoul(arg1);
788     }
789     #endif
790 niro 816 #if ENABLE_FEATURE_FIND_USER
791     else if (parm == PARM_user) {
792     action_user *ap;
793     ap = ALLOC_ACTION(user);
794     ap->uid = bb_strtou(arg1, NULL, 10);
795     if (errno)
796     ap->uid = xuname2uid(arg1);
797 niro 532 }
798     #endif
799 niro 816 #if ENABLE_FEATURE_FIND_GROUP
800     else if (parm == PARM_group) {
801     action_group *ap;
802     ap = ALLOC_ACTION(group);
803     ap->gid = bb_strtou(arg1, NULL, 10);
804     if (errno)
805     ap->gid = xgroup2gid(arg1);
806 niro 532 }
807 niro 816 #endif
808     #if ENABLE_FEATURE_FIND_SIZE
809     else if (parm == PARM_size) {
810     /* -size n[bckw]: file uses n units of space
811     * b (default): units are 512-byte blocks
812     * c: 1 byte
813     * k: kilobytes
814     * w: 2-byte words
815     */
816     #if ENABLE_LFS
817     #define XATOU_SFX xatoull_sfx
818     #else
819     #define XATOU_SFX xatoul_sfx
820     #endif
821     static const struct suffix_mult find_suffixes[] = {
822     { "c", 1 },
823     { "w", 2 },
824     { "", 512 },
825     { "b", 512 },
826     { "k", 1024 },
827 niro 984 { "", 0 }
828 niro 816 };
829 niro 532 action_size *ap;
830     ap = ALLOC_ACTION(size);
831 niro 816 ap->size_char = arg1[0];
832     ap->size = XATOU_SFX(plus_minus_num(arg1), find_suffixes);
833 niro 532 }
834     #endif
835 niro 816 #if ENABLE_FEATURE_FIND_CONTEXT
836     else if (parm == PARM_context) {
837     action_context *ap;
838     ap = ALLOC_ACTION(context);
839     ap->context = NULL;
840     /* SELinux headers erroneously declare non-const parameter */
841     if (selinux_raw_to_trans_context((char*)arg1, &ap->context))
842     bb_simple_perror_msg(arg1);
843     }
844     #endif
845 niro 984 #if ENABLE_FEATURE_FIND_LINKS
846     else if (parm == PARM_links) {
847     action_links *ap;
848     ap = ALLOC_ACTION(links);
849     ap->links_char = arg1[0];
850     ap->links_count = xatoul(plus_minus_num(arg1));
851     }
852     #endif
853 niro 816 else {
854     bb_error_msg("unrecognized: %s", arg);
855 niro 532 bb_show_usage();
856 niro 816 }
857 niro 532 argv++;
858     }
859     return appp;
860     #undef ALLOC_ACTION
861     }
862    
863    
864 niro 816 int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
865 niro 984 int find_main(int argc UNUSED_PARAM, char **argv)
866 niro 532 {
867 niro 816 static const char options[] ALIGN1 =
868     "-follow\0"
869 niro 984 IF_FEATURE_FIND_XDEV( "-xdev\0" )
870     IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
871 niro 816 ;
872     enum {
873     OPT_FOLLOW,
874 niro 984 IF_FEATURE_FIND_XDEV( OPT_XDEV ,)
875     IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,)
876 niro 816 };
877    
878 niro 532 char *arg;
879     char **argp;
880     int i, firstopt, status = EXIT_SUCCESS;
881 niro 816 #if ENABLE_FEATURE_FIND_MAXDEPTH
882 niro 984 int minmaxdepth[2] = { 0, INT_MAX };
883     #else
884     #define minmaxdepth NULL
885 niro 816 #endif
886 niro 532
887 niro 984 INIT_G();
888    
889     for (firstopt = 1; argv[firstopt]; firstopt++) {
890 niro 532 if (argv[firstopt][0] == '-')
891     break;
892 niro 816 if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!'))
893     break;
894     #if ENABLE_FEATURE_FIND_PAREN
895 niro 532 if (LONE_CHAR(argv[firstopt], '('))
896     break;
897     #endif
898     }
899     if (firstopt == 1) {
900 niro 816 argv[0] = (char*)".";
901 niro 532 argv--;
902     firstopt++;
903     }
904    
905 niro 816 /* All options always return true. They always take effect
906     * rather than being processed only when their place in the
907     * expression is reached.
908     * We implement: -follow, -xdev, -maxdepth
909     */
910 niro 532 /* Process options, and replace then with -a */
911     /* (-a will be ignored by recursive parser later) */
912     argp = &argv[firstopt];
913     while ((arg = argp[0])) {
914 niro 816 int opt = index_in_strings(options, arg);
915     if (opt == OPT_FOLLOW) {
916 niro 984 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
917 niro 816 argp[0] = (char*)"-a";
918 niro 532 }
919     #if ENABLE_FEATURE_FIND_XDEV
920 niro 816 if (opt == OPT_XDEV) {
921 niro 532 struct stat stbuf;
922 niro 984 if (!G.xdev_count) {
923     G.xdev_count = firstopt - 1;
924     G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
925 niro 532 for (i = 1; i < firstopt; i++) {
926     /* not xstat(): shouldn't bomb out on
927     * "find not_exist exist -xdev" */
928 niro 984 if (stat(argv[i], &stbuf) == 0)
929     G.xdev_dev[i-1] = stbuf.st_dev;
930     /* else G.xdev_dev[i-1] stays 0 and
931     * won't match any real device dev_t */
932 niro 532 }
933     }
934 niro 816 argp[0] = (char*)"-a";
935 niro 532 }
936     #endif
937 niro 816 #if ENABLE_FEATURE_FIND_MAXDEPTH
938 niro 984 if (opt == OPT_MINDEPTH || opt == OPT_MINDEPTH + 1) {
939 niro 816 if (!argp[1])
940     bb_show_usage();
941 niro 984 minmaxdepth[opt - OPT_MINDEPTH] = xatoi_u(argp[1]);
942 niro 816 argp[0] = (char*)"-a";
943     argp[1] = (char*)"-a";
944     argp++;
945     }
946     #endif
947 niro 532 argp++;
948     }
949    
950 niro 984 G.actions = parse_params(&argv[firstopt]);
951 niro 532
952     for (i = 1; i < firstopt; i++) {
953     if (!recursive_action(argv[i],
954 niro 984 G.recurse_flags,/* flags */
955 niro 816 fileAction, /* file action */
956     fileAction, /* dir action */
957     #if ENABLE_FEATURE_FIND_MAXDEPTH
958 niro 984 minmaxdepth, /* user data */
959 niro 816 #else
960     NULL, /* user data */
961     #endif
962     0)) /* depth */
963 niro 532 status = EXIT_FAILURE;
964     }
965     return status;
966     }