source: MondoRescue/branches/stable/mindi-busybox/findutils/find.c@ 1770

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 16 years ago
  • Better output for mindi-busybox revision
  • Remove dummy file created on NFS - report from Arnaud Tiger <arnaud.tiger_at_hp.com>
  • strace useful for debug
  • fix new versions for pb (2.0.0 for mindi and 1.7.2 for mindi-busybox)
  • fix build process for mindi-busybox + options used in that version (dd for label-partitions-as-necessary)
  • fix typo in label-partitions-as-necessary which doesn't seem to work
  • Update to busybox 1.7.2
  • perl is now required at restore time to support uuid swap partitions (and will be used for many other thigs

in the future for sure)

  • next mindi version will be 2.0.0 due to all the changes made in it (udev may break working distros)
  • small optimization in mindi on keyboard handling (one single find instead of multiple)
  • better interaction for USB device when launching mindi manually
  • attempt to automatically guess block disk size for ramdisk
  • fix typos in bkphw
  • Fix the remaining problem with UUID support for swap partitions
  • Updates mondoarchive man page for USB support
  • Adds preliminary Hardware support to mindi (Proliant SSSTK)
  • Tries to add udev support also for rhel4
  • Fix UUID support which was still broken.
  • Be conservative in test for the start-nfs script
  • Update config file for mindi-busybox for 1.7.2 migration
  • Try to run around a busybox bug (1.2.2 pb on inexistant links)
  • Add build content for mindi-busybox in pb
  • Remove distributions content for mindi-busybox
  • Fix a warning on inexistant raidtab
  • Solve problem on tmpfs in restore init (Problem of inexistant symlink and busybox)
  • Create MONDO_CACHE and use it everywhere + creation at start
  • Really never try to eject a USB device
  • Fix a issue with &> usage (replaced with 1> and 2>)
  • Adds magic file to depllist in order to have file working + ldd which helps for debugging issues
  • tty modes correct to avoid sh error messages
  • Use ext3 normally and not ext2 instead
  • USB device should be corrected after reading (take 1st part)
  • Adds a mount_USB_here function derived from mount_CDROM_here
  • usb detection place before /dev detection in device name at restore time
  • Fix when restoring from USB: media is asked in interactive mode
  • Adds USB support for mondorestore
  • mount_cdrom => mount_media
  • elilo.efi is now searched throughout /boot/efi and not in a fixed place as there is no standard
  • untar-and-softlink => untar (+ interface change)
  • suppress useless softlinks creation/removal in boot process
  • avoids udevd messages on groups
  • Increase # of disks to 99 as in mindi at restore time (should be a conf file parameter)
  • skip existing big file creation
  • seems to work correctly for USB mindi boot
  • Adds group and tty link to udev conf
  • Always load usb-torage (even 2.6) to initiate USB bus discovery
  • Better printing of messages
  • Attempt to fix a bug in supporting OpenSusE 10.3 kernel for initramfs (mindi may now use multiple regex for kernel initrd detection)
  • Links were not correctly done as non relative for modules in mindi
  • exclusion of modules denied now works
  • Also create modules in their ordinary place, so that classical modprobe works + copy modules.dep
  • Fix bugs for DENY_MODS handling
  • Add device /dev/console for udev
  • ide-generic should now really be excluded
  • Fix a bug in major number for tty
  • If udev then adds modprobe/insmod to rootfs
  • tty0 is also cretaed with udev
  • ide-generic put rather in DENY_MODS
  • udevd remove from deplist s handled in mindi directly
  • better default for mindi when using --usb
  • Handles dynamically linked busybox (in case we want to use it soon ;-)
  • Adds fixed devices to create for udev
  • ide-generic should not be part of the initrd when using libata v2
  • support a dynamically linked udev (case on Ubuntu 7.10 and Mandriva 2008.0 so should be quite generic) This will give incitation to move to dyn. linked binaries in the initrd which will help for other tasks (ia6 4)
  • Improvement in udev support (do not use cl options not available in busybox)
  • Udev in mindi
    • auto creation of the right links at boot time with udev-links.conf(from Mandriva 2008.0)
    • rework startup of udev as current makes kernel crash (from Mandriva 2008.0)
    • add support for 64 bits udev
  • Try to render MyInsmod silent at boot time
  • Adds udev support (mandatory for newest distributions to avoid remapping of devices in a different way as on the original system)
  • We also need vaft format support for USB boot
  • Adds libusual support (Ubuntu 7.10 needs it for USB)
  • Improve Ubuntu/Debian keyboard detection and support
  • pbinit adapted to new pb (0.8.10). Filtering of docs done in it
  • Suppress some mondo warnings and errors on USB again
  • Tries to fix lack of files in deb mindi package
  • Verify should now work for USB devices
  • More log/mesages improvement for USB support
  • - Supress g_erase_tmpdir_and_scratchdir
  • Improve some log messages for USB support
  • Try to improve install in mindi to avoid issues with isolinux.cfg not installed vene if in the pkg :-(
  • Improve mindi-busybox build
  • In conformity with pb 0.8.9
  • Add support for Ubuntu 7.10 in build process
  • Add USB Key button to Menu UI (CD streamer removed)
  • Attempt to fix error messages on tmp/scratch files at the end by removing those dir at the latest possible.
  • Fix a bug linked to the size of the -E param which could be used (Arnaud Tiger/René Ribaud).
  • Integrate ~/.pbrc content into mondorescue.pb (required project-builder >= 0.8.7)
  • Put mondorescue in conformity with new pb filtering rules
  • Add USB support at restore time (no test done yet). New start-usb script PB varibale added where useful
  • Unmounting USB device before removal of temporary scratchdir
  • Stil refining USB copy back to mondo (one command was not executed)
  • No need to have the image subdor in the csratchdir when USB.
  • umount the USB partition before attempting to use it
  • Remove useless copy from mindi to mondo at end of USB handling

(risky merge, we are raising the limits of 2 diverging branches. The status of stable is not completely sure as such. Will need lots of tests, but it's not yet done :-()
(merge -r1692:1769 $SVN_M/branches/2.2.5)

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