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

Last change on this file since 1770 was 1770, checked in by Bruno Cornec, 12 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
RevLine 
[821]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 *
[1770]10 * Licensed under the GPL version 2, see the file LICENSE in this tarball.
[821]11 */
12
[1770]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 */
[821]47
[1770]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 */
[821]55
[1770]56#include <fnmatch.h>
57#include "libbb.h"
58#if ENABLE_FEATURE_FIND_REGEX
59#include "xregex.h"
[821]60#endif
61
[1770]62/* This is a NOEXEC applet. Be very careful! */
[821]63
64
[1770]65USE_FEATURE_FIND_XDEV(static dev_t *xdev_dev;)
66USE_FEATURE_FIND_XDEV(static int xdev_count;)
[821]67
[1770]68typedef int (*action_fp)(const char *fileName, struct stat *statbuf, void *);
[821]69
[1770]70typedef struct {
71    action_fp f;
72#if ENABLE_FEATURE_FIND_NOT
73    bool invert;
[821]74#endif
[1770]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;))
[821]97
[1770]98static action ***actions;
99static bool need_print = 1;
100static int recurse_flags = ACTION_RECURSE;
[821]101
[1770]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}
[821]130#endif
131
[1770]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)
[821]138{
[1770]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;
[821]175        }
176    }
[1770]177    return rc ^ TRUE; /* restore TRUE bit */
178}
[821]179
[1770]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 == '/')
[821]189            tmp++;
190    }
[1770]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}
[821]198#endif
[1770]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}
[821]211#endif
[1770]212#if ENABLE_FEATURE_FIND_TYPE
213ACTF(type)
214{
215    return ((statbuf->st_mode & S_IFMT) == ap->type_mask);
216}
[821]217#endif
[1770]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}
[821]230#endif
[1770]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}
[821]243#endif
[1770]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);
[821]348    }
[1770]349    if (rc < 0)
350        bb_perror_msg("%s", fileName);
351    return TRUE;
352}
[821]353#endif
[1770]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);
[821]364    }
[1770]365    if (rc < 0)
366        return FALSE;
367    rc = strcmp(ap->context, con);
368    freecon(con);
369    return rc == 0;
370}
[821]371#endif
372
[1770]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;
[821]381#endif
[1770]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;
[821]400}
401
[1770]402
403#if ENABLE_FEATURE_FIND_TYPE
404static int find_type(const char *type)
[821]405{
406    int mask = 0;
407
[1770]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;
[821]422
[1770]423    if (mask == 0 || *(type + 1) != '\0')
[821]424        bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
425
426    return mask;
427}
428#endif
429
[1770]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)
[821]434{
[1770]435    if (*str == '-' || *str == '+')
436        str++;
437    return str;
438}
439#endif
[821]440
[1770]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;
[821]523    }
524
[1770]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        }
[821]575#endif
[1770]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        }
[821]590#endif
[1770]591#if ENABLE_FEATURE_FIND_DEPTH
592        else if (parm == PARM_depth) {
593            recurse_flags |= ACTION_DEPTHFIRST;
594        }
[821]595#endif
[1770]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        }
[821]601#endif
[1770]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        }
[821]608#endif
[1770]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++;
[821]625            }
[1770]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;
[821]639
[1770]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;
[821]649                }
650            }
[1770]651            ap = ALLOC_ACTION(paren);
652            ap->subexpr = parse_params(argv + 1);
653            *endarg = (char*) ")"; /* restore NULLed parameter */
654            argv = endarg;
655        }
[821]656#endif
[1770]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) {
[821]717            struct stat stat_newer;
[1770]718            action_newer *ap;
719            ap = ALLOC_ACTION(newer);
720            xstat(arg1, &stat_newer);
721            ap->newer_mtime = stat_newer.st_mtime;
722        }
[821]723#endif
[1770]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        }
[821]730#endif
[1770]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}
[821]795
796
[1770]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    };
[821]810
[1770]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                }
[821]861            }
[1770]862            argp[0] = (char*)"-a";
863        }
[821]864#endif
[1770]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++;
[821]876    }
877
[1770]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 */
[821]893            status = EXIT_FAILURE;
894    }
895    return status;
896}
Note: See TracBrowser for help on using the repository browser.