Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/findutils


Ignore:
Timestamp:
Nov 6, 2007, 11:01:53 AM (16 years ago)
Author:
Bruno Cornec
Message:
  • 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)

Location:
branches/stable/mindi-busybox/findutils
Files:
2 deleted
4 edited
1 copied

Legend:

Unmodified
Added
Removed
  • branches/stable/mindi-busybox/findutils/Config.in

    r821 r1770  
    66menu "Finding Utilities"
    77
    8 config CONFIG_FIND
     8config FIND
    99    bool "find"
    1010    default n
     
    1212      find is used to search your system to find specified files.
    1313
    14 config CONFIG_FEATURE_FIND_PRINT0
     14config FEATURE_FIND_PRINT0
    1515    bool "Enable -print0 option"
    1616    default y
    17     depends on CONFIG_FIND
     17    depends on FIND
    1818    help
    1919      Causes output names to be separated by a null character
     
    2222      interpreted by other programs.
    2323
    24 config CONFIG_FEATURE_FIND_MTIME
     24config FEATURE_FIND_MTIME
    2525    bool "Enable modified time matching (-mtime) option"
    2626    default y
    27     depends on CONFIG_FIND
     27    depends on FIND
    2828    help
    2929      Allow searching based on the modification time of
    3030      files, in days.
    3131
    32 config CONFIG_FEATURE_FIND_MMIN
    33     bool "Enable modified time matching (-min) option"
    34     default y
    35     depends on CONFIG_FIND
     32config FEATURE_FIND_MMIN
     33    bool "Enable modified time matching (-mmin) option"
     34    default y
     35    depends on FIND
    3636    help
    3737      Allow searching based on the modification time of
    3838      files, in minutes.
    3939
    40 config CONFIG_FEATURE_FIND_PERM
     40config FEATURE_FIND_PERM
    4141    bool "Enable permissions matching (-perm) option"
    4242    default y
    43     depends on CONFIG_FIND
     43    depends on FIND
    4444    help
    4545      Enable searching based on file permissions.
    4646
    47 config CONFIG_FEATURE_FIND_TYPE
     47config FEATURE_FIND_TYPE
    4848    bool "Enable filetype matching (-type) option"
    4949    default y
    50     depends on CONFIG_FIND
     50    depends on FIND
    5151    help
    5252      Enable searching based on file type (file,
    5353      directory, socket, device, etc.).
    5454
    55 config CONFIG_FEATURE_FIND_XDEV
     55config FEATURE_FIND_XDEV
    5656    bool "Enable stay in filesystem (-xdev) option"
    5757    default y
    58     depends on CONFIG_FIND
    59     help
    60       This option will allow find to restrict searches to a single
    61       filesystem.
    62 
    63 config CONFIG_FEATURE_FIND_NEWER
     58    depends on FIND
     59    help
     60      This option allows find to restrict searches to a single filesystem.
     61
     62config FEATURE_FIND_MAXDEPTH
     63    bool "Enable -maxdepth N option"
     64    default y
     65    depends on FIND
     66    help
     67      This option enables -maxdepth N option.
     68
     69config FEATURE_FIND_NEWER
    6470    bool "Enable -newer option for comparing file mtimes"
    6571    default y
    66     depends on CONFIG_FIND
     72    depends on FIND
    6773    help
    6874      Support the 'find -newer' option for finding any files which have
    6975      a modified time that is more recent than the specified FILE.
    7076
    71 config CONFIG_FEATURE_FIND_INUM
     77config FEATURE_FIND_INUM
    7278    bool "Enable inode number matching (-inum) option"
    7379    default y
    74     depends on CONFIG_FIND
     80    depends on FIND
    7581    help
    7682      Support the 'find -inum' option for searching by inode number.
    7783
    78 config CONFIG_FEATURE_FIND_EXEC
     84config FEATURE_FIND_EXEC
    7985    bool "Enable (-exec) option allowing execution of commands"
    8086    default y
    81     depends on CONFIG_FIND
     87    depends on FIND
    8288    help
    8389      Support the 'find -exec' option for executing commands based upon
    8490      the files matched.
    8591
    86 config CONFIG_GREP
     92config FEATURE_FIND_USER
     93    bool "Enable username/uid matching (-user) option"
     94    default y
     95    depends on FIND
     96    help
     97      Support the 'find -user' option for searching by username or uid.
     98
     99config FEATURE_FIND_GROUP
     100    bool "Enable group/gid matching (-group) option"
     101    default y
     102    depends on FIND
     103    help
     104      Support the 'find -group' option for searching by group name or gid.
     105
     106config FEATURE_FIND_NOT
     107    bool "Enable the 'not' (!) operator"
     108    default y
     109    depends on FIND
     110    help
     111      Support the '!' operator to invert the test results.
     112      If 'Enable full-blown desktop' is enabled, then will also support
     113      the non-POSIX notation '-not'.
     114
     115config FEATURE_FIND_DEPTH
     116    bool "Enable the -depth option"
     117    default y
     118    depends on FIND
     119    help
     120      Process each directory's contents before the directory itself.
     121
     122config FEATURE_FIND_PAREN
     123    bool "Enable parens in options"
     124    default y
     125    depends on FIND
     126    help
     127      Enable usage of parens '(' to specify logical order of arguments.
     128
     129config FEATURE_FIND_SIZE
     130    bool "Enable (-size) option allowing matching for file size"
     131    default y
     132    depends on FIND
     133    help
     134      Support the 'find -size' option for searching by file size.
     135
     136config FEATURE_FIND_PRUNE
     137    bool "Enable (-prune) option allowing to exclude subdirectories"
     138    default y
     139    depends on FIND
     140    help
     141      If the file is a directory, dont descend into it. Useful for
     142      exclusion .svn and CVS directories.
     143
     144config FEATURE_FIND_DELETE
     145    bool "Enable -delete option allowing to delete files"
     146    default n
     147    depends on FIND && FEATURE_FIND_DEPTH
     148    help
     149      Support the 'find -delete' option for deleting files and direcotries.
     150      WARNING: This option can do much harm if used wrong. Busybox will not
     151      try to protect the user from doing stupid things. Use with care.
     152
     153config FEATURE_FIND_PATH
     154    bool "Enable -path option allowing to match pathname patterns"
     155    default y
     156    depends on FIND
     157    help
     158      The -path option matches whole pathname instead of just filename.
     159
     160config FEATURE_FIND_REGEX
     161    bool "Enable -regex: match pathname to regex"
     162    default y
     163    depends on FIND
     164    help
     165      The -regex option matches whole pathname against regular expression.
     166
     167config FEATURE_FIND_CONTEXT
     168    bool "Enable (-context) option for matching security context"
     169    default n
     170    depends on FIND && SELINUX
     171    help
     172      Support the 'find -context' option for matching security context.
     173
     174config GREP
    87175    bool "grep"
    88176    default n
     
    90178      grep is used to search files for a specified pattern.
    91179
    92 config CONFIG_FEATURE_GREP_EGREP_ALIAS
     180config FEATURE_GREP_EGREP_ALIAS
    93181    bool "Support extended regular expressions (egrep & grep -E)"
    94182    default y
    95     depends on CONFIG_GREP
     183    depends on GREP
    96184    help
    97185      Enabled support for extended regular expressions.  Extended
     
    99187      and various repetition operators.
    100188
    101 config CONFIG_FEATURE_GREP_FGREP_ALIAS
     189config FEATURE_GREP_FGREP_ALIAS
    102190    bool "Alias fgrep to grep -F"
    103191    default y
    104     depends on CONFIG_GREP
     192    depends on GREP
    105193    help
    106194      fgrep sees the search pattern as a normal string rather than
     
    108196      grep -F is always builtin, this just creates the fgrep alias.
    109197
    110 config CONFIG_FEATURE_GREP_CONTEXT
     198config FEATURE_GREP_CONTEXT
    111199    bool "Enable before and after context flags (-A, -B and -C)"
    112200    default y
    113     depends on CONFIG_GREP
     201    depends on GREP
    114202    help
    115203      Print the specified number of leading (-B) and/or trailing (-A)
     
    117205      Print the specified number of context lines (-C).
    118206
    119 config CONFIG_XARGS
     207config XARGS
    120208    bool "xargs"
    121209    default n
     
    124212      every item from standard input.
    125213
    126 config CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
     214config FEATURE_XARGS_SUPPORT_CONFIRMATION
    127215    bool "Enable prompt and confirmation option -p"
    128216    default n
    129     depends on CONFIG_XARGS
     217    depends on XARGS
    130218    help
    131219      Support prompt the user about whether to run each command
    132220      line and read a line from the terminal.
    133221
    134 config CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
     222config FEATURE_XARGS_SUPPORT_QUOTES
    135223    bool "Enable support single and double quotes and backslash"
    136224    default n
    137     depends on CONFIG_XARGS
     225    depends on XARGS
    138226    help
    139227      Default xargs unsupport single and double quotes
    140228      and backslash for can use aruments with spaces.
    141229
    142 config CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
     230config FEATURE_XARGS_SUPPORT_TERMOPT
    143231    bool "Enable support options -x"
    144232    default n
    145     depends on CONFIG_XARGS
     233    depends on XARGS
    146234    help
    147235      Enable support exit if the size (see the -s or -n option)
    148236      is exceeded.
    149237
    150 config CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
    151     bool "Enable options -0"
    152     default n
    153     depends on CONFIG_XARGS
     238config FEATURE_XARGS_SUPPORT_ZERO_TERM
     239    bool "Enable null terminated option -0"
     240    default n
     241    depends on XARGS
    154242    help
    155243      Enable input filenames are terminated by a null character
  • branches/stable/mindi-busybox/findutils/find.c

    r821 r1770  
    88 *  Matt Kraai <kraai@alumni.carnegiemellon.edu>.
    99 *
    10  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     10 * Licensed under the GPL version 2, see the file LICENSE in this tarball.
    1111 */
    1212
    13 #include "busybox.h"
    14 #include <stdio.h>
    15 #include <unistd.h>
    16 #include <dirent.h>
    17 #include <string.h>
    18 #include <stdlib.h>
     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
    1956#include <fnmatch.h>
    20 #include <time.h>
    21 #include <ctype.h>
    22 
    23 static char *pattern;
    24 #ifdef CONFIG_FEATURE_FIND_PRINT0
    25 static char printsep = '\n';
    26 #endif
    27 
    28 #ifdef CONFIG_FEATURE_FIND_TYPE
    29 static int type_mask = 0;
    30 #endif
    31 
    32 #ifdef CONFIG_FEATURE_FIND_PERM
    33 static char perm_char = 0;
    34 static int perm_mask = 0;
    35 #endif
    36 
    37 #ifdef CONFIG_FEATURE_FIND_MTIME
    38 static char mtime_char;
    39 static int mtime_days;
    40 #endif
    41 
    42 #ifdef CONFIG_FEATURE_FIND_MMIN
    43 static char mmin_char;
    44 static int mmin_mins;
    45 #endif
    46 
    47 #ifdef CONFIG_FEATURE_FIND_XDEV
    48 static dev_t *xdev_dev;
    49 static int xdev_count = 0;
    50 #endif
    51 
    52 #ifdef CONFIG_FEATURE_FIND_NEWER
    53 static time_t newer_mtime;
    54 #endif
    55 
    56 #ifdef CONFIG_FEATURE_FIND_INUM
    57 static ino_t inode_num;
    58 #endif
    59 
    60 #ifdef CONFIG_FEATURE_FIND_EXEC
    61 static char **exec_str;
    62 static int num_matches;
    63 static int exec_opt;
    64 #endif
    65 
    66 static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
    67 {
    68 #ifdef CONFIG_FEATURE_FIND_XDEV
     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
    69384    if (S_ISDIR(statbuf->st_mode) && xdev_count) {
    70         int i;
    71         for (i=0; i<xdev_count; i++) {
    72             if (xdev_dev[i] != statbuf->st_dev)
    73                 return SKIP;
    74         }
    75     }
    76 #endif
    77     if (pattern != NULL) {
    78         const char *tmp = strrchr(fileName, '/');
    79 
    80         if (tmp == NULL)
    81             tmp = fileName;
    82         else
    83             tmp++;
    84         if (!(fnmatch(pattern, tmp, FNM_PERIOD) == 0))
    85             goto no_match;
    86     }
    87 #ifdef CONFIG_FEATURE_FIND_TYPE
    88     if (type_mask != 0) {
    89         if (!((statbuf->st_mode & S_IFMT) == type_mask))
    90             goto no_match;
    91     }
    92 #endif
    93 #ifdef CONFIG_FEATURE_FIND_PERM
    94     if (perm_mask != 0) {
    95         if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) ||
    96              (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) ||
    97              (perm_char == '+' && (statbuf->st_mode & perm_mask) != 0)))
    98             goto no_match;
    99     }
    100 #endif
    101 #ifdef CONFIG_FEATURE_FIND_MTIME
    102     if (mtime_char != 0) {
    103         time_t file_age = time(NULL) - statbuf->st_mtime;
    104         time_t mtime_secs = mtime_days * 24 * 60 * 60;
    105         if (!((isdigit(mtime_char) && file_age >= mtime_secs &&
    106                         file_age < mtime_secs + 24 * 60 * 60) ||
    107                 (mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) ||
    108                 (mtime_char == '-' && file_age < mtime_secs)))
    109             goto no_match;
    110     }
    111 #endif
    112 #ifdef CONFIG_FEATURE_FIND_MMIN
    113     if (mmin_char != 0) {
    114         time_t file_age = time(NULL) - statbuf->st_mtime;
    115         time_t mmin_secs = mmin_mins * 60;
    116         if (!((isdigit(mmin_char) && file_age >= mmin_secs &&
    117                         file_age < mmin_secs + 60) ||
    118                 (mmin_char == '+' && file_age >= mmin_secs + 60) ||
    119                 (mmin_char == '-' && file_age < mmin_secs)))
    120             goto no_match;
    121     }
    122 #endif
    123 #ifdef CONFIG_FEATURE_FIND_NEWER
    124     if (newer_mtime != 0) {
    125         time_t file_age = newer_mtime - statbuf->st_mtime;
    126         if (file_age >= 0)
    127             goto no_match;
    128     }
    129 #endif
    130 #ifdef CONFIG_FEATURE_FIND_INUM
    131     if (inode_num != 0) {
    132         if (!(statbuf->st_ino == inode_num))
    133             goto no_match;
    134     }
    135 #endif
    136 #ifdef CONFIG_FEATURE_FIND_EXEC
    137     if (exec_opt) {
    138         int i;
    139         char *cmd_string = "";
    140         for (i = 0; i < num_matches; i++)
    141             cmd_string = bb_xasprintf("%s%s%s", cmd_string, exec_str[i], fileName);
    142         cmd_string = bb_xasprintf("%s%s", cmd_string, exec_str[num_matches]);
    143         system(cmd_string);
    144         goto no_match;
    145     }
    146 #endif
    147 
    148 #ifdef CONFIG_FEATURE_FIND_PRINT0
    149     printf("%s%c", fileName, printsep);
     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
    150759#else
    151     puts(fileName);
    152 #endif
    153 no_match:
    154     return (TRUE);
    155 }
    156 
    157 #ifdef CONFIG_FEATURE_FIND_TYPE
    158 static int find_type(char *type)
    159 {
    160     int mask = 0;
    161 
    162     switch (type[0]) {
    163         case 'b':
    164             mask = S_IFBLK;
    165             break;
    166         case 'c':
    167             mask = S_IFCHR;
    168             break;
    169         case 'd':
    170             mask = S_IFDIR;
    171             break;
    172         case 'p':
    173             mask = S_IFIFO;
    174             break;
    175         case 'f':
    176             mask = S_IFREG;
    177             break;
    178         case 'l':
    179             mask = S_IFLNK;
    180             break;
    181         case 's':
    182             mask = S_IFSOCK;
    183             break;
    184     }
    185 
    186     if (mask == 0 || type[1] != '\0')
    187         bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
    188 
    189     return mask;
    190 }
    191 #endif
    192 
     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);
    193798int find_main(int argc, char **argv)
    194799{
    195     int dereference = FALSE;
     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;
    196813    int i, firstopt, status = EXIT_SUCCESS;
     814#if ENABLE_FEATURE_FIND_MAXDEPTH
     815    int maxdepth = INT_MAX;
     816#endif
    197817
    198818    for (firstopt = 1; firstopt < argc; firstopt++) {
    199819        if (argv[firstopt][0] == '-')
    200820            break;
    201     }
    202 
    203     /* Parse any options */
    204     for (i = firstopt; i < argc; i++) {
    205         if (strcmp(argv[i], "-follow") == 0)
    206             dereference = TRUE;
    207         else if (strcmp(argv[i], "-print") == 0) {
    208             ;
    209             }
    210 #ifdef CONFIG_FEATURE_FIND_PRINT0
    211         else if (strcmp(argv[i], "-print0") == 0)
    212             printsep = '\0';
    213 #endif
    214         else if (strcmp(argv[i], "-name") == 0) {
    215             if (++i == argc)
    216                 bb_error_msg_and_die(bb_msg_requires_arg, "-name");
    217             pattern = argv[i];
    218 #ifdef CONFIG_FEATURE_FIND_TYPE
    219         } else if (strcmp(argv[i], "-type") == 0) {
    220             if (++i == argc)
    221                 bb_error_msg_and_die(bb_msg_requires_arg, "-type");
    222             type_mask = find_type(argv[i]);
    223 #endif
    224 #ifdef CONFIG_FEATURE_FIND_PERM
    225         } else if (strcmp(argv[i], "-perm") == 0) {
    226             char *end;
    227             if (++i == argc)
    228                 bb_error_msg_and_die(bb_msg_requires_arg, "-perm");
    229             perm_mask = strtol(argv[i], &end, 8);
    230             if ((end[0] != '\0') || (perm_mask > 07777))
    231                 bb_error_msg_and_die(bb_msg_invalid_arg, argv[i], "-perm");
    232             if ((perm_char = argv[i][0]) == '-')
    233                 perm_mask = -perm_mask;
    234 #endif
    235 #ifdef CONFIG_FEATURE_FIND_MTIME
    236         } else if (strcmp(argv[i], "-mtime") == 0) {
    237             char *end;
    238             if (++i == argc)
    239                 bb_error_msg_and_die(bb_msg_requires_arg, "-mtime");
    240             mtime_days = strtol(argv[i], &end, 10);
    241             if (end[0] != '\0')
    242                 bb_error_msg_and_die(bb_msg_invalid_arg, argv[i], "-mtime");
    243             if ((mtime_char = argv[i][0]) == '-')
    244                 mtime_days = -mtime_days;
    245 #endif
    246 #ifdef CONFIG_FEATURE_FIND_MMIN
    247         } else if (strcmp(argv[i], "-mmin") == 0) {
    248             char *end;
    249             if (++i == argc)
    250                 bb_error_msg_and_die(bb_msg_requires_arg, "-mmin");
    251             mmin_mins = strtol(argv[i], &end, 10);
    252             if (end[0] != '\0')
    253                 bb_error_msg_and_die(bb_msg_invalid_arg, argv[i], "-mmin");
    254             if ((mmin_char = argv[i][0]) == '-')
    255                 mmin_mins = -mmin_mins;
    256 #endif
    257 #ifdef CONFIG_FEATURE_FIND_XDEV
    258         } else if (strcmp(argv[i], "-xdev") == 0) {
     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) {
    259850            struct stat stbuf;
    260 
    261             xdev_count = ( firstopt - 1 ) ? ( firstopt - 1 ) : 1;
    262             xdev_dev = xmalloc ( xdev_count * sizeof( dev_t ));
    263 
    264             if ( firstopt == 1 ) {
    265                 xstat ( ".", &stbuf );
    266                 xdev_dev [0] = stbuf. st_dev;
    267             }
    268             else {
    269 
     851            if (!xdev_count) {
     852                xdev_count = firstopt - 1;
     853                xdev_dev = xmalloc(xdev_count * sizeof(dev_t));
    270854                for (i = 1; i < firstopt; i++) {
    271                     xstat ( argv [i], &stbuf );
    272                     xdev_dev [i-1] = stbuf. st_dev;
     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;
    273860                }
    274861            }
    275 #endif
    276 #ifdef CONFIG_FEATURE_FIND_NEWER
    277         } else if (strcmp(argv[i], "-newer") == 0) {
    278             struct stat stat_newer;
    279             if (++i == argc)
    280                 bb_error_msg_and_die(bb_msg_requires_arg, "-newer");
    281             xstat (argv[i], &stat_newer);
    282             newer_mtime = stat_newer.st_mtime;
    283 #endif
    284 #ifdef CONFIG_FEATURE_FIND_INUM
    285         } else if (strcmp(argv[i], "-inum") == 0) {
    286             char *end;
    287             if (++i == argc)
    288                 bb_error_msg_and_die(bb_msg_requires_arg, "-inum");
    289             inode_num = strtol(argv[i], &end, 10);
    290             if (end[0] != '\0')
    291                 bb_error_msg_and_die(bb_msg_invalid_arg, argv[i], "-inum");
    292 #endif
    293 #ifdef CONFIG_FEATURE_FIND_EXEC
    294         } else if (strcmp(argv[i], "-exec") == 0) {
    295             int b_pos;
    296             char *cmd_string = "";
    297 
    298             while (i++) {
    299                 if (i == argc)
    300                     bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
    301                 if (*argv[i] == ';')
    302                     break;
    303                 cmd_string = bb_xasprintf("%s %s", cmd_string, argv[i]);
    304             }
    305 
    306             if (*cmd_string == 0)
    307                 bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
    308             cmd_string++;
    309             exec_str = xmalloc(sizeof(char *));
    310 
    311             while ((b_pos = strstr(cmd_string, "{}") - cmd_string), (b_pos >= 0)) {
    312                 num_matches++;
    313                 exec_str = xrealloc(exec_str, (num_matches + 1) * sizeof(char *));
    314                 exec_str[num_matches - 1] = bb_xstrndup(cmd_string, b_pos);
    315                 cmd_string += b_pos + 2;
    316             }
    317             exec_str[num_matches] = bb_xstrdup(cmd_string);
    318             exec_opt = 1;
    319 #endif
    320         } else
    321             bb_show_usage();
    322     }
    323 
    324     if (firstopt == 1) {
    325         if (! recursive_action(".", TRUE, dereference, FALSE, fileAction,
    326                     fileAction, NULL))
     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 */
    327893            status = EXIT_FAILURE;
    328     } else {
    329         for (i = 1; i < firstopt; i++) {
    330             if (! recursive_action(argv[i], TRUE, dereference, FALSE, fileAction,
    331                         fileAction, NULL))
    332                 status = EXIT_FAILURE;
    333         }
    334     }
    335 
     894    }
    336895    return status;
    337896}
  • branches/stable/mindi-busybox/findutils/grep.c

    r821 r1770  
    66 * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
    77 *
    8  * Licensed under the GPL v2, see the file LICENSE in this tarball.
     8 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
    99 */
    1010/* BB_AUDIT SUSv3 defects - unsupported option -x.  */
     
    1515 * correction "-e pattern1 -e pattern2" logic and more optimizations.
    1616 * precompiled regex
    17 */
    18 
    19 #include "busybox.h"
    20 #include <stdio.h>
    21 #include <stdlib.h>
    22 #include <getopt.h>
    23 #include <string.h>
    24 #include <errno.h>
     17 */
     18/*
     19 * (C) 2006 Jac Goudsmit added -o option
     20 */
     21
     22#include "libbb.h"
    2523#include "xregex.h"
    2624
    27 
    2825/* options */
    29 static unsigned long opt;
    30 #define GREP_OPTS "lnqvscFiHhe:f:L"
    31 #define GREP_OPT_l (1<<0)
    32 #define PRINT_FILES_WITH_MATCHES (opt & GREP_OPT_l)
    33 #define GREP_OPT_n (1<<1)
    34 #define PRINT_LINE_NUM (opt & GREP_OPT_n)
    35 #define GREP_OPT_q (1<<2)
    36 #define BE_QUIET (opt & GREP_OPT_q)
    37 #define GREP_OPT_v (1<<3)
    38 typedef char invert_search_t;
    39 static invert_search_t invert_search;
    40 #define GREP_OPT_s (1<<4)
    41 #define SUPPRESS_ERR_MSGS (opt & GREP_OPT_s)
    42 #define GREP_OPT_c (1<<5)
    43 #define PRINT_MATCH_COUNTS (opt & GREP_OPT_c)
    44 #define GREP_OPT_F (1<<6)
    45 #define FGREP_FLAG (opt & GREP_OPT_F)
    46 #define GREP_OPT_i (1<<7)
    47 #define GREP_OPT_H (1<<8)
    48 #define GREP_OPT_h (1<<9)
    49 #define GREP_OPT_e (1<<10)
    50 #define GREP_OPT_f (1<<11)
    51 #define GREP_OPT_L (1<<12)
    52 #define PRINT_FILES_WITHOUT_MATCHES ((opt & GREP_OPT_L) != 0)
    53 #if ENABLE_FEATURE_GREP_CONTEXT
    54 #define GREP_OPT_CONTEXT "A:B:C"
    55 #define GREP_OPT_A (1<<13)
    56 #define GREP_OPT_B (1<<14)
    57 #define GREP_OPT_C (1<<15)
    58 #define GREP_OPT_E (1<<16)
    59 #else
    60 #define GREP_OPT_CONTEXT ""
    61 #define GREP_OPT_A (0)
    62 #define GREP_OPT_B (0)
    63 #define GREP_OPT_C (0)
    64 #define GREP_OPT_E (1<<13)
    65 #endif
    66 #if ENABLE_FEATURE_GREP_EGREP_ALIAS
    67 # define OPT_EGREP "E"
    68 #else
    69 # define OPT_EGREP ""
    70 #endif
    71 
     26#define OPTSTR_GREP \
     27    "lnqvscFiHhe:f:Lorm:" \
     28    USE_FEATURE_GREP_CONTEXT("A:B:C:") \
     29    USE_FEATURE_GREP_EGREP_ALIAS("E") \
     30    USE_DESKTOP("w") \
     31    "aI"
     32/* ignored: -a "assume all files to be text" */
     33/* ignored: -I "assume binary files have no matches" */
     34
     35enum {
     36    OPTBIT_l, /* list matched file names only */
     37    OPTBIT_n, /* print line# */
     38    OPTBIT_q, /* quiet - exit(0) of first match */
     39    OPTBIT_v, /* invert the match, to select non-matching lines */
     40    OPTBIT_s, /* suppress errors about file open errors */
     41    OPTBIT_c, /* count matches per file (suppresses normal output) */
     42    OPTBIT_F, /* literal match */
     43    OPTBIT_i, /* case-insensitive */
     44    OPTBIT_H, /* force filename display */
     45    OPTBIT_h, /* inhibit filename display */
     46    OPTBIT_e, /* -e PATTERN */
     47    OPTBIT_f, /* -f FILE_WITH_PATTERNS */
     48    OPTBIT_L, /* list unmatched file names only */
     49    OPTBIT_o, /* show only matching parts of lines */
     50    OPTBIT_r, /* recurse dirs */
     51    OPTBIT_m, /* -m MAX_MATCHES */
     52    USE_FEATURE_GREP_CONTEXT(    OPTBIT_A ,) /* -A NUM: after-match context */
     53    USE_FEATURE_GREP_CONTEXT(    OPTBIT_B ,) /* -B NUM: before-match context */
     54    USE_FEATURE_GREP_CONTEXT(    OPTBIT_C ,) /* -C NUM: -A and -B combined */
     55    USE_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */
     56    USE_DESKTOP(                 OPTBIT_w ,) /* whole word match */
     57    OPT_l = 1 << OPTBIT_l,
     58    OPT_n = 1 << OPTBIT_n,
     59    OPT_q = 1 << OPTBIT_q,
     60    OPT_v = 1 << OPTBIT_v,
     61    OPT_s = 1 << OPTBIT_s,
     62    OPT_c = 1 << OPTBIT_c,
     63    OPT_F = 1 << OPTBIT_F,
     64    OPT_i = 1 << OPTBIT_i,
     65    OPT_H = 1 << OPTBIT_H,
     66    OPT_h = 1 << OPTBIT_h,
     67    OPT_e = 1 << OPTBIT_e,
     68    OPT_f = 1 << OPTBIT_f,
     69    OPT_L = 1 << OPTBIT_L,
     70    OPT_o = 1 << OPTBIT_o,
     71    OPT_r = 1 << OPTBIT_r,
     72    OPT_m = 1 << OPTBIT_m,
     73    OPT_A = USE_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_A)) + 0,
     74    OPT_B = USE_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_B)) + 0,
     75    OPT_C = USE_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_C)) + 0,
     76    OPT_E = USE_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0,
     77    OPT_w = USE_DESKTOP(                 (1 << OPTBIT_w)) + 0,
     78};
     79
     80#define PRINT_FILES_WITH_MATCHES    (option_mask32 & OPT_l)
     81#define PRINT_LINE_NUM              (option_mask32 & OPT_n)
     82#define BE_QUIET                    (option_mask32 & OPT_q)
     83#define SUPPRESS_ERR_MSGS           (option_mask32 & OPT_s)
     84#define PRINT_MATCH_COUNTS          (option_mask32 & OPT_c)
     85#define FGREP_FLAG                  (option_mask32 & OPT_F)
     86#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L)
     87
     88typedef unsigned char byte_t;
     89
     90static int max_matches;
    7291static int reflags;
    73 static int print_filename;
    74 
    75 #if ENABLE_FEATURE_GREP_CONTEXT
     92static byte_t invert_search;
     93static byte_t print_filename;
     94static byte_t open_errors;
     95
     96#if ENABLE_FEATURE_GREP_CONTEXT
     97static byte_t did_print_line;
    7698static int lines_before;
    7799static int lines_after;
     
    79101static int last_line_printed;
    80102#endif /* ENABLE_FEATURE_GREP_CONTEXT */
    81 
    82103/* globals used internally */
    83104static llist_t *pattern_head;   /* growable list of patterns to match */
    84 static char *cur_file;          /* the current file we are reading */
    85 
    86 typedef struct GREP_LIST_DATA {
     105static const char *cur_file;    /* the current file we are reading */
     106
     107typedef struct grep_list_data_t {
    87108    char *pattern;
    88109    regex_t preg;
     
    95116{
    96117#if ENABLE_FEATURE_GREP_CONTEXT
     118    /* Happens when we go to next file, immediately hit match
     119     * and try to print prev context... from prev file! Don't do it */
     120    if (linenum < 1)
     121        return;
    97122    /* possibly print the little '--' separator */
    98     if ((lines_before || lines_after) && last_line_printed &&
    99             last_line_printed < linenum - 1) {
     123    if ((lines_before || lines_after) && did_print_line &&
     124            last_line_printed != linenum - 1) {
    100125        puts("--");
    101126    }
     127    /* guard against printing "--" before first line of first file */
     128    did_print_line = 1;
    102129    last_line_printed = linenum;
    103130#endif
    104     if (print_filename > 0)
     131    if (print_filename)
    105132        printf("%s%c", cur_file, decoration);
    106133    if (PRINT_LINE_NUM)
    107134        printf("%i%c", linenum, decoration);
    108     puts(line);
    109 }
    110 
     135    /* Emulate weird GNU grep behavior with -ov */
     136    if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o))
     137        puts(line);
     138}
    111139
    112140static int grep_file(FILE *file)
    113141{
    114142    char *line;
    115     invert_search_t ret;
     143    byte_t ret;
    116144    int linenum = 0;
    117145    int nmatches = 0;
     146    regmatch_t regmatch;
    118147#if ENABLE_FEATURE_GREP_CONTEXT
    119148    int print_n_lines_after = 0;
    120149    int curpos = 0; /* track where we are in the circular 'before' buffer */
    121150    int idx = 0; /* used for iteration through the circular buffer */
     151#else
     152    enum { print_n_lines_after = 0 };
    122153#endif /* ENABLE_FEATURE_GREP_CONTEXT */
    123154
    124     while ((line = bb_get_chomped_line_from_file(file)) != NULL) {
     155    while ((line = xmalloc_getline(file)) != NULL) {
    125156        llist_t *pattern_ptr = pattern_head;
    126157        grep_list_data_t * gl;
     
    139170                 * invert search)
    140171                 */
    141                 if(!(gl->flg_mem_alocated_compiled & COMPILED)) {
     172                if (!(gl->flg_mem_alocated_compiled & COMPILED)) {
    142173                    gl->flg_mem_alocated_compiled |= COMPILED;
    143174                    xregcomp(&(gl->preg), gl->pattern, reflags);
    144175                }
    145                 ret |= regexec(&(gl->preg), line, 0, NULL, 0) == 0;
     176                regmatch.rm_so = 0;
     177                regmatch.rm_eo = 0;
     178                if (regexec(&(gl->preg), line, 1, &regmatch, 0) == 0) {
     179                    if (!(option_mask32 & OPT_w))
     180                        ret = 1;
     181                    else {
     182                        char c = ' ';
     183                        if (regmatch.rm_so)
     184                            c = line[regmatch.rm_so - 1];
     185                        if (!isalnum(c) && c != '_') {
     186                            c = line[regmatch.rm_eo];
     187                            if (!c || (!isalnum(c) && c != '_'))
     188                                ret = 1;
     189                        }
     190                    }
     191                }
    146192            }
    147193            pattern_ptr = pattern_ptr->link;
    148194        } /* while (pattern_ptr) */
    149195
    150         if ((ret ^ invert_search)) {
    151 
    152             if (PRINT_FILES_WITH_MATCHES || BE_QUIET)
    153                 free(line);
    154 
    155             /* if we found a match but were told to be quiet, stop here */
    156             if (BE_QUIET || PRINT_FILES_WITHOUT_MATCHES)
    157                 return -1;
    158 
    159                 /* keep track of matches */
    160                 nmatches++;
    161 
     196        if (ret ^ invert_search) {
     197            /* keep track of matches */
     198            nmatches++;
     199
     200            /* quiet/print (non)matching file names only? */
     201            if (option_mask32 & (OPT_q|OPT_l|OPT_L)) {
     202                free(line); /* we don't need line anymore */
     203                if (BE_QUIET) {
     204                    /* manpage says about -q:
     205                     * "exit immediately with zero status
     206                     * if any match is found,
     207                     * even if errors were detected" */
     208                    exit(0);
     209                }
    162210                /* if we're just printing filenames, we stop after the first match */
    163                 if (PRINT_FILES_WITH_MATCHES)
    164                     break;
    165 
    166                 /* print the matched line */
    167                 if (PRINT_MATCH_COUNTS == 0) {
    168 #if ENABLE_FEATURE_GREP_CONTEXT
    169                     int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
    170 
    171                     /* if we were told to print 'before' lines and there is at least
    172                      * one line in the circular buffer, print them */
    173                     if (lines_before && before_buf[prevpos] != NULL) {
    174                         int first_buf_entry_line_num = linenum - lines_before;
    175 
    176                         /* advance to the first entry in the circular buffer, and
    177                          * figure out the line number is of the first line in the
    178                          * buffer */
    179                         idx = curpos;
    180                         while (before_buf[idx] == NULL) {
    181                             idx = (idx + 1) % lines_before;
    182                             first_buf_entry_line_num++;
    183                         }
    184 
    185                         /* now print each line in the buffer, clearing them as we go */
    186                         while (before_buf[idx] != NULL) {
    187                             print_line(before_buf[idx], first_buf_entry_line_num, '-');
    188                             free(before_buf[idx]);
    189                             before_buf[idx] = NULL;
    190                             idx = (idx + 1) % lines_before;
    191                             first_buf_entry_line_num++;
    192                         }
     211                if (PRINT_FILES_WITH_MATCHES) {
     212                    puts(cur_file);
     213                    /* fall thru to "return 1" */
     214                }
     215                /* OPT_L aka PRINT_FILES_WITHOUT_MATCHES: return early */
     216                return 1; /* one match */
     217            }
     218
     219#if ENABLE_FEATURE_GREP_CONTEXT
     220            /* Were we printing context and saw next (unwanted) match? */
     221            if ((option_mask32 & OPT_m) && nmatches > max_matches)
     222                break;
     223#endif
     224
     225            /* print the matched line */
     226            if (PRINT_MATCH_COUNTS == 0) {
     227#if ENABLE_FEATURE_GREP_CONTEXT
     228                int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
     229
     230                /* if we were told to print 'before' lines and there is at least
     231                 * one line in the circular buffer, print them */
     232                if (lines_before && before_buf[prevpos] != NULL) {
     233                    int first_buf_entry_line_num = linenum - lines_before;
     234
     235                    /* advance to the first entry in the circular buffer, and
     236                     * figure out the line number is of the first line in the
     237                     * buffer */
     238                    idx = curpos;
     239                    while (before_buf[idx] == NULL) {
     240                        idx = (idx + 1) % lines_before;
     241                        first_buf_entry_line_num++;
    193242                    }
    194243
    195                     /* make a note that we need to print 'after' lines */
    196                     print_n_lines_after = lines_after;
     244                    /* now print each line in the buffer, clearing them as we go */
     245                    while (before_buf[idx] != NULL) {
     246                        print_line(before_buf[idx], first_buf_entry_line_num, '-');
     247                        free(before_buf[idx]);
     248                        before_buf[idx] = NULL;
     249                        idx = (idx + 1) % lines_before;
     250                        first_buf_entry_line_num++;
     251                    }
     252                }
     253
     254                /* make a note that we need to print 'after' lines */
     255                print_n_lines_after = lines_after;
    197256#endif
     257                if (option_mask32 & OPT_o) {
     258                    line[regmatch.rm_eo] = '\0';
     259                    print_line(line + regmatch.rm_so, linenum, ':');
     260                } else {
    198261                    print_line(line, linenum, ':');
    199262                }
    200263            }
    201 #if ENABLE_FEATURE_GREP_CONTEXT
    202             else { /* no match */
    203                 /* Add the line to the circular 'before' buffer */
    204                 if(lines_before) {
    205                     free(before_buf[curpos]);
    206                     before_buf[curpos] = bb_xstrdup(line);
    207                     curpos = (curpos + 1) % lines_before;
    208                 }
    209             }
    210 
     264        }
     265#if ENABLE_FEATURE_GREP_CONTEXT
     266        else { /* no match */
    211267            /* if we need to print some context lines after the last match, do so */
    212             if (print_n_lines_after && (last_line_printed != linenum)) {
     268            if (print_n_lines_after) {
    213269                print_line(line, linenum, '-');
    214270                print_n_lines_after--;
     271            } else if (lines_before) {
     272                /* Add the line to the circular 'before' buffer */
     273                free(before_buf[curpos]);
     274                before_buf[curpos] = line;
     275                curpos = (curpos + 1) % lines_before;
     276                /* avoid free(line) - we took line */
     277                line = NULL;
    215278            }
     279        }
     280
    216281#endif /* ENABLE_FEATURE_GREP_CONTEXT */
    217282        free(line);
    218     }
    219 
     283
     284        /* Did we print all context after last requested match? */
     285        if ((option_mask32 & OPT_m)
     286         && !print_n_lines_after && nmatches == max_matches)
     287            break;
     288    }
    220289
    221290    /* special-case file post-processing for options where we don't print line
     
    224293    /* grep -c: print [filename:]count, even if count is zero */
    225294    if (PRINT_MATCH_COUNTS) {
    226         if (print_filename > 0)
     295        if (print_filename)
    227296            printf("%s:", cur_file);
    228             printf("%d\n", nmatches);
    229     }
    230 
    231     /* grep -l: print just the filename, but only if we grepped the line in the file  */
    232     if (PRINT_FILES_WITH_MATCHES && nmatches > 0) {
    233         puts(cur_file);
    234     }
    235 
    236     /* grep -L: print just the filename, but only if we didn't grep the line in the file  */
    237     if (PRINT_FILES_WITHOUT_MATCHES && nmatches == 0) {
     297        printf("%d\n", nmatches);
     298    }
     299
     300    /* grep -L: print just the filename */
     301    if (PRINT_FILES_WITHOUT_MATCHES) {
     302        /* nmatches is zero, no need to check it:
     303         * we return 1 early if we detected a match
     304         * and PRINT_FILES_WITHOUT_MATCHES is set */
    238305        puts(cur_file);
    239306    }
     
    260327}
    261328
    262 
    263329static void load_regexes_from_file(llist_t *fopt)
    264330{
     
    266332    FILE *f;
    267333
    268     while(fopt) {
     334    while (fopt) {
    269335        llist_t *cur = fopt;
    270336        char *ffile = cur->data;
     
    272338        fopt = cur->link;
    273339        free(cur);
    274         f = bb_xfopen(ffile, "r");
    275         while ((line = bb_get_chomped_line_from_file(f)) != NULL) {
     340        f = xfopen(ffile, "r");
     341        while ((line = xmalloc_getline(f)) != NULL) {
    276342            llist_add_to(&pattern_head,
    277343                new_grep_list_data(line, PATTERN_MEM_A));
     
    280346}
    281347
    282 
     348static int file_action_grep(const char *filename, struct stat *statbuf, void* matched, int depth)
     349{
     350    FILE *file = fopen(filename, "r");
     351    if (file == NULL) {
     352        if (!SUPPRESS_ERR_MSGS)
     353            bb_perror_msg("%s", cur_file);
     354        open_errors = 1;
     355        return 0;
     356    }
     357    cur_file = filename;
     358    *(int*)matched += grep_file(file);
     359    fclose(file);
     360    return 1;
     361}
     362
     363static int grep_dir(const char *dir)
     364{
     365    int matched = 0;
     366    recursive_action(dir,
     367        /* recurse=yes */ ACTION_RECURSE |
     368        /* followLinks=no */
     369        /* depthFirst=yes */ ACTION_DEPTHFIRST,
     370        /* fileAction= */ file_action_grep,
     371        /* dirAction= */ NULL,
     372        /* userData= */ &matched,
     373        /* depth= */ 0);
     374    return matched;
     375}
     376
     377int grep_main(int argc, char **argv);
    283378int grep_main(int argc, char **argv)
    284379{
    285380    FILE *file;
    286381    int matched;
     382    char *mopt;
    287383    llist_t *fopt = NULL;
    288     int error_open_count = 0;
    289384
    290385    /* do normal option parsing */
    291386#if ENABLE_FEATURE_GREP_CONTEXT
    292   {
    293     char *junk;
    294387    char *slines_after;
    295388    char *slines_before;
    296389    char *Copt;
    297390
    298     bb_opt_complementally = "H-h:e::f::C-AB";
    299     opt = bb_getopt_ulflags(argc, argv,
    300         GREP_OPTS GREP_OPT_CONTEXT OPT_EGREP,
    301         &pattern_head, &fopt,
     391    opt_complementary = "H-h:e::f::C-AB";
     392    getopt32(argv,
     393        OPTSTR_GREP,
     394        &pattern_head, &fopt, &mopt,
    302395        &slines_after, &slines_before, &Copt);
    303396
    304     if(opt & GREP_OPT_C) {
    305         /* C option unseted A and B options, but next -A or -B
    306            may be ovewrite own option */
    307         if(!(opt & GREP_OPT_A))         /* not overwtited */
     397    if (option_mask32 & OPT_C) {
     398        /* -C unsets prev -A and -B, but following -A or -B
     399           may override it */
     400        if (!(option_mask32 & OPT_A)) /* not overridden */
    308401            slines_after = Copt;
    309         if(!(opt & GREP_OPT_B))         /* not overwtited */
     402        if (!(option_mask32 & OPT_B)) /* not overridden */
    310403            slines_before = Copt;
    311         opt |= GREP_OPT_A|GREP_OPT_B;   /* set for parse now */
    312     }
    313     if(opt & GREP_OPT_A) {
    314         lines_after = strtoul(slines_after, &junk, 10);
    315         if(*junk != '\0')
    316             bb_error_msg_and_die(bb_msg_invalid_arg, slines_after, "-A");
    317     }
    318     if(opt & GREP_OPT_B) {
    319         lines_before = strtoul(slines_before, &junk, 10);
    320         if(*junk != '\0')
    321             bb_error_msg_and_die(bb_msg_invalid_arg, slines_before, "-B");
    322     }
    323     /* sanity checks after parse may be invalid numbers ;-) */
    324     if ((opt & (GREP_OPT_c|GREP_OPT_q|GREP_OPT_l|GREP_OPT_L))) {
    325         opt &= ~GREP_OPT_n;
     404        option_mask32 |= OPT_A|OPT_B; /* for parser */
     405    }
     406    if (option_mask32 & OPT_A) {
     407        lines_after = xatoi_u(slines_after);
     408    }
     409    if (option_mask32 & OPT_B) {
     410        lines_before = xatoi_u(slines_before);
     411    }
     412    /* sanity checks */
     413    if (option_mask32 & (OPT_c|OPT_q|OPT_l|OPT_L)) {
     414        option_mask32 &= ~OPT_n;
    326415        lines_before = 0;
    327416        lines_after = 0;
    328     } else if(lines_before > 0)
    329         before_buf = (char **)xcalloc(lines_before, sizeof(char *));
    330   }
     417    } else if (lines_before > 0)
     418        before_buf = xzalloc(lines_before * sizeof(char *));
    331419#else
    332420    /* with auto sanity checks */
    333     bb_opt_complementally = "H-h:e::f::c-n:q-n:l-n";
    334     opt = bb_getopt_ulflags(argc, argv, GREP_OPTS OPT_EGREP,
    335         &pattern_head, &fopt);
     421    opt_complementary = "H-h:e::f::c-n:q-n:l-n";
     422    getopt32(argv, OPTSTR_GREP,
     423        &pattern_head, &fopt, &mopt);
    336424#endif
    337     invert_search = (opt & GREP_OPT_v) != 0;        /* 0 | 1 */
    338 
    339     if(opt & GREP_OPT_H)
    340         print_filename++;
    341     if(opt & GREP_OPT_h)
    342         print_filename--;
     425    if (option_mask32 & OPT_m) {
     426        max_matches = xatoi_u(mopt);
     427    }
     428    invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */
     429
    343430    if (pattern_head != NULL) {
    344         /* convert char *argv[] to grep_list_data_t */
     431        /* convert char **argv to grep_list_data_t */
    345432        llist_t *cur;
    346433
    347         for(cur = pattern_head; cur; cur = cur->link)
     434        for (cur = pattern_head; cur; cur = cur->link)
    348435            cur->data = new_grep_list_data(cur->data, 0);
    349436    }
    350     if(opt & GREP_OPT_f)
     437    if (option_mask32 & OPT_f)
    351438        load_regexes_from_file(fopt);
    352439
    353     if(ENABLE_FEATURE_GREP_FGREP_ALIAS && bb_applet_name[0] == 'f')
    354         opt |= GREP_OPT_F;
    355 
    356     if(ENABLE_FEATURE_GREP_EGREP_ALIAS &&
    357             (bb_applet_name[0] == 'e' || (opt & GREP_OPT_E)))
    358         reflags = REG_EXTENDED | REG_NOSUB;
    359     else
     440    if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f')
     441        option_mask32 |= OPT_F;
     442
     443    if (!(option_mask32 & (OPT_o | OPT_w)))
    360444        reflags = REG_NOSUB;
    361445
    362     if(opt & GREP_OPT_i)
     446    if (ENABLE_FEATURE_GREP_EGREP_ALIAS &&
     447            (applet_name[0] == 'e' || (option_mask32 & OPT_E)))
     448        reflags |= REG_EXTENDED;
     449
     450    if (option_mask32 & OPT_i)
    363451        reflags |= REG_ICASE;
    364452
     
    369457     * argv[optind] should be the pattern. no pattern, no worky */
    370458    if (pattern_head == NULL) {
     459        char *pattern;
    371460        if (*argv == NULL)
    372461            bb_show_usage();
    373         else {
    374             char *pattern = new_grep_list_data(*argv++, 0);
    375 
    376             llist_add_to(&pattern_head, pattern);
    377             argc--;
    378         }
     462        pattern = new_grep_list_data(*argv++, 0);
     463        llist_add_to(&pattern_head, pattern);
     464        argc--;
    379465    }
    380466
    381467    /* argv[(optind)..(argc-1)] should be names of file to grep through. If
    382      * there is more than one file to grep, we will print the filenames */
    383     if (argc > 1) {
    384         print_filename++;
     468     * there is more than one file to grep, we will print the filenames. */
     469    if (argc > 1)
     470        print_filename = 1;
     471    /* -H / -h of course override */
     472    if (option_mask32 & OPT_H)
     473        print_filename = 1;
     474    if (option_mask32 & OPT_h)
     475        print_filename = 0;
    385476
    386477    /* If no files were specified, or '-' was specified, take input from
    387478     * stdin. Otherwise, we grep through all the files specified. */
    388     } else if (argc == 0) {
     479    if (argc == 0)
    389480        argc++;
    390     }
    391481    matched = 0;
    392482    while (argc--) {
    393483        cur_file = *argv++;
    394         if(!cur_file || (*cur_file == '-' && !cur_file[1])) {
     484        file = stdin;
     485        if (!cur_file || (*cur_file == '-' && !cur_file[1])) {
    395486            cur_file = "(standard input)";
    396             file = stdin;
    397487        } else {
     488            if (option_mask32 & OPT_r) {
     489                struct stat st;
     490                if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
     491                    if (!(option_mask32 & OPT_h))
     492                        print_filename = 1;
     493                    matched += grep_dir(cur_file);
     494                    goto grep_done;
     495                }
     496            }
     497            /* else: fopen(dir) will succeed, but reading won't */
    398498            file = fopen(cur_file, "r");
     499            if (file == NULL) {
     500                if (!SUPPRESS_ERR_MSGS)
     501                    bb_perror_msg("%s", cur_file);
     502                open_errors = 1;
     503                continue;
     504            }
    399505        }
    400         if (file == NULL) {
    401             if (!SUPPRESS_ERR_MSGS)
    402                 bb_perror_msg("%s", cur_file);
    403             error_open_count++;
    404         } else {
    405             matched += grep_file(file);
    406             if(matched < 0) {
    407                 /* we found a match but were told to be quiet, stop here and
    408                 * return success */
    409                 break;
    410             }
    411             fclose(file);
    412         }
     506        matched += grep_file(file);
     507        fclose_if_not_stdin(file);
     508 grep_done: ;
    413509    }
    414510
     
    421517
    422518            pattern_head = pattern_head->link;
    423             if((gl->flg_mem_alocated_compiled & PATTERN_MEM_A))
     519            if ((gl->flg_mem_alocated_compiled & PATTERN_MEM_A))
    424520                free(gl->pattern);
    425             if((gl->flg_mem_alocated_compiled & COMPILED))
     521            if ((gl->flg_mem_alocated_compiled & COMPILED))
    426522                regfree(&(gl->preg));
     523            free(gl);
    427524            free(pattern_head_ptr);
    428525        }
    429526    }
    430527    /* 0 = success, 1 = failed, 2 = error */
    431     /* If the -q option is specified, the exit status shall be zero
    432      * if an input line is selected, even if an error was detected.  */
    433     if(BE_QUIET && matched)
    434         return 0;
    435     if(error_open_count)
     528    if (open_errors)
    436529        return 2;
    437530    return !matched; /* invert return value 0 = success, 1 = failed */
  • branches/stable/mindi-busybox/findutils/xargs.c

    r821 r1770  
    1818 */
    1919
    20 #include "busybox.h"
    21 #include <stdio.h>
    22 #include <stdlib.h>
    23 #include <string.h>
    24 #include <unistd.h>
    25 #include <getopt.h>
    26 #include <errno.h>
    27 #include <fcntl.h>
    28 #include <sys/types.h>
    29 #include <sys/wait.h>
     20#include "libbb.h"
     21
     22/* This is a NOEXEC applet. Be very careful! */
     23
    3024
    3125/* COMPAT:  SYSV version defaults size (and has a max value of) to 470.
     
    4034
    4135#ifdef TEST
    42 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
    43 #  define CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
     36# ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
     37#  define ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION 1
    4438# endif
    45 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
    46 #  define CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
     39# ifndef ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
     40#  define ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 1
    4741# endif
    48 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
    49 #  define CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
     42# ifndef ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT
     43#  define ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT 1
    5044# endif
    51 # ifndef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
    52 #  define CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
     45# ifndef ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
     46#  define ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM 1
    5347# endif
    5448#endif
    5549
    5650/*
    57    This function have special algorithm.
    58    Don`t use fork and include to main!
     51   This function has special algorithm.
     52   Don't use fork and include to main!
    5953*/
    60 static int xargs_exec(char *const *args)
    61 {
    62     pid_t p;
    63     volatile int exec_errno = 0;    /* shared vfork stack */
    64 
    65     if ((p = vfork()) >= 0) {
    66         if (p == 0) {
    67             /* vfork -- child */
    68             execvp(args[0], args);
    69             exec_errno = errno;     /* set error to shared stack */
    70             _exit(1);
    71         } else {
    72             /* vfork -- parent */
    73             int status;
    74 
    75             while (wait(&status) == (pid_t) - 1)
    76                 if (errno != EINTR)
    77                     break;
    78             if (exec_errno) {
    79                 errno = exec_errno;
    80                 bb_perror_msg("%s", args[0]);
    81                 return exec_errno == ENOENT ? 127 : 126;
    82             } else {
    83                 if (WEXITSTATUS(status) == 255) {
    84                     bb_error_msg("%s: exited with status 255; aborting", args[0]);
    85                     return 124;
    86                 }
    87                 if (WIFSTOPPED(status)) {
    88                     bb_error_msg("%s: stopped by signal %d",
    89                         args[0], WSTOPSIG(status));
    90                     return 125;
    91                 }
    92                 if (WIFSIGNALED(status)) {
    93                     bb_error_msg("%s: terminated by signal %d",
    94                         args[0], WTERMSIG(status));
    95                     return 125;
    96                 }
    97                 if (WEXITSTATUS(status) != 0)
    98                     return 123;
    99                 return 0;
    100             }
    101         }
    102     } else {
    103         bb_perror_msg_and_die("vfork");
    104     }
    105 }
    106 
    107 
    108 typedef struct xlist_s {
    109     char *data;
    110     size_t lenght;
    111     struct xlist_s *link;
     54static int xargs_exec(char **args)
     55{
     56    int status;
     57
     58    status = spawn_and_wait(args);
     59    if (status < 0) {
     60        bb_perror_msg("%s", args[0]);
     61        return errno == ENOENT ? 127 : 126;
     62    }
     63    if (status == 255) {
     64        bb_error_msg("%s: exited with status 255; aborting", args[0]);
     65        return 124;
     66    }
     67/* Huh? I think we won't see this, ever. We don't wait with WUNTRACED!
     68    if (WIFSTOPPED(status)) {
     69        bb_error_msg("%s: stopped by signal %d",
     70            args[0], WSTOPSIG(status));
     71        return 125;
     72    }
     73*/
     74    if (status >= 1000) {
     75        bb_error_msg("%s: terminated by signal %d",
     76            args[0], status - 1000);
     77        return 125;
     78    }
     79    if (status)
     80        return 123;
     81    return 0;
     82}
     83
     84
     85typedef struct xlist_t {
     86    struct xlist_t *link;
     87    size_t length;
     88    char xstr[1];
    11289} xlist_t;
    11390
    114 static int eof_stdin_detected;
     91static smallint eof_stdin_detected;
    11592
    11693#define ISBLANK(c) ((c) == ' ' || (c) == '\t')
    117 #define ISSPACE(c) (ISBLANK (c) || (c) == '\n' || (c) == '\r' \
     94#define ISSPACE(c) (ISBLANK(c) || (c) == '\n' || (c) == '\r' \
    11895            || (c) == '\f' || (c) == '\v')
    11996
    120 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_QUOTES
    121 static xlist_t *process_stdin(xlist_t * list_arg,
     97#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
     98static xlist_t *process_stdin(xlist_t *list_arg,
    12299    const char *eof_str, size_t mc, char *buf)
    123100{
     
    129106    char *s = NULL;         /* start word */
    130107    char *p = NULL;         /* pointer to end word */
    131     char q = 0;             /* quote char */
     108    char q = '\0';          /* quote char */
    132109    char state = NORM;
    133110    char eof_str_detected = 0;
     
    137114    xlist_t *prev;
    138115
    139     for (prev = cur = list_arg; cur; cur = cur->link) {
    140         line_l += cur->lenght;  /* previous allocated */
    141         if (prev != cur)
    142             prev = prev->link;
     116    prev = cur = list_arg;
     117    while (1) {
     118        if (!cur) break;
     119        prev = cur;
     120        line_l += cur->length;
     121        cur = cur->link;
    143122    }
    144123
     
    146125        c = getchar();
    147126        if (c == EOF) {
    148             eof_stdin_detected++;
     127            eof_stdin_detected = 1;
    149128            if (s)
    150129                goto unexpected_eof;
     
    157136            goto set;
    158137        } else if (state == QUOTE) {
    159             if (c == q) {
    160                 q = 0;
    161                 state = NORM;
    162             } else {
     138            if (c != q)
    163139                goto set;
    164             }
    165         } else { /* if(state == NORM) */
    166 
     140            q = '\0';
     141            state = NORM;
     142        } else { /* if (state == NORM) */
    167143            if (ISSPACE(c)) {
    168144                if (s) {
    169 unexpected_eof:
     145 unexpected_eof:
    170146                    state = SPACE;
    171                     c = 0;
     147                    c = '\0';
    172148                    goto set;
    173149                }
     
    181157                    state = QUOTE;
    182158                } else {
    183 set:
     159 set:
    184160                    if ((size_t)(p - buf) >= mc)
    185161                        bb_error_msg_and_die("argument line too long");
     
    195171            /* word loaded */
    196172            if (eof_str) {
    197                 eof_str_detected = strcmp(s, eof_str) == 0;
     173                eof_str_detected = (strcmp(s, eof_str) == 0);
    198174            }
    199175            if (!eof_str_detected) {
    200                 size_t lenght = (p - buf);
    201 
    202                 cur = xmalloc(sizeof(xlist_t) + lenght);
    203                 cur->data = memcpy(cur + 1, s, lenght);
    204                 cur->lenght = lenght;
     176                size_t length = (p - buf);
     177                /* Dont xzalloc - it can be quite big */
     178                cur = xmalloc(offsetof(xlist_t, xstr) + length);
    205179                cur->link = NULL;
     180                cur->length = length;
     181                memcpy(cur->xstr, s, length);
    206182                if (prev == NULL) {
    207183                    list_arg = cur;
     
    210186                }
    211187                prev = cur;
    212                 line_l += lenght;
     188                line_l += length;
    213189                if (line_l > mc) {
    214190                    /* stop memory usage :-) */
     
    224200#else
    225201/* The variant does not support single quotes, double quotes or backslash */
    226 static xlist_t *process_stdin(xlist_t * list_arg,
    227     const char *eof_str, size_t mc, char *buf)
     202static xlist_t *process_stdin(xlist_t *list_arg,
     203        const char *eof_str, size_t mc, char *buf)
    228204{
    229205
    230206    int c;                  /* current char */
    231     int eof_str_detected = 0;
     207    char eof_str_detected = 0;
    232208    char *s = NULL;         /* start word */
    233209    char *p = NULL;         /* pointer to end word */
     
    236212    xlist_t *prev;
    237213
    238     for (prev = cur = list_arg; cur; cur = cur->link) {
    239         line_l += cur->lenght;  /* previous allocated */
    240         if (prev != cur)
    241             prev = prev->link;
     214    prev = cur = list_arg;
     215    while (1) {
     216        if (!cur) break;
     217        prev = cur;
     218        line_l += cur->length;
     219        cur = cur->link;
    242220    }
    243221
     
    245223        c = getchar();
    246224        if (c == EOF) {
    247             eof_stdin_detected++;
     225            eof_stdin_detected = 1;
    248226        }
    249227        if (eof_str_detected)
     
    258236        if ((p - buf) >= mc)
    259237            bb_error_msg_and_die("argument line too long");
    260         *p++ = c == EOF ? 0 : c;
     238        *p++ = (c == EOF ? '\0' : c);
    261239        if (c == EOF) { /* word's delimiter or EOF detected */
    262240            /* word loaded */
    263241            if (eof_str) {
    264                 eof_str_detected = strcmp(s, eof_str) == 0;
     242                eof_str_detected = (strcmp(s, eof_str) == 0);
    265243            }
    266244            if (!eof_str_detected) {
    267                 size_t lenght = (p - buf);
    268 
    269                 cur = xmalloc(sizeof(xlist_t) + lenght);
    270                 cur->data = memcpy(cur + 1, s, lenght);
    271                 cur->lenght = lenght;
     245                size_t length = (p - buf);
     246                /* Dont xzalloc - it can be quite big */
     247                cur = xmalloc(offsetof(xlist_t, xstr) + length);
    272248                cur->link = NULL;
     249                cur->length = length;
     250                memcpy(cur->xstr, s, length);
    273251                if (prev == NULL) {
    274252                    list_arg = cur;
     
    277255                }
    278256                prev = cur;
    279                 line_l += lenght;
     257                line_l += length;
    280258                if (line_l > mc) {
    281259                    /* stop memory usage :-) */
     
    288266    return list_arg;
    289267}
    290 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_QUOTES */
    291 
    292 
    293 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
     268#endif /* FEATURE_XARGS_SUPPORT_QUOTES */
     269
     270
     271#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
    294272/* Prompt the user for a response, and
    295273   if the user responds affirmatively, return true;
    296    otherwise, return false. Used "/dev/tty", not stdin. */
     274   otherwise, return false. Uses "/dev/tty", not stdin. */
    297275static int xargs_ask_confirmation(void)
    298276{
    299     static FILE *tty_stream;
     277    FILE *tty_stream;
    300278    int c, savec;
    301279
    302     if (!tty_stream) {
    303         tty_stream = bb_xfopen(CURRENT_TTY, "r");
    304         /* pranoidal security by vodz */
    305         fcntl(fileno(tty_stream), F_SETFD, FD_CLOEXEC);
    306     }
     280    tty_stream = xfopen(CURRENT_TTY, "r");
    307281    fputs(" ?...", stderr);
    308282    fflush(stderr);
     
    310284    while (c != EOF && c != '\n')
    311285        c = getc(tty_stream);
    312     if (savec == 'y' || savec == 'Y')
    313         return 1;
    314     return 0;
    315 }
    316 
    317 # define OPT_INC_P 1
     286    fclose(tty_stream);
     287    return (savec == 'y' || savec == 'Y');
     288}
    318289#else
    319 # define OPT_INC_P 0
    320290# define xargs_ask_confirmation() 1
    321 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION */
    322 
    323 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
    324 # define OPT_INC_X 1
    325 #else
    326 # define OPT_INC_X 0
    327 #endif
    328 
    329 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
    330 static xlist_t *process0_stdin(xlist_t * list_arg, const char *eof_str ATTRIBUTE_UNUSED,
    331                                size_t mc, char *buf)
     291#endif /* FEATURE_XARGS_SUPPORT_CONFIRMATION */
     292
     293#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
     294static xlist_t *process0_stdin(xlist_t *list_arg,
     295        const char *eof_str ATTRIBUTE_UNUSED, size_t mc, char *buf)
    332296{
    333297    int c;                  /* current char */
     
    338302    xlist_t *prev;
    339303
    340     for (prev = cur = list_arg; cur; cur = cur->link) {
    341         line_l += cur->lenght;  /* previous allocated */
    342         if (prev != cur)
    343             prev = prev->link;
     304    prev = cur = list_arg;
     305    while (1) {
     306        if (!cur) break;
     307        prev = cur;
     308        line_l += cur->length;
     309        cur = cur->link;
    344310    }
    345311
     
    347313        c = getchar();
    348314        if (c == EOF) {
    349             eof_stdin_detected++;
     315            eof_stdin_detected = 1;
    350316            if (s == NULL)
    351317                break;
    352             c = 0;
     318            c = '\0';
    353319        }
    354320        if (s == NULL)
     
    357323            bb_error_msg_and_die("argument line too long");
    358324        *p++ = c;
    359         if (c == 0) {   /* word's delimiter or EOF detected */
     325        if (c == '\0') {   /* word's delimiter or EOF detected */
    360326            /* word loaded */
    361             size_t lenght = (p - buf);
    362 
    363             cur = xmalloc(sizeof(xlist_t) + lenght);
    364             cur->data = memcpy(cur + 1, s, lenght);
    365             cur->lenght = lenght;
     327            size_t length = (p - buf);
     328            /* Dont xzalloc - it can be quite big */
     329            cur = xmalloc(offsetof(xlist_t, xstr) + length);
    366330            cur->link = NULL;
     331            cur->length = length;
     332            memcpy(cur->xstr, s, length);
    367333            if (prev == NULL) {
    368334                list_arg = cur;
     
    371337            }
    372338            prev = cur;
    373             line_l += lenght;
     339            line_l += length;
    374340            if (line_l > mc) {
    375341                /* stop memory usage :-) */
     
    381347    return list_arg;
    382348}
    383 
    384 # define READ_ARGS(l, e, nmc, mc) (*read_args)(l, e, nmc, mc)
    385 # define OPT_INC_0 1    /* future use */
    386 #else
    387 # define OPT_INC_0 0    /* future use */
    388 # define READ_ARGS(l, e, nmc, mc) process_stdin(l, e, nmc, mc)
    389 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM */
    390 
    391 
    392 #define OPT_VERBOSE     (1<<0)
    393 #define OPT_NO_EMPTY    (1<<1)
    394 #define OPT_UPTO_NUMBER (1<<2)
    395 #define OPT_UPTO_SIZE   (1<<3)
    396 #define OPT_EOF_STRING  (1<<4)
    397 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
    398 #define OPT_INTERACTIVE (1<<5)
    399 #else
    400 #define OPT_INTERACTIVE (0)     /* require for algorithm &| */
    401 #endif
    402 #define OPT_TERMINATE   (1<<(5+OPT_INC_P))
    403 #define OPT_ZEROTERM    (1<<(5+OPT_INC_P+OPT_INC_X))
    404 /* next future
    405 #define OPT_NEXT_OTHER  (1<<(5+OPT_INC_P+OPT_INC_X+OPT_INC_0))
    406 */
    407 
     349#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
     350
     351/* Correct regardless of combination of CONFIG_xxx */
     352enum {
     353    OPTBIT_VERBOSE = 0,
     354    OPTBIT_NO_EMPTY,
     355    OPTBIT_UPTO_NUMBER,
     356    OPTBIT_UPTO_SIZE,
     357    OPTBIT_EOF_STRING,
     358    USE_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
     359    USE_FEATURE_XARGS_SUPPORT_TERMOPT(     OPTBIT_TERMINATE  ,)
     360    USE_FEATURE_XARGS_SUPPORT_ZERO_TERM(   OPTBIT_ZEROTERM   ,)
     361
     362    OPT_VERBOSE     = 1<<OPTBIT_VERBOSE    ,
     363    OPT_NO_EMPTY    = 1<<OPTBIT_NO_EMPTY   ,
     364    OPT_UPTO_NUMBER = 1<<OPTBIT_UPTO_NUMBER,
     365    OPT_UPTO_SIZE   = 1<<OPTBIT_UPTO_SIZE  ,
     366    OPT_EOF_STRING  = 1<<OPTBIT_EOF_STRING ,
     367    OPT_INTERACTIVE = USE_FEATURE_XARGS_SUPPORT_CONFIRMATION((1<<OPTBIT_INTERACTIVE)) + 0,
     368    OPT_TERMINATE   = USE_FEATURE_XARGS_SUPPORT_TERMOPT(     (1<<OPTBIT_TERMINATE  )) + 0,
     369    OPT_ZEROTERM    = USE_FEATURE_XARGS_SUPPORT_ZERO_TERM(   (1<<OPTBIT_ZEROTERM   )) + 0,
     370};
     371#define OPTION_STR "+trn:s:e::" \
     372    USE_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
     373    USE_FEATURE_XARGS_SUPPORT_TERMOPT(     "x") \
     374    USE_FEATURE_XARGS_SUPPORT_ZERO_TERM(   "0")
     375
     376int xargs_main(int argc, char **argv);
    408377int xargs_main(int argc, char **argv)
    409378{
    410379    char **args;
    411     int i, a, n;
     380    int i, n;
    412381    xlist_t *list = NULL;
    413382    xlist_t *cur;
     
    418387    long orig_arg_max;
    419388    const char *eof_str = "_";
    420     unsigned long opt;
     389    unsigned opt;
    421390    size_t n_max_chars;
    422 
    423 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
    424     xlist_t *(*read_args) (xlist_t *, const char *, size_t, char *) = process_stdin;
     391#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
     392    xlist_t* (*read_args)(xlist_t*, const char*, size_t, char*) = process_stdin;
     393#else
     394#define read_args process_stdin
    425395#endif
    426396
    427 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
    428     bb_opt_complementally = "pt";
    429 #endif
    430 
    431     opt = bb_getopt_ulflags(argc, argv, "+trn:s:e::"
    432 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION
    433     "p"
    434 #endif
    435 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
    436     "x"
    437 #endif
    438 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
    439     "0"
    440 #endif
    441     ,&max_args, &max_chars, &eof_str);
    442 
    443     a = argc - optind;
     397    opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &eof_str);
     398
     399    if (opt & OPT_ZEROTERM)
     400        USE_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin);
     401
    444402    argv += optind;
    445     if (a == 0) {
     403    argc -= optind;
     404    if (!argc) {
    446405        /* default behavior is to echo all the filenames */
    447         *argv = "echo";
    448         a++;
     406        *argv = (char*)"echo";
     407        argc++;
    449408    }
    450409
     
    452411    if (orig_arg_max == -1)
    453412        orig_arg_max = LONG_MAX;
    454     orig_arg_max -= 2048;   /* POSIX.2 requires subtracting 2048.  */
    455     if ((opt & OPT_UPTO_SIZE)) {
    456         n_max_chars = bb_xgetularg10_bnd(max_chars, 1, orig_arg_max);
    457         for (i = 0; i < a; i++) {
     413    orig_arg_max -= 2048;   /* POSIX.2 requires subtracting 2048 */
     414
     415    if (opt & OPT_UPTO_SIZE) {
     416        n_max_chars = xatoul_range(max_chars, 1, orig_arg_max);
     417        for (i = 0; i < argc; i++) {
    458418            n_chars += strlen(*argv) + 1;
    459419        }
    460420        if (n_max_chars < n_chars) {
    461             bb_error_msg_and_die("can not fit single argument within argument list size limit");
     421            bb_error_msg_and_die("cannot fit single argument within argument list size limit");
    462422        }
    463423        n_max_chars -= n_chars;
     
    473433    max_chars = xmalloc(n_max_chars);
    474434
    475     if ((opt & OPT_UPTO_NUMBER)) {
    476         n_max_arg = bb_xgetularg10_bnd(max_args, 1, INT_MAX);
     435    if (opt & OPT_UPTO_NUMBER) {
     436        n_max_arg = xatoul_range(max_args, 1, INT_MAX);
    477437    } else {
    478438        n_max_arg = n_max_chars;
    479439    }
    480440
    481 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM
    482     if (opt & OPT_ZEROTERM)
    483         read_args = process0_stdin;
    484 #endif
    485 
    486     while ((list = READ_ARGS(list, eof_str, n_max_chars, max_chars)) != NULL ||
    487         (opt & OPT_NO_EMPTY) == 0)
     441    while ((list = read_args(list, eof_str, n_max_chars, max_chars)) != NULL ||
     442        !(opt & OPT_NO_EMPTY))
    488443    {
    489444        opt |= OPT_NO_EMPTY;
    490445        n = 0;
    491446        n_chars = 0;
    492 #ifdef CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT
     447#if ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT
    493448        for (cur = list; cur;) {
    494             n_chars += cur->lenght;
     449            n_chars += cur->length;
    495450            n++;
    496451            cur = cur->link;
     
    503458#else
    504459        for (cur = list; cur; cur = cur->link) {
    505             n_chars += cur->lenght;
     460            n_chars += cur->length;
    506461            n++;
    507462            if (n_chars > n_max_chars || n == n_max_arg) {
     
    509464            }
    510465        }
    511 #endif /* CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT */
    512 
    513         /* allocating pointers for execvp:
    514            a*arg, n*arg from stdin, NULL */
    515         args = xcalloc(n + a + 1, sizeof(char *));
    516 
    517         /* Store the command to be executed
     466#endif /* FEATURE_XARGS_SUPPORT_TERMOPT */
     467
     468        /* allocate pointers for execvp:
     469           argc*arg, n*arg from stdin, NULL */
     470        args = xzalloc((n + argc + 1) * sizeof(char *));
     471
     472        /* store the command to be executed
    518473           (taken from the command line) */
    519         for (i = 0; i < a; i++)
     474        for (i = 0; i < argc; i++)
    520475            args[i] = argv[i];
    521476        /* (taken from stdin) */
    522477        for (cur = list; n; cur = cur->link) {
    523             args[i++] = cur->data;
     478            args[i++] = cur->xstr;
    524479            n--;
    525480        }
    526481
    527         if ((opt & (OPT_INTERACTIVE | OPT_VERBOSE))) {
     482        if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
    528483            for (i = 0; args[i]; i++) {
    529484                if (i)
     
    531486                fputs(args[i], stderr);
    532487            }
    533             if ((opt & OPT_INTERACTIVE) == 0)
     488            if (!(opt & OPT_INTERACTIVE))
    534489                fputc('\n', stderr);
    535490        }
    536         if ((opt & OPT_INTERACTIVE) == 0 || xargs_ask_confirmation() != 0) {
     491        if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
    537492            child_error = xargs_exec(args);
    538493        }
    539494
    540495        /* clean up */
    541         for (i = a; args[i]; i++) {
     496        for (i = argc; args[i]; i++) {
    542497            cur = list;
    543498            list = list->link;
     
    549504        }
    550505    }
    551 #ifdef CONFIG_FEATURE_CLEAN_UP
    552     free(max_chars);
    553 #endif
     506    if (ENABLE_FEATURE_CLEAN_UP)
     507        free(max_chars);
    554508    return child_error;
    555509}
     
    558512#ifdef TEST
    559513
    560 const char *bb_applet_name = "debug stuff usage";
     514const char *applet_name = "debug stuff usage";
    561515
    562516void bb_show_usage(void)
    563517{
    564518    fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
    565         bb_applet_name);
     519        applet_name);
    566520    exit(1);
    567521}
Note: See TracChangeset for help on using the changeset viewer.