source: MondoRescue/branches/3.3/mindi-busybox/findutils/find.c@ 3647

Last change on this file since 3647 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 41.5 KB
Line 
1/* 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 GPLv2, see file LICENSE in this source tree.
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/* 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//config:config FIND
57//config: bool "find"
58//config: default y
59//config: help
60//config: find is used to search your system to find specified files.
61//config:
62//config:config FEATURE_FIND_PRINT0
63//config: bool "Enable -print0: NUL-terminated output"
64//config: default y
65//config: depends on FIND
66//config: help
67//config: Causes output names to be separated by a NUL character
68//config: rather than a newline. This allows names that contain
69//config: newlines and other whitespace to be more easily
70//config: interpreted by other programs.
71//config:
72//config:config FEATURE_FIND_MTIME
73//config: bool "Enable -mtime: modified time matching"
74//config: default y
75//config: depends on FIND
76//config: help
77//config: Allow searching based on the modification time of
78//config: files, in days.
79//config:
80//config:config FEATURE_FIND_MMIN
81//config: bool "Enable -mmin: modified time matching by minutes"
82//config: default y
83//config: depends on FIND
84//config: help
85//config: Allow searching based on the modification time of
86//config: files, in minutes.
87//config:
88//config:config FEATURE_FIND_PERM
89//config: bool "Enable -perm: permissions matching"
90//config: default y
91//config: depends on FIND
92//config: help
93//config: Enable searching based on file permissions.
94//config:
95//config:config FEATURE_FIND_TYPE
96//config: bool "Enable -type: file type matching (file/dir/link/...)"
97//config: default y
98//config: depends on FIND
99//config: help
100//config: Enable searching based on file type (file,
101//config: directory, socket, device, etc.).
102//config:
103//config:config FEATURE_FIND_XDEV
104//config: bool "Enable -xdev: 'stay in filesystem'"
105//config: default y
106//config: depends on FIND
107//config: help
108//config: This option allows find to restrict searches to a single filesystem.
109//config:
110//config:config FEATURE_FIND_MAXDEPTH
111//config: bool "Enable -mindepth N and -maxdepth N"
112//config: default y
113//config: depends on FIND
114//config: help
115//config: This option enables -mindepth N and -maxdepth N option.
116//config:
117//config:config FEATURE_FIND_NEWER
118//config: bool "Enable -newer: compare file modification times"
119//config: default y
120//config: depends on FIND
121//config: help
122//config: Support the 'find -newer' option for finding any files which have
123//config: modification time that is more recent than the specified FILE.
124//config:
125//config:config FEATURE_FIND_INUM
126//config: bool "Enable -inum: inode number matching"
127//config: default y
128//config: depends on FIND
129//config: help
130//config: Support the 'find -inum' option for searching by inode number.
131//config:
132//config:config FEATURE_FIND_EXEC
133//config: bool "Enable -exec: execute commands"
134//config: default y
135//config: depends on FIND
136//config: help
137//config: Support the 'find -exec' option for executing commands based upon
138//config: the files matched.
139//config:
140//config:config FEATURE_FIND_EXEC_PLUS
141//config: bool "Enable -exec ... {} +"
142//config: default y
143//config: depends on FEATURE_FIND_EXEC
144//config: help
145//config: Support the 'find -exec ... {} +' option for executing commands
146//config: for all matched files at once.
147//config: Without this option, -exec + is a synonym for -exec ;
148//config: (IOW: it works correctly, but without expected speedup)
149//config:
150//config:config FEATURE_FIND_USER
151//config: bool "Enable -user: username/uid matching"
152//config: default y
153//config: depends on FIND
154//config: help
155//config: Support the 'find -user' option for searching by username or uid.
156//config:
157//config:config FEATURE_FIND_GROUP
158//config: bool "Enable -group: group/gid matching"
159//config: default y
160//config: depends on FIND
161//config: help
162//config: Support the 'find -group' option for searching by group name or gid.
163//config:
164//config:config FEATURE_FIND_NOT
165//config: bool "Enable the 'not' (!) operator"
166//config: default y
167//config: depends on FIND
168//config: help
169//config: Support the '!' operator to invert the test results.
170//config: If 'Enable full-blown desktop' is enabled, then will also support
171//config: the non-POSIX notation '-not'.
172//config:
173//config:config FEATURE_FIND_DEPTH
174//config: bool "Enable -depth"
175//config: default y
176//config: depends on FIND
177//config: help
178//config: Process each directory's contents before the directory itself.
179//config:
180//config:config FEATURE_FIND_PAREN
181//config: bool "Enable parens in options"
182//config: default y
183//config: depends on FIND
184//config: help
185//config: Enable usage of parens '(' to specify logical order of arguments.
186//config:
187//config:config FEATURE_FIND_SIZE
188//config: bool "Enable -size: file size matching"
189//config: default y
190//config: depends on FIND
191//config: help
192//config: Support the 'find -size' option for searching by file size.
193//config:
194//config:config FEATURE_FIND_PRUNE
195//config: bool "Enable -prune: exclude subdirectories"
196//config: default y
197//config: depends on FIND
198//config: help
199//config: If the file is a directory, dont descend into it. Useful for
200//config: exclusion .svn and CVS directories.
201//config:
202//config:config FEATURE_FIND_DELETE
203//config: bool "Enable -delete: delete files/dirs"
204//config: default y
205//config: depends on FIND && FEATURE_FIND_DEPTH
206//config: help
207//config: Support the 'find -delete' option for deleting files and directories.
208//config: WARNING: This option can do much harm if used wrong. Busybox will not
209//config: try to protect the user from doing stupid things. Use with care.
210//config:
211//config:config FEATURE_FIND_PATH
212//config: bool "Enable -path: match pathname with shell pattern"
213//config: default y
214//config: depends on FIND
215//config: help
216//config: The -path option matches whole pathname instead of just filename.
217//config:
218//config:config FEATURE_FIND_REGEX
219//config: bool "Enable -regex: match pathname with regex"
220//config: default y
221//config: depends on FIND
222//config: help
223//config: The -regex option matches whole pathname against regular expression.
224//config:
225//config:config FEATURE_FIND_CONTEXT
226//config: bool "Enable -context: security context matching"
227//config: default n
228//config: depends on FIND && SELINUX
229//config: help
230//config: Support the 'find -context' option for matching security context.
231//config:
232//config:config FEATURE_FIND_LINKS
233//config: bool "Enable -links: link count matching"
234//config: default y
235//config: depends on FIND
236//config: help
237//config: Support the 'find -links' option for matching number of links.
238
239//applet:IF_FIND(APPLET_NOEXEC(find, find, BB_DIR_USR_BIN, BB_SUID_DROP, find))
240
241//kbuild:lib-$(CONFIG_FIND) += find.o
242
243//usage:#define find_trivial_usage
244//usage: "[-HL] [PATH]... [OPTIONS] [ACTIONS]"
245//usage:#define find_full_usage "\n\n"
246//usage: "Search for files and perform actions on them.\n"
247//usage: "First failed action stops processing of current file.\n"
248//usage: "Defaults: PATH is current directory, action is '-print'\n"
249//usage: "\n -L,-follow Follow symlinks"
250//usage: "\n -H ...on command line only"
251//usage: IF_FEATURE_FIND_XDEV(
252//usage: "\n -xdev Don't descend directories on other filesystems"
253//usage: )
254//usage: IF_FEATURE_FIND_MAXDEPTH(
255//usage: "\n -maxdepth N Descend at most N levels. -maxdepth 0 applies"
256//usage: "\n actions to command line arguments only"
257//usage: "\n -mindepth N Don't act on first N levels"
258//usage: )
259//usage: IF_FEATURE_FIND_DEPTH(
260//usage: "\n -depth Act on directory *after* traversing it"
261//usage: )
262//usage: "\n"
263//usage: "\nActions:"
264//usage: IF_FEATURE_FIND_PAREN(
265//usage: "\n ( ACTIONS ) Group actions for -o / -a"
266//usage: )
267//usage: IF_FEATURE_FIND_NOT(
268//usage: "\n ! ACT Invert ACT's success/failure"
269//usage: )
270//usage: "\n ACT1 [-a] ACT2 If ACT1 fails, stop, else do ACT2"
271//usage: "\n ACT1 -o ACT2 If ACT1 succeeds, stop, else do ACT2"
272//usage: "\n Note: -a has higher priority than -o"
273//usage: "\n -name PATTERN Match file name (w/o directory name) to PATTERN"
274//usage: "\n -iname PATTERN Case insensitive -name"
275//usage: IF_FEATURE_FIND_PATH(
276//usage: "\n -path PATTERN Match path to PATTERN"
277//usage: "\n -ipath PATTERN Case insensitive -path"
278//usage: )
279//usage: IF_FEATURE_FIND_REGEX(
280//usage: "\n -regex PATTERN Match path to regex PATTERN"
281//usage: )
282//usage: IF_FEATURE_FIND_TYPE(
283//usage: "\n -type X File type is X (one of: f,d,l,b,c,...)"
284//usage: )
285//usage: IF_FEATURE_FIND_PERM(
286//usage: "\n -perm MASK At least one mask bit (+MASK), all bits (-MASK),"
287//usage: "\n or exactly MASK bits are set in file's mode"
288//usage: )
289//usage: IF_FEATURE_FIND_MTIME(
290//usage: "\n -mtime DAYS mtime is greater than (+N), less than (-N),"
291//usage: "\n or exactly N days in the past"
292//usage: )
293//usage: IF_FEATURE_FIND_MMIN(
294//usage: "\n -mmin MINS mtime is greater than (+N), less than (-N),"
295//usage: "\n or exactly N minutes in the past"
296//usage: )
297//usage: IF_FEATURE_FIND_NEWER(
298//usage: "\n -newer FILE mtime is more recent than FILE's"
299//usage: )
300//usage: IF_FEATURE_FIND_INUM(
301//usage: "\n -inum N File has inode number N"
302//usage: )
303//usage: IF_FEATURE_FIND_USER(
304//usage: "\n -user NAME/ID File is owned by given user"
305//usage: )
306//usage: IF_FEATURE_FIND_GROUP(
307//usage: "\n -group NAME/ID File is owned by given group"
308//usage: )
309//usage: IF_FEATURE_FIND_SIZE(
310//usage: "\n -size N[bck] File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
311//usage: "\n +/-N: file size is bigger/smaller than N"
312//usage: )
313//usage: IF_FEATURE_FIND_LINKS(
314//usage: "\n -links N Number of links is greater than (+N), less than (-N),"
315//usage: "\n or exactly N"
316//usage: )
317//usage: IF_FEATURE_FIND_CONTEXT(
318//usage: "\n -context CTX File has specified security context"
319//usage: )
320//usage: IF_FEATURE_FIND_PRUNE(
321//usage: "\n -prune If current file is directory, don't descend into it"
322//usage: )
323//usage: "\nIf none of the following actions is specified, -print is assumed"
324//usage: "\n -print Print file name"
325//usage: IF_FEATURE_FIND_PRINT0(
326//usage: "\n -print0 Print file name, NUL terminated"
327//usage: )
328//usage: IF_FEATURE_FIND_EXEC(
329//usage: "\n -exec CMD ARG ; Run CMD with all instances of {} replaced by"
330//usage: "\n file name. Fails if CMD exits with nonzero"
331//usage: )
332//usage: IF_FEATURE_FIND_EXEC_PLUS(
333//usage: "\n -exec CMD ARG + Run CMD with {} replaced by list of file names"
334//usage: )
335//usage: IF_FEATURE_FIND_DELETE(
336//usage: "\n -delete Delete current file/directory. Turns on -depth option"
337//usage: )
338//usage:
339//usage:#define find_example_usage
340//usage: "$ find / -name passwd\n"
341//usage: "/etc/passwd\n"
342
343#include <fnmatch.h>
344#include "libbb.h"
345#include "common_bufsiz.h"
346#if ENABLE_FEATURE_FIND_REGEX
347# include "xregex.h"
348#endif
349/* GNUism: */
350#ifndef FNM_CASEFOLD
351# define FNM_CASEFOLD 0
352#endif
353
354#if 1
355# define dbg(...) ((void)0)
356#else
357# define dbg(...) bb_error_msg(__VA_ARGS__)
358#endif
359
360
361/* This is a NOEXEC applet. Be very careful! */
362
363
364typedef int (*action_fp)(const char *fileName, const struct stat *statbuf, void *) FAST_FUNC;
365
366typedef struct {
367 action_fp f;
368#if ENABLE_FEATURE_FIND_NOT
369 bool invert;
370#endif
371} action;
372
373#define ACTS(name, ...) typedef struct { action a; __VA_ARGS__ } action_##name;
374#define ACTF(name) \
375 static int FAST_FUNC func_##name(const char *fileName UNUSED_PARAM, \
376 const struct stat *statbuf UNUSED_PARAM, \
377 action_##name* ap UNUSED_PARAM)
378
379 ACTS(print)
380 ACTS(name, const char *pattern; bool iname;)
381IF_FEATURE_FIND_PATH( ACTS(path, const char *pattern; bool ipath;))
382IF_FEATURE_FIND_REGEX( ACTS(regex, regex_t compiled_pattern;))
383IF_FEATURE_FIND_PRINT0( ACTS(print0))
384IF_FEATURE_FIND_TYPE( ACTS(type, int type_mask;))
385IF_FEATURE_FIND_PERM( ACTS(perm, char perm_char; mode_t perm_mask;))
386IF_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; unsigned mtime_days;))
387IF_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; unsigned mmin_mins;))
388IF_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;))
389IF_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;))
390IF_FEATURE_FIND_USER( ACTS(user, uid_t uid;))
391IF_FEATURE_FIND_SIZE( ACTS(size, char size_char; off_t size;))
392IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
393IF_FEATURE_FIND_PAREN( ACTS(paren, action ***subexpr;))
394IF_FEATURE_FIND_PRUNE( ACTS(prune))
395IF_FEATURE_FIND_DELETE( ACTS(delete))
396IF_FEATURE_FIND_EXEC( ACTS(exec,
397 char **exec_argv; /* -exec ARGS */
398 unsigned *subst_count;
399 int exec_argc; /* count of ARGS */
400 IF_FEATURE_FIND_EXEC_PLUS(
401 /*
402 * filelist is NULL if "exec ;"
403 * non-NULL if "exec +"
404 */
405 char **filelist;
406 int filelist_idx;
407 int file_len;
408 )
409 ))
410IF_FEATURE_FIND_GROUP( ACTS(group, gid_t gid;))
411IF_FEATURE_FIND_LINKS( ACTS(links, char links_char; int links_count;))
412
413struct globals {
414 IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;)
415 IF_FEATURE_FIND_XDEV(int xdev_count;)
416#if ENABLE_FEATURE_FIND_MAXDEPTH
417 int minmaxdepth[2];
418#endif
419 action ***actions;
420 smallint need_print;
421 smallint xdev_on;
422 recurse_flags_t recurse_flags;
423 IF_FEATURE_FIND_EXEC_PLUS(unsigned max_argv_len;)
424} FIX_ALIASING;
425#define G (*(struct globals*)bb_common_bufsiz1)
426#define INIT_G() do { \
427 setup_common_bufsiz(); \
428 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
429 /* we have to zero it out because of NOEXEC */ \
430 memset(&G, 0, sizeof(G)); \
431 IF_FEATURE_FIND_MAXDEPTH(G.minmaxdepth[1] = INT_MAX;) \
432 IF_FEATURE_FIND_EXEC_PLUS(G.max_argv_len = bb_arg_max() - 2048;) \
433 G.need_print = 1; \
434 G.recurse_flags = ACTION_RECURSE; \
435} while (0)
436
437/* Return values of ACTFs ('action functions') are a bit mask:
438 * bit 1=1: prune (use SKIP constant for setting it)
439 * bit 0=1: matched successfully (TRUE)
440 */
441
442static int exec_actions(action ***appp, const char *fileName, const struct stat *statbuf)
443{
444 int cur_group;
445 int cur_action;
446 int rc = 0;
447 action **app, *ap;
448
449 /* "action group" is a set of actions ANDed together.
450 * groups are ORed together.
451 * We simply evaluate each group until we find one in which all actions
452 * succeed. */
453
454 /* -prune is special: if it is encountered, then we won't
455 * descend into current directory. It doesn't matter whether
456 * action group (in which -prune sits) will succeed or not:
457 * find * -prune -name 'f*' -o -name 'm*' -- prunes every dir
458 * find * -name 'f*' -o -prune -name 'm*' -- prunes all dirs
459 * not starting with 'f' */
460
461 /* We invert TRUE bit (bit 0). Now 1 there means 'failure'.
462 * and bitwise OR in "rc |= TRUE ^ ap->f()" will:
463 * (1) make SKIP (-prune) bit stick; and (2) detect 'failure'.
464 * On return, bit is restored. */
465
466 cur_group = -1;
467 while ((app = appp[++cur_group]) != NULL) {
468 rc &= ~TRUE; /* 'success' so far, clear TRUE bit */
469 cur_action = -1;
470 while (1) {
471 ap = app[++cur_action];
472 if (!ap) /* all actions in group were successful */
473 return rc ^ TRUE; /* restore TRUE bit */
474 rc |= TRUE ^ ap->f(fileName, statbuf, ap);
475#if ENABLE_FEATURE_FIND_NOT
476 if (ap->invert) rc ^= TRUE;
477#endif
478 dbg("grp %d action %d rc:0x%x", cur_group, cur_action, rc);
479 if (rc & TRUE) /* current group failed, try next */
480 break;
481 }
482 }
483 dbg("returning:0x%x", rc ^ TRUE);
484 return rc ^ TRUE; /* restore TRUE bit */
485}
486
487#if !FNM_CASEFOLD
488static char *strcpy_upcase(char *dst, const char *src)
489{
490 char *d = dst;
491 while (1) {
492 unsigned char ch = *src++;
493 if (ch >= 'a' && ch <= 'z')
494 ch -= ('a' - 'A');
495 *d++ = ch;
496 if (ch == '\0')
497 break;
498 }
499 return dst;
500}
501#endif
502
503ACTF(name)
504{
505 const char *tmp = bb_basename(fileName);
506 if (tmp != fileName && *tmp == '\0') {
507 /* "foo/bar/". Oh no... go back to 'b' */
508 tmp--;
509 while (tmp != fileName && *--tmp != '/')
510 continue;
511 if (*tmp == '/')
512 tmp++;
513 }
514 /* Was using FNM_PERIOD flag too,
515 * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it.
516 * find -name '*foo' should match .foo too:
517 */
518#if FNM_CASEFOLD
519 return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0;
520#else
521 if (ap->iname)
522 tmp = strcpy_upcase(alloca(strlen(tmp) + 1), tmp);
523 return fnmatch(ap->pattern, tmp, 0) == 0;
524#endif
525}
526
527#if ENABLE_FEATURE_FIND_PATH
528ACTF(path)
529{
530# if FNM_CASEFOLD
531 return fnmatch(ap->pattern, fileName, (ap->ipath ? FNM_CASEFOLD : 0)) == 0;
532# else
533 if (ap->ipath)
534 fileName = strcpy_upcase(alloca(strlen(fileName) + 1), fileName);
535 return fnmatch(ap->pattern, fileName, 0) == 0;
536# endif
537}
538#endif
539#if ENABLE_FEATURE_FIND_REGEX
540ACTF(regex)
541{
542 regmatch_t match;
543 if (regexec(&ap->compiled_pattern, fileName, 1, &match, 0 /*eflags*/))
544 return 0; /* no match */
545 if (match.rm_so)
546 return 0; /* match doesn't start at pos 0 */
547 if (fileName[match.rm_eo])
548 return 0; /* match doesn't end exactly at end of pathname */
549 return 1;
550}
551#endif
552#if ENABLE_FEATURE_FIND_TYPE
553ACTF(type)
554{
555 return ((statbuf->st_mode & S_IFMT) == ap->type_mask);
556}
557#endif
558#if ENABLE_FEATURE_FIND_PERM
559ACTF(perm)
560{
561 /* -perm [+/]mode: at least one of perm_mask bits are set */
562 if (ap->perm_char == '+' || ap->perm_char == '/')
563 return (statbuf->st_mode & ap->perm_mask) != 0;
564 /* -perm -mode: all of perm_mask are set */
565 if (ap->perm_char == '-')
566 return (statbuf->st_mode & ap->perm_mask) == ap->perm_mask;
567 /* -perm mode: file mode must match perm_mask */
568 return (statbuf->st_mode & 07777) == ap->perm_mask;
569}
570#endif
571#if ENABLE_FEATURE_FIND_MTIME
572ACTF(mtime)
573{
574 time_t file_age = time(NULL) - statbuf->st_mtime;
575 time_t mtime_secs = ap->mtime_days * 24*60*60;
576 if (ap->mtime_char == '+')
577 return file_age >= mtime_secs + 24*60*60;
578 if (ap->mtime_char == '-')
579 return file_age < mtime_secs;
580 /* just numeric mtime */
581 return file_age >= mtime_secs && file_age < (mtime_secs + 24*60*60);
582}
583#endif
584#if ENABLE_FEATURE_FIND_MMIN
585ACTF(mmin)
586{
587 time_t file_age = time(NULL) - statbuf->st_mtime;
588 time_t mmin_secs = ap->mmin_mins * 60;
589 if (ap->mmin_char == '+')
590 return file_age >= mmin_secs + 60;
591 if (ap->mmin_char == '-')
592 return file_age < mmin_secs;
593 /* just numeric mmin */
594 return file_age >= mmin_secs && file_age < (mmin_secs + 60);
595}
596#endif
597#if ENABLE_FEATURE_FIND_NEWER
598ACTF(newer)
599{
600 return (ap->newer_mtime < statbuf->st_mtime);
601}
602#endif
603#if ENABLE_FEATURE_FIND_INUM
604ACTF(inum)
605{
606 return (statbuf->st_ino == ap->inode_num);
607}
608#endif
609#if ENABLE_FEATURE_FIND_EXEC
610static int do_exec(action_exec *ap, const char *fileName)
611{
612 int i, rc;
613# if ENABLE_FEATURE_FIND_EXEC_PLUS
614 int size = ap->exec_argc + ap->filelist_idx + 1;
615# else
616 int size = ap->exec_argc + 1;
617# endif
618# if ENABLE_USE_PORTABLE_CODE
619 char **argv = alloca(sizeof(char*) * size);
620# else /* gcc 4.3.1 generates smaller code: */
621 char *argv[size];
622# endif
623 char **pp = argv;
624
625 for (i = 0; i < ap->exec_argc; i++) {
626 const char *arg = ap->exec_argv[i];
627
628# if ENABLE_FEATURE_FIND_EXEC_PLUS
629 if (ap->filelist) {
630 /* Handling "-exec +"
631 * Only one exec_argv[i] has substitution in it.
632 * Expand that one exec_argv[i] into file list.
633 */
634 if (ap->subst_count[i] == 0) {
635 *pp++ = xstrdup(arg);
636 } else {
637 int j = 0;
638 while (ap->filelist[j]) {
639 /* 2nd arg here should be ap->subst_count[i], but it is always 1: */
640 *pp++ = xmalloc_substitute_string(arg, 1, "{}", ap->filelist[j]);
641 free(ap->filelist[j]);
642 j++;
643 }
644 }
645 } else
646# endif
647 {
648 /* Handling "-exec ;" */
649 *pp++ = xmalloc_substitute_string(arg, ap->subst_count[i], "{}", fileName);
650 }
651 }
652 *pp = NULL; /* terminate the list */
653
654# if ENABLE_FEATURE_FIND_EXEC_PLUS
655 if (ap->filelist) {
656 ap->filelist[0] = NULL;
657 ap->filelist_idx = 0;
658 ap->file_len = 0;
659 }
660# endif
661
662 rc = spawn_and_wait(argv);
663 if (rc < 0)
664 bb_simple_perror_msg(argv[0]);
665
666 i = 0;
667 while (argv[i])
668 free(argv[i++]);
669 return rc == 0; /* return 1 if exitcode 0 */
670}
671ACTF(exec)
672{
673# if ENABLE_FEATURE_FIND_EXEC_PLUS
674 if (ap->filelist) {
675 int rc;
676
677 ap->filelist = xrealloc_vector(ap->filelist, 8, ap->filelist_idx);
678 ap->filelist[ap->filelist_idx++] = xstrdup(fileName);
679 ap->file_len += strlen(fileName) + sizeof(char*) + 1;
680 /* If we have lots of files already, exec the command */
681 rc = 1;
682 if (ap->file_len >= G.max_argv_len)
683 rc = do_exec(ap, NULL);
684 return rc;
685 }
686# endif
687 return do_exec(ap, fileName);
688}
689# if ENABLE_FEATURE_FIND_EXEC_PLUS
690static int flush_exec_plus(void)
691{
692 action *ap;
693 action **app;
694 action ***appp = G.actions;
695 while ((app = *appp++) != NULL) {
696 while ((ap = *app++) != NULL) {
697 if (ap->f == (action_fp)func_exec) {
698 action_exec *ae = (void*)ap;
699 if (ae->filelist_idx != 0) {
700 int rc = do_exec(ae, NULL);
701# if ENABLE_FEATURE_FIND_NOT
702 if (ap->invert) rc = !rc;
703# endif
704 if (rc == 0)
705 return 1;
706 }
707 }
708 }
709 }
710 return 0;
711}
712# endif
713#endif
714#if ENABLE_FEATURE_FIND_USER
715ACTF(user)
716{
717 return (statbuf->st_uid == ap->uid);
718}
719#endif
720#if ENABLE_FEATURE_FIND_GROUP
721ACTF(group)
722{
723 return (statbuf->st_gid == ap->gid);
724}
725#endif
726#if ENABLE_FEATURE_FIND_PRINT0
727ACTF(print0)
728{
729 printf("%s%c", fileName, '\0');
730 return TRUE;
731}
732#endif
733ACTF(print)
734{
735 puts(fileName);
736 return TRUE;
737}
738#if ENABLE_FEATURE_FIND_PAREN
739ACTF(paren)
740{
741 return exec_actions(ap->subexpr, fileName, statbuf);
742}
743#endif
744#if ENABLE_FEATURE_FIND_SIZE
745ACTF(size)
746{
747 if (ap->size_char == '+')
748 return statbuf->st_size > ap->size;
749 if (ap->size_char == '-')
750 return statbuf->st_size < ap->size;
751 return statbuf->st_size == ap->size;
752}
753#endif
754#if ENABLE_FEATURE_FIND_PRUNE
755/*
756 * -prune: if -depth is not given, return true and do not descend
757 * current dir; if -depth is given, return false with no effect.
758 * Example:
759 * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print
760 */
761ACTF(prune)
762{
763 return SKIP + TRUE;
764}
765#endif
766#if ENABLE_FEATURE_FIND_DELETE
767ACTF(delete)
768{
769 int rc;
770 if (S_ISDIR(statbuf->st_mode)) {
771 /* "find . -delete" skips rmdir(".") */
772 rc = 0;
773 if (NOT_LONE_CHAR(fileName, '.'))
774 rc = rmdir(fileName);
775 } else {
776 rc = unlink(fileName);
777 }
778 if (rc < 0)
779 bb_simple_perror_msg(fileName);
780 return TRUE;
781}
782#endif
783#if ENABLE_FEATURE_FIND_CONTEXT
784ACTF(context)
785{
786 security_context_t con;
787 int rc;
788
789 if (G.recurse_flags & ACTION_FOLLOWLINKS) {
790 rc = getfilecon(fileName, &con);
791 } else {
792 rc = lgetfilecon(fileName, &con);
793 }
794 if (rc < 0)
795 return FALSE;
796 rc = strcmp(ap->context, con);
797 freecon(con);
798 return rc == 0;
799}
800#endif
801#if ENABLE_FEATURE_FIND_LINKS
802ACTF(links)
803{
804 switch(ap->links_char) {
805 case '-' : return (statbuf->st_nlink < ap->links_count);
806 case '+' : return (statbuf->st_nlink > ap->links_count);
807 default: return (statbuf->st_nlink == ap->links_count);
808 }
809}
810#endif
811
812static int FAST_FUNC fileAction(const char *fileName,
813 struct stat *statbuf,
814 void *userData UNUSED_PARAM,
815 int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM))
816{
817 int r;
818 int same_fs = 1;
819
820#if ENABLE_FEATURE_FIND_XDEV
821 if (S_ISDIR(statbuf->st_mode) && G.xdev_count) {
822 int i;
823 for (i = 0; i < G.xdev_count; i++) {
824 if (G.xdev_dev[i] == statbuf->st_dev)
825 goto found;
826 }
827 //bb_error_msg("'%s': not same fs", fileName);
828 same_fs = 0;
829 found: ;
830 }
831#endif
832
833#if ENABLE_FEATURE_FIND_MAXDEPTH
834 if (depth < G.minmaxdepth[0]) {
835 if (same_fs)
836 return TRUE; /* skip this, continue recursing */
837 return SKIP; /* stop recursing */
838 }
839 if (depth > G.minmaxdepth[1])
840 return SKIP; /* stop recursing */
841#endif
842
843 r = exec_actions(G.actions, fileName, statbuf);
844 /* Had no explicit -print[0] or -exec? then print */
845 if ((r & TRUE) && G.need_print)
846 puts(fileName);
847
848#if ENABLE_FEATURE_FIND_MAXDEPTH
849 if (S_ISDIR(statbuf->st_mode)) {
850 if (depth == G.minmaxdepth[1])
851 return SKIP;
852 }
853#endif
854 /* -xdev stops on mountpoints, but AFTER mountpoit itself
855 * is processed as usual */
856 if (!same_fs) {
857 return SKIP;
858 }
859
860 /* Cannot return 0: our caller, recursive_action(),
861 * will perror() and skip dirs (if called on dir) */
862 return (r & SKIP) ? SKIP : TRUE;
863}
864
865
866#if ENABLE_FEATURE_FIND_TYPE
867static int find_type(const char *type)
868{
869 int mask = 0;
870
871 if (*type == 'b')
872 mask = S_IFBLK;
873 else if (*type == 'c')
874 mask = S_IFCHR;
875 else if (*type == 'd')
876 mask = S_IFDIR;
877 else if (*type == 'p')
878 mask = S_IFIFO;
879 else if (*type == 'f')
880 mask = S_IFREG;
881 else if (*type == 'l')
882 mask = S_IFLNK;
883 else if (*type == 's')
884 mask = S_IFSOCK;
885
886 if (mask == 0 || type[1] != '\0')
887 bb_error_msg_and_die(bb_msg_invalid_arg_to, type, "-type");
888
889 return mask;
890}
891#endif
892
893#if ENABLE_FEATURE_FIND_PERM \
894 || ENABLE_FEATURE_FIND_MTIME || ENABLE_FEATURE_FIND_MMIN \
895 || ENABLE_FEATURE_FIND_SIZE || ENABLE_FEATURE_FIND_LINKS
896static const char* plus_minus_num(const char* str)
897{
898 if (*str == '-' || *str == '+')
899 str++;
900 return str;
901}
902#endif
903
904/* Say no to GCCism */
905#define USE_NESTED_FUNCTION 0
906
907#if !USE_NESTED_FUNCTION
908struct pp_locals {
909 action*** appp;
910 unsigned cur_group;
911 unsigned cur_action;
912 IF_FEATURE_FIND_NOT( bool invert_flag; )
913};
914static action* alloc_action(struct pp_locals *ppl, int sizeof_struct, action_fp f)
915{
916 action *ap = xzalloc(sizeof_struct);
917 action **app;
918 action ***group = &ppl->appp[ppl->cur_group];
919 *group = app = xrealloc(*group, (ppl->cur_action+2) * sizeof(ppl->appp[0][0]));
920 app[ppl->cur_action++] = ap;
921 app[ppl->cur_action] = NULL;
922 ap->f = f;
923 IF_FEATURE_FIND_NOT( ap->invert = ppl->invert_flag; )
924 IF_FEATURE_FIND_NOT( ppl->invert_flag = 0; )
925 return ap;
926}
927#endif
928
929static action*** parse_params(char **argv)
930{
931 enum {
932 OPT_FOLLOW ,
933 IF_FEATURE_FIND_XDEV( OPT_XDEV ,)
934 IF_FEATURE_FIND_DEPTH( OPT_DEPTH ,)
935 PARM_a ,
936 PARM_o ,
937 IF_FEATURE_FIND_NOT( PARM_char_not ,)
938#if ENABLE_DESKTOP
939 PARM_and ,
940 PARM_or ,
941 IF_FEATURE_FIND_NOT( PARM_not ,)
942#endif
943 PARM_print ,
944 IF_FEATURE_FIND_PRINT0( PARM_print0 ,)
945 IF_FEATURE_FIND_PRUNE( PARM_prune ,)
946 IF_FEATURE_FIND_DELETE( PARM_delete ,)
947 IF_FEATURE_FIND_EXEC( PARM_exec ,)
948 IF_FEATURE_FIND_PAREN( PARM_char_brace,)
949 /* All options/actions starting from here require argument */
950 PARM_name ,
951 PARM_iname ,
952 IF_FEATURE_FIND_PATH( PARM_path ,)
953#if ENABLE_DESKTOP
954 /* -wholename is a synonym for -path */
955 /* We support it because Linux kernel's "make tags" uses it */
956 IF_FEATURE_FIND_PATH( PARM_wholename ,)
957#endif
958 IF_FEATURE_FIND_PATH( PARM_ipath ,)
959 IF_FEATURE_FIND_REGEX( PARM_regex ,)
960 IF_FEATURE_FIND_TYPE( PARM_type ,)
961 IF_FEATURE_FIND_PERM( PARM_perm ,)
962 IF_FEATURE_FIND_MTIME( PARM_mtime ,)
963 IF_FEATURE_FIND_MMIN( PARM_mmin ,)
964 IF_FEATURE_FIND_NEWER( PARM_newer ,)
965 IF_FEATURE_FIND_INUM( PARM_inum ,)
966 IF_FEATURE_FIND_USER( PARM_user ,)
967 IF_FEATURE_FIND_GROUP( PARM_group ,)
968 IF_FEATURE_FIND_SIZE( PARM_size ,)
969 IF_FEATURE_FIND_CONTEXT(PARM_context ,)
970 IF_FEATURE_FIND_LINKS( PARM_links ,)
971 IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,OPT_MAXDEPTH,)
972 };
973
974 static const char params[] ALIGN1 =
975 "-follow\0"
976 IF_FEATURE_FIND_XDEV( "-xdev\0" )
977 IF_FEATURE_FIND_DEPTH( "-depth\0" )
978 "-a\0"
979 "-o\0"
980 IF_FEATURE_FIND_NOT( "!\0" )
981#if ENABLE_DESKTOP
982 "-and\0"
983 "-or\0"
984 IF_FEATURE_FIND_NOT( "-not\0" )
985#endif
986 "-print\0"
987 IF_FEATURE_FIND_PRINT0( "-print0\0" )
988 IF_FEATURE_FIND_PRUNE( "-prune\0" )
989 IF_FEATURE_FIND_DELETE( "-delete\0" )
990 IF_FEATURE_FIND_EXEC( "-exec\0" )
991 IF_FEATURE_FIND_PAREN( "(\0" )
992 /* All options/actions starting from here require argument */
993 "-name\0"
994 "-iname\0"
995 IF_FEATURE_FIND_PATH( "-path\0" )
996#if ENABLE_DESKTOP
997 IF_FEATURE_FIND_PATH( "-wholename\0")
998#endif
999 IF_FEATURE_FIND_PATH( "-ipath\0" )
1000 IF_FEATURE_FIND_REGEX( "-regex\0" )
1001 IF_FEATURE_FIND_TYPE( "-type\0" )
1002 IF_FEATURE_FIND_PERM( "-perm\0" )
1003 IF_FEATURE_FIND_MTIME( "-mtime\0" )
1004 IF_FEATURE_FIND_MMIN( "-mmin\0" )
1005 IF_FEATURE_FIND_NEWER( "-newer\0" )
1006 IF_FEATURE_FIND_INUM( "-inum\0" )
1007 IF_FEATURE_FIND_USER( "-user\0" )
1008 IF_FEATURE_FIND_GROUP( "-group\0" )
1009 IF_FEATURE_FIND_SIZE( "-size\0" )
1010 IF_FEATURE_FIND_CONTEXT("-context\0")
1011 IF_FEATURE_FIND_LINKS( "-links\0" )
1012 IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
1013 ;
1014
1015#if !USE_NESTED_FUNCTION
1016 struct pp_locals ppl;
1017#define appp (ppl.appp )
1018#define cur_group (ppl.cur_group )
1019#define cur_action (ppl.cur_action )
1020#define invert_flag (ppl.invert_flag)
1021#define ALLOC_ACTION(name) (action_##name*)alloc_action(&ppl, sizeof(action_##name), (action_fp) func_##name)
1022#else
1023 action*** appp;
1024 unsigned cur_group;
1025 unsigned cur_action;
1026 IF_FEATURE_FIND_NOT( bool invert_flag; )
1027
1028 /* This is the only place in busybox where we use nested function.
1029 * So far more standard alternatives were bigger. */
1030 /* Auto decl suppresses "func without a prototype" warning: */
1031 auto action* alloc_action(int sizeof_struct, action_fp f);
1032 action* alloc_action(int sizeof_struct, action_fp f)
1033 {
1034 action *ap;
1035 appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(appp[0][0]));
1036 appp[cur_group][cur_action++] = ap = xzalloc(sizeof_struct);
1037 appp[cur_group][cur_action] = NULL;
1038 ap->f = f;
1039 IF_FEATURE_FIND_NOT( ap->invert = invert_flag; )
1040 IF_FEATURE_FIND_NOT( invert_flag = 0; )
1041 return ap;
1042 }
1043#define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name)
1044#endif
1045
1046 cur_group = 0;
1047 cur_action = 0;
1048 IF_FEATURE_FIND_NOT( invert_flag = 0; )
1049 appp = xzalloc(2 * sizeof(appp[0])); /* appp[0],[1] == NULL */
1050
1051 while (*argv) {
1052 const char *arg = argv[0];
1053 int parm = index_in_strings(params, arg);
1054 const char *arg1 = argv[1];
1055
1056 dbg("arg:'%s' arg1:'%s' parm:%d PARM_type:%d", arg, arg1, parm, PARM_type);
1057
1058 if (parm >= PARM_name) {
1059 /* All options/actions starting from -name require argument */
1060 if (!arg1)
1061 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1062 argv++;
1063 }
1064
1065 /* We can use big switch() here, but on i386
1066 * it doesn't give smaller code. Other arches? */
1067
1068/* Options always return true. They always take effect
1069 * rather than being processed only when their place in the
1070 * expression is reached.
1071 */
1072 /* Options */
1073 if (parm == OPT_FOLLOW) {
1074 dbg("follow enabled: %d", __LINE__);
1075 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
1076 }
1077#if ENABLE_FEATURE_FIND_XDEV
1078 else if (parm == OPT_XDEV) {
1079 dbg("%d", __LINE__);
1080 G.xdev_on = 1;
1081 }
1082#endif
1083#if ENABLE_FEATURE_FIND_MAXDEPTH
1084 else if (parm == OPT_MINDEPTH || parm == OPT_MINDEPTH + 1) {
1085 dbg("%d", __LINE__);
1086 G.minmaxdepth[parm - OPT_MINDEPTH] = xatoi_positive(arg1);
1087 }
1088#endif
1089#if ENABLE_FEATURE_FIND_DEPTH
1090 else if (parm == OPT_DEPTH) {
1091 dbg("%d", __LINE__);
1092 G.recurse_flags |= ACTION_DEPTHFIRST;
1093 }
1094#endif
1095/* Actions are grouped by operators
1096 * ( expr ) Force precedence
1097 * ! expr True if expr is false
1098 * -not expr Same as ! expr
1099 * expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false
1100 * expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true
1101 * expr1 , expr2 List; both expr1 and expr2 are always evaluated
1102 * We implement: (), -a, -o
1103 */
1104 /* Operators */
1105 else if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
1106 dbg("%d", __LINE__);
1107 /* no further special handling required */
1108 }
1109 else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
1110 dbg("%d", __LINE__);
1111 /* start new OR group */
1112 cur_group++;
1113 appp = xrealloc(appp, (cur_group+2) * sizeof(appp[0]));
1114 /*appp[cur_group] = NULL; - already NULL */
1115 appp[cur_group+1] = NULL;
1116 cur_action = 0;
1117 }
1118#if ENABLE_FEATURE_FIND_NOT
1119 else if (parm == PARM_char_not IF_DESKTOP(|| parm == PARM_not)) {
1120 /* also handles "find ! ! -name 'foo*'" */
1121 invert_flag ^= 1;
1122 dbg("invert_flag:%d", invert_flag);
1123 }
1124#endif
1125 /* Actions */
1126 else if (parm == PARM_print) {
1127 dbg("%d", __LINE__);
1128 G.need_print = 0;
1129 (void) ALLOC_ACTION(print);
1130 }
1131#if ENABLE_FEATURE_FIND_PRINT0
1132 else if (parm == PARM_print0) {
1133 dbg("%d", __LINE__);
1134 G.need_print = 0;
1135 (void) ALLOC_ACTION(print0);
1136 }
1137#endif
1138#if ENABLE_FEATURE_FIND_PRUNE
1139 else if (parm == PARM_prune) {
1140 dbg("%d", __LINE__);
1141 (void) ALLOC_ACTION(prune);
1142 }
1143#endif
1144#if ENABLE_FEATURE_FIND_DELETE
1145 else if (parm == PARM_delete) {
1146 dbg("%d", __LINE__);
1147 G.need_print = 0;
1148 G.recurse_flags |= ACTION_DEPTHFIRST;
1149 (void) ALLOC_ACTION(delete);
1150 }
1151#endif
1152#if ENABLE_FEATURE_FIND_EXEC
1153 else if (parm == PARM_exec) {
1154 int i;
1155 action_exec *ap;
1156 IF_FEATURE_FIND_EXEC_PLUS(int all_subst = 0;)
1157 dbg("%d", __LINE__);
1158 G.need_print = 0;
1159 ap = ALLOC_ACTION(exec);
1160 ap->exec_argv = ++argv; /* first arg after -exec */
1161 /*ap->exec_argc = 0; - ALLOC_ACTION did it */
1162 while (1) {
1163 if (!*argv) /* did not see ';' or '+' until end */
1164 bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
1165 // find -exec echo Foo ">{}<" ";"
1166 // executes "echo Foo >FILENAME<",
1167 // find -exec echo Foo ">{}<" "+"
1168 // executes "echo Foo FILENAME1 FILENAME2 FILENAME3...".
1169 if ((argv[0][0] == ';' || argv[0][0] == '+')
1170 && argv[0][1] == '\0'
1171 ) {
1172# if ENABLE_FEATURE_FIND_EXEC_PLUS
1173 if (argv[0][0] == '+')
1174 ap->filelist = xzalloc(sizeof(ap->filelist[0]));
1175# endif
1176 break;
1177 }
1178 argv++;
1179 ap->exec_argc++;
1180 }
1181 if (ap->exec_argc == 0)
1182 bb_error_msg_and_die(bb_msg_requires_arg, arg);
1183 ap->subst_count = xmalloc(ap->exec_argc * sizeof(int));
1184 i = ap->exec_argc;
1185 while (i--) {
1186 ap->subst_count[i] = count_strstr(ap->exec_argv[i], "{}");
1187 IF_FEATURE_FIND_EXEC_PLUS(all_subst += ap->subst_count[i];)
1188 }
1189# if ENABLE_FEATURE_FIND_EXEC_PLUS
1190 /*
1191 * coreutils expects {} to appear only once in "-exec +"
1192 */
1193 if (all_subst != 1 && ap->filelist)
1194 bb_error_msg_and_die("only one '{}' allowed for -exec +");
1195# endif
1196 }
1197#endif
1198#if ENABLE_FEATURE_FIND_PAREN
1199 else if (parm == PARM_char_brace) {
1200 action_paren *ap;
1201 char **endarg;
1202 unsigned nested = 1;
1203
1204 dbg("%d", __LINE__);
1205 endarg = argv;
1206 while (1) {
1207 if (!*++endarg)
1208 bb_error_msg_and_die("unpaired '('");
1209 if (LONE_CHAR(*endarg, '('))
1210 nested++;
1211 else if (LONE_CHAR(*endarg, ')') && !--nested) {
1212 *endarg = NULL;
1213 break;
1214 }
1215 }
1216 ap = ALLOC_ACTION(paren);
1217 ap->subexpr = parse_params(argv + 1);
1218 *endarg = (char*) ")"; /* restore NULLed parameter */
1219 argv = endarg;
1220 }
1221#endif
1222 else if (parm == PARM_name || parm == PARM_iname) {
1223 action_name *ap;
1224 dbg("%d", __LINE__);
1225 ap = ALLOC_ACTION(name);
1226 ap->pattern = arg1;
1227 ap->iname = (parm == PARM_iname);
1228 }
1229#if ENABLE_FEATURE_FIND_PATH
1230 else if (parm == PARM_path IF_DESKTOP(|| parm == PARM_wholename) || parm == PARM_ipath) {
1231 action_path *ap;
1232 dbg("%d", __LINE__);
1233 ap = ALLOC_ACTION(path);
1234 ap->pattern = arg1;
1235 ap->ipath = (parm == PARM_ipath);
1236 }
1237#endif
1238#if ENABLE_FEATURE_FIND_REGEX
1239 else if (parm == PARM_regex) {
1240 action_regex *ap;
1241 dbg("%d", __LINE__);
1242 ap = ALLOC_ACTION(regex);
1243 xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/);
1244 }
1245#endif
1246#if ENABLE_FEATURE_FIND_TYPE
1247 else if (parm == PARM_type) {
1248 action_type *ap;
1249 ap = ALLOC_ACTION(type);
1250 ap->type_mask = find_type(arg1);
1251 dbg("created:type mask:%x", ap->type_mask);
1252 }
1253#endif
1254#if ENABLE_FEATURE_FIND_PERM
1255/* -perm BITS File's mode bits are exactly BITS (octal or symbolic).
1256 * Symbolic modes use mode 0 as a point of departure.
1257 * -perm -BITS All of the BITS are set in file's mode.
1258 * -perm [+/]BITS At least one of the BITS is set in file's mode.
1259 */
1260 else if (parm == PARM_perm) {
1261 action_perm *ap;
1262 dbg("%d", __LINE__);
1263 ap = ALLOC_ACTION(perm);
1264 ap->perm_char = arg1[0];
1265 arg1 = (arg1[0] == '/' ? arg1+1 : plus_minus_num(arg1));
1266 /*ap->perm_mask = 0; - ALLOC_ACTION did it */
1267 ap->perm_mask = bb_parse_mode(arg1, ap->perm_mask);
1268 if (ap->perm_mask == (mode_t)-1)
1269 bb_error_msg_and_die("invalid mode '%s'", arg1);
1270 }
1271#endif
1272#if ENABLE_FEATURE_FIND_MTIME
1273 else if (parm == PARM_mtime) {
1274 action_mtime *ap;
1275 dbg("%d", __LINE__);
1276 ap = ALLOC_ACTION(mtime);
1277 ap->mtime_char = arg1[0];
1278 ap->mtime_days = xatoul(plus_minus_num(arg1));
1279 }
1280#endif
1281#if ENABLE_FEATURE_FIND_MMIN
1282 else if (parm == PARM_mmin) {
1283 action_mmin *ap;
1284 dbg("%d", __LINE__);
1285 ap = ALLOC_ACTION(mmin);
1286 ap->mmin_char = arg1[0];
1287 ap->mmin_mins = xatoul(plus_minus_num(arg1));
1288 }
1289#endif
1290#if ENABLE_FEATURE_FIND_NEWER
1291 else if (parm == PARM_newer) {
1292 struct stat stat_newer;
1293 action_newer *ap;
1294 dbg("%d", __LINE__);
1295 ap = ALLOC_ACTION(newer);
1296 xstat(arg1, &stat_newer);
1297 ap->newer_mtime = stat_newer.st_mtime;
1298 }
1299#endif
1300#if ENABLE_FEATURE_FIND_INUM
1301 else if (parm == PARM_inum) {
1302 action_inum *ap;
1303 dbg("%d", __LINE__);
1304 ap = ALLOC_ACTION(inum);
1305 ap->inode_num = xatoul(arg1);
1306 }
1307#endif
1308#if ENABLE_FEATURE_FIND_USER
1309 else if (parm == PARM_user) {
1310 action_user *ap;
1311 dbg("%d", __LINE__);
1312 ap = ALLOC_ACTION(user);
1313 ap->uid = bb_strtou(arg1, NULL, 10);
1314 if (errno)
1315 ap->uid = xuname2uid(arg1);
1316 }
1317#endif
1318#if ENABLE_FEATURE_FIND_GROUP
1319 else if (parm == PARM_group) {
1320 action_group *ap;
1321 dbg("%d", __LINE__);
1322 ap = ALLOC_ACTION(group);
1323 ap->gid = bb_strtou(arg1, NULL, 10);
1324 if (errno)
1325 ap->gid = xgroup2gid(arg1);
1326 }
1327#endif
1328#if ENABLE_FEATURE_FIND_SIZE
1329 else if (parm == PARM_size) {
1330/* -size n[bckw]: file uses n units of space
1331 * b (default): units are 512-byte blocks
1332 * c: 1 byte
1333 * k: kilobytes
1334 * w: 2-byte words
1335 */
1336#if ENABLE_LFS
1337#define XATOU_SFX xatoull_sfx
1338#else
1339#define XATOU_SFX xatoul_sfx
1340#endif
1341 static const struct suffix_mult find_suffixes[] = {
1342 { "c", 1 },
1343 { "w", 2 },
1344 { "", 512 },
1345 { "b", 512 },
1346 { "k", 1024 },
1347 { "", 0 }
1348 };
1349 action_size *ap;
1350 dbg("%d", __LINE__);
1351 ap = ALLOC_ACTION(size);
1352 ap->size_char = arg1[0];
1353 ap->size = XATOU_SFX(plus_minus_num(arg1), find_suffixes);
1354 }
1355#endif
1356#if ENABLE_FEATURE_FIND_CONTEXT
1357 else if (parm == PARM_context) {
1358 action_context *ap;
1359 dbg("%d", __LINE__);
1360 ap = ALLOC_ACTION(context);
1361 /*ap->context = NULL; - ALLOC_ACTION did it */
1362 /* SELinux headers erroneously declare non-const parameter */
1363 if (selinux_raw_to_trans_context((char*)arg1, &ap->context))
1364 bb_simple_perror_msg(arg1);
1365 }
1366#endif
1367#if ENABLE_FEATURE_FIND_LINKS
1368 else if (parm == PARM_links) {
1369 action_links *ap;
1370 dbg("%d", __LINE__);
1371 ap = ALLOC_ACTION(links);
1372 ap->links_char = arg1[0];
1373 ap->links_count = xatoul(plus_minus_num(arg1));
1374 }
1375#endif
1376 else {
1377 bb_error_msg("unrecognized: %s", arg);
1378 bb_show_usage();
1379 }
1380 argv++;
1381 }
1382 dbg("exiting %s", __func__);
1383 return appp;
1384#undef ALLOC_ACTION
1385#undef appp
1386#undef cur_action
1387#undef invert_flag
1388}
1389
1390int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1391int find_main(int argc UNUSED_PARAM, char **argv)
1392{
1393 int i, firstopt, status = EXIT_SUCCESS;
1394 char **past_HLP, *saved;
1395
1396 INIT_G();
1397
1398 /* "find -type f" + getopt("+HLP") => disaster.
1399 * Need to avoid getopt running into a non-HLP option.
1400 * Do this by temporarily storing NULL there:
1401 */
1402 past_HLP = argv;
1403 for (;;) {
1404 saved = *++past_HLP;
1405 if (!saved)
1406 break;
1407 if (saved[0] != '-')
1408 break;
1409 if (!saved[1])
1410 break; /* it is "-" */
1411 if ((saved+1)[strspn(saved+1, "HLP")] != '\0')
1412 break;
1413 }
1414 *past_HLP = NULL;
1415 /* "+": stop on first non-option */
1416 i = getopt32(argv, "+HLP");
1417 if (i & (1<<0))
1418 G.recurse_flags |= ACTION_FOLLOWLINKS_L0 | ACTION_DANGLING_OK;
1419 if (i & (1<<1))
1420 G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
1421 /* -P is default and is ignored */
1422 argv = past_HLP; /* same result as "argv += optind;" */
1423 *past_HLP = saved;
1424
1425 for (firstopt = 0; argv[firstopt]; firstopt++) {
1426 if (argv[firstopt][0] == '-')
1427 break;
1428 if (ENABLE_FEATURE_FIND_NOT && LONE_CHAR(argv[firstopt], '!'))
1429 break;
1430 if (ENABLE_FEATURE_FIND_PAREN && LONE_CHAR(argv[firstopt], '('))
1431 break;
1432 }
1433 if (firstopt == 0) {
1434 *--argv = (char*)".";
1435 firstopt++;
1436 }
1437
1438 G.actions = parse_params(&argv[firstopt]);
1439 argv[firstopt] = NULL;
1440
1441#if ENABLE_FEATURE_FIND_XDEV
1442 if (G.xdev_on) {
1443 struct stat stbuf;
1444
1445 G.xdev_count = firstopt;
1446 G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
1447 for (i = 0; argv[i]; i++) {
1448 /* not xstat(): shouldn't bomb out on
1449 * "find not_exist exist -xdev" */
1450 if (stat(argv[i], &stbuf) == 0)
1451 G.xdev_dev[i] = stbuf.st_dev;
1452 /* else G.xdev_dev[i] stays 0 and
1453 * won't match any real device dev_t
1454 */
1455 }
1456 }
1457#endif
1458
1459 for (i = 0; argv[i]; i++) {
1460 if (!recursive_action(argv[i],
1461 G.recurse_flags,/* flags */
1462 fileAction, /* file action */
1463 fileAction, /* dir action */
1464 NULL, /* user data */
1465 0) /* depth */
1466 ) {
1467 status |= EXIT_FAILURE;
1468 }
1469 }
1470
1471 IF_FEATURE_FIND_EXEC_PLUS(status |= flush_exec_plus();)
1472 return status;
1473}
Note: See TracBrowser for help on using the repository browser.