Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/findutils


Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
Location:
branches/2.2.9/mindi-busybox/findutils
Files:
2 added
5 edited

Legend:

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

    r1765 r2725  
     1# DO NOT EDIT. This file is generated from Config.src
    12#
    23# For a description of the syntax of this configuration file,
     
    89config FIND
    910    bool "find"
    10     default n
     11    default y
    1112    help
    1213      find is used to search your system to find specified files.
    1314
    1415config FEATURE_FIND_PRINT0
    15     bool "Enable -print0 option"
    16     default y
    17     depends on FIND
    18     help
    19       Causes output names to be separated by a null character
    20       rather than a newline.  This allows names that contain
     16    bool "Enable -print0: NUL-terminated output"
     17    default y
     18    depends on FIND
     19    help
     20      Causes output names to be separated by a NUL character
     21      rather than a newline. This allows names that contain
    2122      newlines and other whitespace to be more easily
    2223      interpreted by other programs.
    2324
    2425config FEATURE_FIND_MTIME
    25     bool "Enable modified time matching (-mtime) option"
     26    bool "Enable -mtime: modified time matching"
    2627    default y
    2728    depends on FIND
     
    3132
    3233config FEATURE_FIND_MMIN
    33     bool "Enable modified time matching (-mmin) option"
     34    bool "Enable -mmin: modified time matching by minutes"
    3435    default y
    3536    depends on FIND
     
    3940
    4041config FEATURE_FIND_PERM
    41     bool "Enable permissions matching (-perm) option"
     42    bool "Enable -perm: permissions matching"
    4243    default y
    4344    depends on FIND
     
    4647
    4748config FEATURE_FIND_TYPE
    48     bool "Enable filetype matching (-type) option"
     49    bool "Enable -type: file type matching (file/dir/link/...)"
    4950    default y
    5051    depends on FIND
     
    5455
    5556config FEATURE_FIND_XDEV
    56     bool "Enable stay in filesystem (-xdev) option"
     57    bool "Enable -xdev: 'stay in filesystem'"
    5758    default y
    5859    depends on FIND
     
    6162
    6263config FEATURE_FIND_MAXDEPTH
    63     bool "Enable -maxdepth N option"
     64    bool "Enable -maxdepth N"
    6465    default y
    6566    depends on FIND
     
    6869
    6970config FEATURE_FIND_NEWER
    70     bool "Enable -newer option for comparing file mtimes"
     71    bool "Enable -newer: compare file modification times"
    7172    default y
    7273    depends on FIND
     
    7677
    7778config FEATURE_FIND_INUM
    78     bool "Enable inode number matching (-inum) option"
     79    bool "Enable -inum: inode number matching"
    7980    default y
    8081    depends on FIND
     
    8384
    8485config FEATURE_FIND_EXEC
    85     bool "Enable (-exec) option allowing execution of commands"
     86    bool "Enable -exec: execute commands"
    8687    default y
    8788    depends on FIND
     
    9192
    9293config FEATURE_FIND_USER
    93     bool "Enable username/uid matching (-user) option"
     94    bool "Enable -user: username/uid matching"
    9495    default y
    9596    depends on FIND
     
    9899
    99100config FEATURE_FIND_GROUP
    100     bool "Enable group/gid matching (-group) option"
     101    bool "Enable -group: group/gid matching"
    101102    default y
    102103    depends on FIND
     
    114115
    115116config FEATURE_FIND_DEPTH
    116     bool "Enable the -depth option"
     117    bool "Enable -depth"
    117118    default y
    118119    depends on FIND
     
    128129
    129130config FEATURE_FIND_SIZE
    130     bool "Enable (-size) option allowing matching for file size"
     131    bool "Enable -size: file size matching"
    131132    default y
    132133    depends on FIND
     
    135136
    136137config FEATURE_FIND_PRUNE
    137     bool "Enable (-prune) option allowing to exclude subdirectories"
     138    bool "Enable -prune: exclude subdirectories"
    138139    default y
    139140    depends on FIND
     
    143144
    144145config FEATURE_FIND_DELETE
    145     bool "Enable -delete option allowing to delete files"
    146     default n
     146    bool "Enable -delete: delete files/dirs"
     147    default y
    147148    depends on FIND && FEATURE_FIND_DEPTH
    148149    help
    149       Support the 'find -delete' option for deleting files and direcotries.
     150      Support the 'find -delete' option for deleting files and directories.
    150151      WARNING: This option can do much harm if used wrong. Busybox will not
    151152      try to protect the user from doing stupid things. Use with care.
    152153
    153154config FEATURE_FIND_PATH
    154     bool "Enable -path option allowing to match pathname patterns"
     155    bool "Enable -path: match pathname with shell pattern"
    155156    default y
    156157    depends on FIND
     
    159160
    160161config FEATURE_FIND_REGEX
    161     bool "Enable -regex: match pathname to regex"
     162    bool "Enable -regex: match pathname with regex"
    162163    default y
    163164    depends on FIND
     
    166167
    167168config FEATURE_FIND_CONTEXT
    168     bool "Enable (-context) option for matching security context"
     169    bool "Enable -context: security context matching"
    169170    default n
    170171    depends on FIND && SELINUX
     
    172173      Support the 'find -context' option for matching security context.
    173174
     175config FEATURE_FIND_LINKS
     176    bool "Enable -links: link count matching"
     177    default y
     178    depends on FIND
     179    help
     180      Support the 'find -links' option for matching number of links.
    174181config GREP
    175182    bool "grep"
    176     default n
     183    default y
    177184    help
    178185      grep is used to search files for a specified pattern.
    179186
    180187config FEATURE_GREP_EGREP_ALIAS
    181     bool "Support extended regular expressions (egrep & grep -E)"
     188    bool "Enable extended regular expressions (egrep & grep -E)"
    182189    default y
    183190    depends on GREP
    184191    help
    185       Enabled support for extended regular expressions.  Extended
     192      Enabled support for extended regular expressions. Extended
    186193      regular expressions allow for alternation (foo|bar), grouping,
    187194      and various repetition operators.
     
    194201      fgrep sees the search pattern as a normal string rather than
    195202      regular expressions.
    196       grep -F is always builtin, this just creates the fgrep alias.
     203      grep -F always works, this just creates the fgrep alias.
    197204
    198205config FEATURE_GREP_CONTEXT
     
    204211      context surrounding our matching lines.
    205212      Print the specified number of context lines (-C).
    206 
    207213config XARGS
    208214    bool "xargs"
    209     default n
    210     help
    211       xargs is used to execute a specified command on
     215    default y
     216    help
     217      xargs is used to execute a specified command for
    212218      every item from standard input.
    213219
    214220config FEATURE_XARGS_SUPPORT_CONFIRMATION
    215     bool "Enable prompt and confirmation option -p"
    216     default n
    217     depends on XARGS
    218     help
    219       Support prompt the user about whether to run each command
     221    bool "Enable -p: prompt and confirmation"
     222    default y
     223    depends on XARGS
     224    help
     225      Support -p: prompt the user whether to run each command
    220226      line and read a line from the terminal.
    221227
    222228config FEATURE_XARGS_SUPPORT_QUOTES
    223     bool "Enable support single and double quotes and backslash"
    224     default n
    225     depends on XARGS
    226     help
    227       Default xargs unsupport single and double quotes
    228       and backslash for can use aruments with spaces.
     229    bool "Enable single and double quotes and backslash"
     230    default y
     231    depends on XARGS
     232    help
     233      Support quoting in the input.
    229234
    230235config FEATURE_XARGS_SUPPORT_TERMOPT
    231     bool "Enable support options -x"
    232     default n
    233     depends on XARGS
    234     help
    235       Enable support exit if the size (see the -s or -n option)
     236    bool "Enable -x: exit if -s or -n is exceeded"
     237    default y
     238    depends on XARGS
     239    help
     240      Support -x: exit if the command size (see the -s or -n option)
    236241      is exceeded.
    237242
    238243config FEATURE_XARGS_SUPPORT_ZERO_TERM
    239     bool "Enable null terminated option -0"
    240     default n
    241     depends on XARGS
    242     help
    243       Enable input filenames are terminated by a null character
    244       instead of by whitespace, and the quotes and backslash
     244    bool "Enable -0: NUL-terminated input"
     245    default y
     246    depends on XARGS
     247    help
     248      Support -0: input items are terminated by a NUL character
     249      instead of whitespace, and the quotes and backslash
    245250      are not special.
    246251
  • branches/2.2.9/mindi-busybox/findutils/Kbuild

    r1765 r2725  
     1# DO NOT EDIT. This file is generated from Kbuild.src
    12# Makefile for busybox
    23#
    34# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
    45#
    5 # Licensed under the GPL v2, see the file LICENSE in this tarball.
     6# Licensed under GPLv2, see file LICENSE in this source tree.
    67
    78lib-y:=
    8 lib-$(CONFIG_FIND)  += find.o
    9 lib-$(CONFIG_GREP)  += grep.o
    10 lib-$(CONFIG_XARGS) += xargs.o
     9
     10lib-$(CONFIG_FIND) += find.o
     11lib-$(CONFIG_GREP) += grep.o
     12lib-$(CONFIG_XARGS) += xargs.o
  • branches/2.2.9/mindi-busybox/findutils/find.c

    r1765 r2725  
    88 *  Matt Kraai <kraai@alumni.carnegiemellon.edu>.
    99 *
    10  * Licensed under the GPL version 2, see the file LICENSE in this tarball.
     10 * Licensed under GPLv2, see file LICENSE in this source tree.
    1111 */
    1212
     
    5454 */
    5555
     56//applet:IF_FIND(APPLET_NOEXEC(find, find, _BB_DIR_USR_BIN, _BB_SUID_DROP, find))
     57
     58//kbuild:lib-$(CONFIG_FIND) += find.o
     59
     60//config:config FIND
     61//config:   bool "find"
     62//config:   default y
     63//config:   help
     64//config:     find is used to search your system to find specified files.
     65//config:
     66//config:config FEATURE_FIND_PRINT0
     67//config:   bool "Enable -print0: NUL-terminated output"
     68//config:   default y
     69//config:   depends on FIND
     70//config:   help
     71//config:     Causes output names to be separated by a NUL character
     72//config:     rather than a newline. This allows names that contain
     73//config:     newlines and other whitespace to be more easily
     74//config:     interpreted by other programs.
     75//config:
     76//config:config FEATURE_FIND_MTIME
     77//config:   bool "Enable -mtime: modified time matching"
     78//config:   default y
     79//config:   depends on FIND
     80//config:   help
     81//config:     Allow searching based on the modification time of
     82//config:     files, in days.
     83//config:
     84//config:config FEATURE_FIND_MMIN
     85//config:   bool "Enable -mmin: modified time matching by minutes"
     86//config:   default y
     87//config:   depends on FIND
     88//config:   help
     89//config:     Allow searching based on the modification time of
     90//config:     files, in minutes.
     91//config:
     92//config:config FEATURE_FIND_PERM
     93//config:   bool "Enable -perm: permissions matching"
     94//config:   default y
     95//config:   depends on FIND
     96//config:   help
     97//config:     Enable searching based on file permissions.
     98//config:
     99//config:config FEATURE_FIND_TYPE
     100//config:   bool "Enable -type: file type matching (file/dir/link/...)"
     101//config:   default y
     102//config:   depends on FIND
     103//config:   help
     104//config:     Enable searching based on file type (file,
     105//config:     directory, socket, device, etc.).
     106//config:
     107//config:config FEATURE_FIND_XDEV
     108//config:   bool "Enable -xdev: 'stay in filesystem'"
     109//config:   default y
     110//config:   depends on FIND
     111//config:   help
     112//config:     This option allows find to restrict searches to a single filesystem.
     113//config:
     114//config:config FEATURE_FIND_MAXDEPTH
     115//config:   bool "Enable -maxdepth N"
     116//config:   default y
     117//config:   depends on FIND
     118//config:   help
     119//config:     This option enables -maxdepth N option.
     120//config:
     121//config:config FEATURE_FIND_NEWER
     122//config:   bool "Enable -newer: compare file modification times"
     123//config:   default y
     124//config:   depends on FIND
     125//config:   help
     126//config:     Support the 'find -newer' option for finding any files which have
     127//config:     a modified time that is more recent than the specified FILE.
     128//config:
     129//config:config FEATURE_FIND_INUM
     130//config:   bool "Enable -inum: inode number matching"
     131//config:   default y
     132//config:   depends on FIND
     133//config:   help
     134//config:     Support the 'find -inum' option for searching by inode number.
     135//config:
     136//config:config FEATURE_FIND_EXEC
     137//config:   bool "Enable -exec: execute commands"
     138//config:   default y
     139//config:   depends on FIND
     140//config:   help
     141//config:     Support the 'find -exec' option for executing commands based upon
     142//config:     the files matched.
     143//config:
     144//config:config FEATURE_FIND_USER
     145//config:   bool "Enable -user: username/uid matching"
     146//config:   default y
     147//config:   depends on FIND
     148//config:   help
     149//config:     Support the 'find -user' option for searching by username or uid.
     150//config:
     151//config:config FEATURE_FIND_GROUP
     152//config:   bool "Enable -group: group/gid matching"
     153//config:   default y
     154//config:   depends on FIND
     155//config:   help
     156//config:     Support the 'find -group' option for searching by group name or gid.
     157//config:
     158//config:config FEATURE_FIND_NOT
     159//config:   bool "Enable the 'not' (!) operator"
     160//config:   default y
     161//config:   depends on FIND
     162//config:   help
     163//config:     Support the '!' operator to invert the test results.
     164//config:     If 'Enable full-blown desktop' is enabled, then will also support
     165//config:     the non-POSIX notation '-not'.
     166//config:
     167//config:config FEATURE_FIND_DEPTH
     168//config:   bool "Enable -depth"
     169//config:   default y
     170//config:   depends on FIND
     171//config:   help
     172//config:     Process each directory's contents before the directory itself.
     173//config:
     174//config:config FEATURE_FIND_PAREN
     175//config:   bool "Enable parens in options"
     176//config:   default y
     177//config:   depends on FIND
     178//config:   help
     179//config:     Enable usage of parens '(' to specify logical order of arguments.
     180//config:
     181//config:config FEATURE_FIND_SIZE
     182//config:   bool "Enable -size: file size matching"
     183//config:   default y
     184//config:   depends on FIND
     185//config:   help
     186//config:     Support the 'find -size' option for searching by file size.
     187//config:
     188//config:config FEATURE_FIND_PRUNE
     189//config:   bool "Enable -prune: exclude subdirectories"
     190//config:   default y
     191//config:   depends on FIND
     192//config:   help
     193//config:     If the file is a directory, dont descend into it. Useful for
     194//config:     exclusion .svn and CVS directories.
     195//config:
     196//config:config FEATURE_FIND_DELETE
     197//config:   bool "Enable -delete: delete files/dirs"
     198//config:   default y
     199//config:   depends on FIND && FEATURE_FIND_DEPTH
     200//config:   help
     201//config:     Support the 'find -delete' option for deleting files and directories.
     202//config:     WARNING: This option can do much harm if used wrong. Busybox will not
     203//config:     try to protect the user from doing stupid things. Use with care.
     204//config:
     205//config:config FEATURE_FIND_PATH
     206//config:   bool "Enable -path: match pathname with shell pattern"
     207//config:   default y
     208//config:   depends on FIND
     209//config:   help
     210//config:     The -path option matches whole pathname instead of just filename.
     211//config:
     212//config:config FEATURE_FIND_REGEX
     213//config:   bool "Enable -regex: match pathname with regex"
     214//config:   default y
     215//config:   depends on FIND
     216//config:   help
     217//config:     The -regex option matches whole pathname against regular expression.
     218//config:
     219//config:config FEATURE_FIND_CONTEXT
     220//config:   bool "Enable -context: security context matching"
     221//config:   default n
     222//config:   depends on FIND && SELINUX
     223//config:   help
     224//config:     Support the 'find -context' option for matching security context.
     225//config:
     226//config:config FEATURE_FIND_LINKS
     227//config:   bool "Enable -links: link count matching"
     228//config:   default y
     229//config:   depends on FIND
     230//config:   help
     231//config:     Support the 'find -links' option for matching number of links.
     232
    56233#include <fnmatch.h>
    57234#include "libbb.h"
     
    63240
    64241
    65 USE_FEATURE_FIND_XDEV(static dev_t *xdev_dev;)
    66 USE_FEATURE_FIND_XDEV(static int xdev_count;)
    67 
    68 typedef int (*action_fp)(const char *fileName, struct stat *statbuf, void *);
     242typedef int (*action_fp)(const char *fileName, const struct stat *statbuf, void *) FAST_FUNC;
    69243
    70244typedef struct {
     
    74248#endif
    75249} 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;)
    80 USE_FEATURE_FIND_PATH(   ACTS(path,  const char *pattern;))
    81 USE_FEATURE_FIND_REGEX(  ACTS(regex, regex_t compiled_pattern;))
    82 USE_FEATURE_FIND_PRINT0( ACTS(print0))
    83 USE_FEATURE_FIND_TYPE(   ACTS(type,  int type_mask;))
    84 USE_FEATURE_FIND_PERM(   ACTS(perm,  char perm_char; mode_t perm_mask;))
    85 USE_FEATURE_FIND_MTIME(  ACTS(mtime, char mtime_char; unsigned mtime_days;))
    86 USE_FEATURE_FIND_MMIN(   ACTS(mmin,  char mmin_char; unsigned mmin_mins;))
    87 USE_FEATURE_FIND_NEWER(  ACTS(newer, time_t newer_mtime;))
    88 USE_FEATURE_FIND_INUM(   ACTS(inum,  ino_t inode_num;))
    89 USE_FEATURE_FIND_USER(   ACTS(user,  uid_t uid;))
    90 USE_FEATURE_FIND_SIZE(   ACTS(size,  char size_char; off_t size;))
    91 USE_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
    92 USE_FEATURE_FIND_PAREN(  ACTS(paren, action ***subexpr;))
    93 USE_FEATURE_FIND_PRUNE(  ACTS(prune))
    94 USE_FEATURE_FIND_DELETE( ACTS(delete))
    95 USE_FEATURE_FIND_EXEC(   ACTS(exec,  char **exec_argv; unsigned *subst_count; int exec_argc;))
    96 USE_FEATURE_FIND_GROUP(  ACTS(group, gid_t gid;))
    97 
    98 static action ***actions;
    99 static bool need_print = 1;
    100 static int recurse_flags = ACTION_RECURSE;
     250
     251#define ACTS(name, ...) typedef struct { action a; __VA_ARGS__ } action_##name;
     252#define ACTF(name) \
     253    static int FAST_FUNC func_##name(const char *fileName UNUSED_PARAM, \
     254        const struct stat *statbuf UNUSED_PARAM, \
     255        action_##name* ap UNUSED_PARAM)
     256
     257                        ACTS(print)
     258                        ACTS(name,  const char *pattern; bool iname;)
     259IF_FEATURE_FIND_PATH(   ACTS(path,  const char *pattern;))
     260IF_FEATURE_FIND_REGEX(  ACTS(regex, regex_t compiled_pattern;))
     261IF_FEATURE_FIND_PRINT0( ACTS(print0))
     262IF_FEATURE_FIND_TYPE(   ACTS(type,  int type_mask;))
     263IF_FEATURE_FIND_PERM(   ACTS(perm,  char perm_char; mode_t perm_mask;))
     264IF_FEATURE_FIND_MTIME(  ACTS(mtime, char mtime_char; unsigned mtime_days;))
     265IF_FEATURE_FIND_MMIN(   ACTS(mmin,  char mmin_char; unsigned mmin_mins;))
     266IF_FEATURE_FIND_NEWER(  ACTS(newer, time_t newer_mtime;))
     267IF_FEATURE_FIND_INUM(   ACTS(inum,  ino_t inode_num;))
     268IF_FEATURE_FIND_USER(   ACTS(user,  uid_t uid;))
     269IF_FEATURE_FIND_SIZE(   ACTS(size,  char size_char; off_t size;))
     270IF_FEATURE_FIND_CONTEXT(ACTS(context, security_context_t context;))
     271IF_FEATURE_FIND_PAREN(  ACTS(paren, action ***subexpr;))
     272IF_FEATURE_FIND_PRUNE(  ACTS(prune))
     273IF_FEATURE_FIND_DELETE( ACTS(delete))
     274IF_FEATURE_FIND_EXEC(   ACTS(exec,  char **exec_argv; unsigned *subst_count; int exec_argc;))
     275IF_FEATURE_FIND_GROUP(  ACTS(group, gid_t gid;))
     276IF_FEATURE_FIND_LINKS(  ACTS(links, char links_char; int links_count;))
     277
     278struct globals {
     279    IF_FEATURE_FIND_XDEV(dev_t *xdev_dev;)
     280    IF_FEATURE_FIND_XDEV(int xdev_count;)
     281    action ***actions;
     282    bool need_print;
     283    recurse_flags_t recurse_flags;
     284} FIX_ALIASING;
     285#define G (*(struct globals*)&bb_common_bufsiz1)
     286#define INIT_G() do { \
     287    struct G_sizecheck { \
     288        char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
     289    }; \
     290    /* we have to zero it out because of NOEXEC */ \
     291    memset(&G, 0, offsetof(struct globals, need_print)); \
     292    G.need_print = 1; \
     293    G.recurse_flags = ACTION_RECURSE; \
     294} while (0)
    101295
    102296#if ENABLE_FEATURE_FIND_EXEC
     
    135329 */
    136330
    137 static int exec_actions(action ***appp, const char *fileName, struct stat *statbuf)
     331static int exec_actions(action ***appp, const char *fileName, const struct stat *statbuf)
    138332{
    139333    int cur_group;
     
    160354
    161355    cur_group = -1;
    162     while ((app = appp[++cur_group])) {
     356    while ((app = appp[++cur_group]) != NULL) {
    163357        rc &= ~TRUE; /* 'success' so far, clear TRUE bit */
    164358        cur_action = -1;
     
    182376{
    183377    const char *tmp = bb_basename(fileName);
    184     if (tmp != fileName && !*tmp) { /* "foo/bar/". Oh no... go back to 'b' */
     378    if (tmp != fileName && *tmp == '\0') {
     379        /* "foo/bar/". Oh no... go back to 'b' */
    185380        tmp--;
    186381        while (tmp != fileName && *--tmp != '/')
     
    189384            tmp++;
    190385    }
    191     return fnmatch(ap->pattern, tmp, FNM_PERIOD) == 0;
    192 }
     386    /* Was using FNM_PERIOD flag too,
     387     * but somewhere between 4.1.20 and 4.4.0 GNU find stopped using it.
     388     * find -name '*foo' should match .foo too:
     389     */
     390    return fnmatch(ap->pattern, tmp, (ap->iname ? FNM_CASEFOLD : 0)) == 0;
     391}
     392
    193393#if ENABLE_FEATURE_FIND_PATH
    194394ACTF(path)
     
    271471{
    272472    int i, rc;
     473#if ENABLE_USE_PORTABLE_CODE
     474    char **argv = alloca(sizeof(char*) * (ap->exec_argc + 1));
     475#else /* gcc 4.3.1 generates smaller code: */
    273476    char *argv[ap->exec_argc + 1];
     477#endif
    274478    for (i = 0; i < ap->exec_argc; i++)
    275479        argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName);
     
    278482    rc = spawn_and_wait(argv);
    279483    if (rc < 0)
    280         bb_perror_msg("%s", argv[0]);
     484        bb_simple_perror_msg(argv[0]);
    281485
    282486    i = 0;
     
    348552    }
    349553    if (rc < 0)
    350         bb_perror_msg("%s", fileName);
     554        bb_simple_perror_msg(fileName);
    351555    return TRUE;
    352556}
     
    358562    int rc;
    359563
    360     if (recurse_flags & ACTION_FOLLOWLINKS) {
     564    if (G.recurse_flags & ACTION_FOLLOWLINKS) {
    361565        rc = getfilecon(fileName, &con);
    362566    } else {
     
    370574}
    371575#endif
    372 
    373 
    374 static int fileAction(const char *fileName, struct stat *statbuf, void *userData, int depth)
    375 {
    376     int i;
     576#if ENABLE_FEATURE_FIND_LINKS
     577ACTF(links)
     578{
     579    switch(ap->links_char) {
     580    case '-' : return (statbuf->st_nlink <  ap->links_count);
     581    case '+' : return (statbuf->st_nlink >  ap->links_count);
     582    default:   return (statbuf->st_nlink == ap->links_count);
     583    }
     584}
     585#endif
     586
     587static int FAST_FUNC fileAction(const char *fileName,
     588        struct stat *statbuf,
     589        void *userData IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM),
     590        int depth IF_NOT_FEATURE_FIND_MAXDEPTH(UNUSED_PARAM))
     591{
     592    int r;
    377593#if ENABLE_FEATURE_FIND_MAXDEPTH
    378     int maxdepth = (int)(ptrdiff_t)userData;
    379 
    380     if (depth > maxdepth) return SKIP;
    381 #endif
    382 
     594#define minmaxdepth ((int*)userData)
     595
     596    if (depth < minmaxdepth[0])
     597        return TRUE; /* skip this, continue recursing */
     598    if (depth > minmaxdepth[1])
     599        return SKIP; /* stop recursing */
     600#endif
     601
     602    r = exec_actions(G.actions, fileName, statbuf);
     603    /* Had no explicit -print[0] or -exec? then print */
     604    if ((r & TRUE) && G.need_print)
     605        puts(fileName);
     606
     607#if ENABLE_FEATURE_FIND_MAXDEPTH
     608    if (S_ISDIR(statbuf->st_mode)) {
     609        if (depth == minmaxdepth[1])
     610            return SKIP;
     611    }
     612#endif
    383613#if ENABLE_FEATURE_FIND_XDEV
    384     if (S_ISDIR(statbuf->st_mode) && xdev_count) {
    385         for (i = 0; i < xdev_count; i++) {
    386             if (xdev_dev[i] == statbuf->st_dev)
    387                 break;
    388         }
    389         if (i == xdev_count)
     614    /* -xdev stops on mountpoints, but AFTER mountpoit itself
     615     * is processed as usual */
     616    if (S_ISDIR(statbuf->st_mode)) {
     617        if (G.xdev_count) {
     618            int i;
     619            for (i = 0; i < G.xdev_count; i++) {
     620                if (G.xdev_dev[i] == statbuf->st_dev)
     621                    goto found;
     622            }
    390623            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);
     624 found: ;
     625        }
     626    }
     627#endif
     628
    397629    /* Cannot return 0: our caller, recursive_action(),
    398630     * will perror() and skip dirs (if called on dir) */
    399     return (i & SKIP) ? SKIP : TRUE;
     631    return (r & SKIP) ? SKIP : TRUE;
     632#undef minmaxdepth
    400633}
    401634
     
    421654        mask = S_IFSOCK;
    422655
    423     if (mask == 0 || *(type + 1) != '\0')
     656    if (mask == 0 || type[1] != '\0')
    424657        bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type");
    425658
     
    430663#if ENABLE_FEATURE_FIND_PERM \
    431664 || ENABLE_FEATURE_FIND_MTIME || ENABLE_FEATURE_FIND_MMIN \
    432  || ENABLE_FEATURE_FIND_SIZE
     665 || ENABLE_FEATURE_FIND_SIZE  || ENABLE_FEATURE_FIND_LINKS
    433666static const char* plus_minus_num(const char* str)
    434667{
     
    442675{
    443676    enum {
    444                              PARM_a         ,
    445                              PARM_o         ,
    446     USE_FEATURE_FIND_NOT(    PARM_char_not  ,)
     677                            PARM_a         ,
     678                            PARM_o         ,
     679    IF_FEATURE_FIND_NOT(    PARM_char_not  ,)
    447680#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,)
     681                            PARM_and       ,
     682                            PARM_or        ,
     683    IF_FEATURE_FIND_NOT(    PARM_not       ,)
     684#endif
     685                            PARM_print     ,
     686    IF_FEATURE_FIND_PRINT0( PARM_print0    ,)
     687    IF_FEATURE_FIND_DEPTH(  PARM_depth     ,)
     688    IF_FEATURE_FIND_PRUNE(  PARM_prune     ,)
     689    IF_FEATURE_FIND_DELETE( PARM_delete    ,)
     690    IF_FEATURE_FIND_EXEC(   PARM_exec      ,)
     691    IF_FEATURE_FIND_PAREN(  PARM_char_brace,)
    459692    /* 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   ,)
     693                            PARM_name      ,
     694                            PARM_iname     ,
     695    IF_FEATURE_FIND_PATH(   PARM_path      ,)
     696    IF_FEATURE_FIND_REGEX(  PARM_regex     ,)
     697    IF_FEATURE_FIND_TYPE(   PARM_type      ,)
     698    IF_FEATURE_FIND_PERM(   PARM_perm      ,)
     699    IF_FEATURE_FIND_MTIME(  PARM_mtime     ,)
     700    IF_FEATURE_FIND_MMIN(   PARM_mmin      ,)
     701    IF_FEATURE_FIND_NEWER(  PARM_newer     ,)
     702    IF_FEATURE_FIND_INUM(   PARM_inum      ,)
     703    IF_FEATURE_FIND_USER(   PARM_user      ,)
     704    IF_FEATURE_FIND_GROUP(  PARM_group     ,)
     705    IF_FEATURE_FIND_SIZE(   PARM_size      ,)
     706    IF_FEATURE_FIND_CONTEXT(PARM_context   ,)
     707    IF_FEATURE_FIND_LINKS(  PARM_links     ,)
    473708    };
    474709
     
    476711                             "-a\0"
    477712                             "-o\0"
    478     USE_FEATURE_FIND_NOT(    "!\0"       )
     713    IF_FEATURE_FIND_NOT(    "!\0"       )
    479714#if ENABLE_DESKTOP
    480715                             "-and\0"
    481716                             "-or\0"
    482     USE_FEATURE_FIND_NOT(    "-not\0"    )
     717    IF_FEATURE_FIND_NOT(     "-not\0"    )
    483718#endif
    484719                             "-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"       )
     720    IF_FEATURE_FIND_PRINT0( "-print0\0" )
     721    IF_FEATURE_FIND_DEPTH(  "-depth\0"  )
     722    IF_FEATURE_FIND_PRUNE(  "-prune\0"  )
     723    IF_FEATURE_FIND_DELETE( "-delete\0" )
     724    IF_FEATURE_FIND_EXEC(   "-exec\0"   )
     725    IF_FEATURE_FIND_PAREN(  "(\0"       )
    491726    /* All options starting from here require argument */
    492727                             "-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")
     728                             "-iname\0"
     729    IF_FEATURE_FIND_PATH(   "-path\0"   )
     730    IF_FEATURE_FIND_REGEX(  "-regex\0"  )
     731    IF_FEATURE_FIND_TYPE(   "-type\0"   )
     732    IF_FEATURE_FIND_PERM(   "-perm\0"   )
     733    IF_FEATURE_FIND_MTIME(  "-mtime\0"  )
     734    IF_FEATURE_FIND_MMIN(   "-mmin\0"   )
     735    IF_FEATURE_FIND_NEWER(  "-newer\0"  )
     736    IF_FEATURE_FIND_INUM(   "-inum\0"   )
     737    IF_FEATURE_FIND_USER(   "-user\0"   )
     738    IF_FEATURE_FIND_GROUP(  "-group\0"  )
     739    IF_FEATURE_FIND_SIZE(   "-size\0"   )
     740    IF_FEATURE_FIND_CONTEXT("-context\0")
     741    IF_FEATURE_FIND_LINKS(  "-links\0"  )
    505742                             ;
    506743
     
    508745    unsigned cur_group = 0;
    509746    unsigned cur_action = 0;
    510     USE_FEATURE_FIND_NOT( bool invert_flag = 0; )
    511 
    512     /* 'static' doesn't work here! (gcc 4.1.2) */
     747    IF_FEATURE_FIND_NOT( bool invert_flag = 0; )
     748
     749    /* This is the only place in busybox where we use nested function.
     750     * So far more standard alternatives were bigger. */
     751    /* Auto decl suppresses "func without a prototype" warning: */
     752    auto action* alloc_action(int sizeof_struct, action_fp f);
    513753    action* alloc_action(int sizeof_struct, action_fp f)
    514754    {
    515755        action *ap;
    516756        appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(*appp));
    517         appp[cur_group][cur_action++] = ap = xmalloc(sizeof_struct);
     757        appp[cur_group][cur_action++] = ap = xzalloc(sizeof_struct);
    518758        appp[cur_group][cur_action] = NULL;
    519759        ap->f = f;
    520         USE_FEATURE_FIND_NOT( ap->invert = invert_flag; )
    521         USE_FEATURE_FIND_NOT( invert_flag = 0; )
     760        IF_FEATURE_FIND_NOT( ap->invert = invert_flag; )
     761        IF_FEATURE_FIND_NOT( invert_flag = 0; )
    522762        return ap;
    523763    }
     
    557797
    558798    /* --- Operators --- */
    559         if (parm == PARM_a USE_DESKTOP(|| parm == PARM_and)) {
     799        if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
    560800            /* no further special handling required */
    561801        }
    562         else if (parm == PARM_o USE_DESKTOP(|| parm == PARM_or)) {
     802        else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
    563803            /* start new OR group */
    564804            cur_group++;
     
    569809        }
    570810#if ENABLE_FEATURE_FIND_NOT
    571         else if (parm == PARM_char_not USE_DESKTOP(|| parm == PARM_not)) {
     811        else if (parm == PARM_char_not IF_DESKTOP(|| parm == PARM_not)) {
    572812            /* also handles "find ! ! -name 'foo*'" */
    573813            invert_flag ^= 1;
     
    577817    /* --- Tests and actions --- */
    578818        else if (parm == PARM_print) {
    579             need_print = 0;
     819            G.need_print = 0;
    580820            /* GNU find ignores '!' here: "find ! -print" */
    581             USE_FEATURE_FIND_NOT( invert_flag = 0; )
     821            IF_FEATURE_FIND_NOT( invert_flag = 0; )
    582822            (void) ALLOC_ACTION(print);
    583823        }
    584824#if ENABLE_FEATURE_FIND_PRINT0
    585825        else if (parm == PARM_print0) {
    586             need_print = 0;
    587             USE_FEATURE_FIND_NOT( invert_flag = 0; )
     826            G.need_print = 0;
     827            IF_FEATURE_FIND_NOT( invert_flag = 0; )
    588828            (void) ALLOC_ACTION(print0);
    589829        }
     
    591831#if ENABLE_FEATURE_FIND_DEPTH
    592832        else if (parm == PARM_depth) {
    593             recurse_flags |= ACTION_DEPTHFIRST;
     833            G.recurse_flags |= ACTION_DEPTHFIRST;
    594834        }
    595835#endif
    596836#if ENABLE_FEATURE_FIND_PRUNE
    597837        else if (parm == PARM_prune) {
    598             USE_FEATURE_FIND_NOT( invert_flag = 0; )
     838            IF_FEATURE_FIND_NOT( invert_flag = 0; )
    599839            (void) ALLOC_ACTION(prune);
    600840        }
     
    602842#if ENABLE_FEATURE_FIND_DELETE
    603843        else if (parm == PARM_delete) {
    604             need_print = 0;
    605             recurse_flags |= ACTION_DEPTHFIRST;
     844            G.need_print = 0;
     845            G.recurse_flags |= ACTION_DEPTHFIRST;
    606846            (void) ALLOC_ACTION(delete);
    607847        }
     
    611851            int i;
    612852            action_exec *ap;
    613             need_print = 0;
    614             USE_FEATURE_FIND_NOT( invert_flag = 0; )
     853            G.need_print = 0;
     854            IF_FEATURE_FIND_NOT( invert_flag = 0; )
    615855            ap = ALLOC_ACTION(exec);
    616856            ap->exec_argv = ++argv; /* first arg after -exec */
    617             ap->exec_argc = 0;
     857            /*ap->exec_argc = 0; - ALLOC_ACTION did it */
    618858            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], ';'))
     859                if (!*argv) /* did not see ';' or '+' until end */
     860                    bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
     861                // find -exec echo Foo ">{}<" ";"
     862                // executes "echo Foo <filename>",
     863                // find -exec echo Foo ">{}<" "+"
     864                // executes "echo Foo <filename1> <filename2> <filename3>...".
     865                // TODO (so far we treat "+" just like ";")
     866                if ((argv[0][0] == ';' || argv[0][0] == '+')
     867                 && argv[0][1] == '\0'
     868                ) {
    622869                    break;
     870                }
    623871                argv++;
    624872                ap->exec_argc++;
     
    655903        }
    656904#endif
    657         else if (parm == PARM_name) {
     905        else if (parm == PARM_name || parm == PARM_iname) {
    658906            action_name *ap;
    659907            ap = ALLOC_ACTION(name);
    660908            ap->pattern = arg1;
     909            ap->iname = (parm == PARM_iname);
    661910        }
    662911#if ENABLE_FEATURE_FIND_PATH
     
    692941            ap->perm_char = arg1[0];
    693942            arg1 = plus_minus_num(arg1);
    694             ap->perm_mask = 0;
     943            /*ap->perm_mask = 0; - ALLOC_ACTION did it */
    695944            if (!bb_parse_mode(arg1, &ap->perm_mask))
    696                 bb_error_msg_and_die("invalid mode: %s", arg1);
     945                bb_error_msg_and_die("invalid mode '%s'", arg1);
    697946        }
    698947#endif
     
    7661015                { "b", 512 },
    7671016                { "k", 1024 },
    768                 { }
     1017                { "", 0 }
    7691018            };
    7701019            action_size *ap;
     
    7781027            action_context *ap;
    7791028            ap = ALLOC_ACTION(context);
    780             ap->context = NULL;
     1029            /*ap->context = NULL; - ALLOC_ACTION did it */
    7811030            /* SELinux headers erroneously declare non-const parameter */
    7821031            if (selinux_raw_to_trans_context((char*)arg1, &ap->context))
    783                 bb_perror_msg("%s", arg1);
     1032                bb_simple_perror_msg(arg1);
     1033        }
     1034#endif
     1035#if ENABLE_FEATURE_FIND_LINKS
     1036        else if (parm == PARM_links) {
     1037            action_links *ap;
     1038            ap = ALLOC_ACTION(links);
     1039            ap->links_char = arg1[0];
     1040            ap->links_count = xatoul(plus_minus_num(arg1));
    7841041        }
    7851042#endif
     
    7941051}
    7951052
    796 
    797 int find_main(int argc, char **argv);
    798 int find_main(int argc, char **argv)
     1053//usage:#define find_trivial_usage
     1054//usage:       "[PATH]... [EXPRESSION]"
     1055//usage:#define find_full_usage "\n\n"
     1056//usage:       "Search for files. The default PATH is the current directory,\n"
     1057//usage:       "default EXPRESSION is '-print'\n"
     1058//usage:     "\nEXPRESSION may consist of:"
     1059//usage:     "\n    -follow     Follow symlinks"
     1060//usage:    IF_FEATURE_FIND_XDEV(
     1061//usage:     "\n    -xdev       Don't descend directories on other filesystems"
     1062//usage:    )
     1063//usage:    IF_FEATURE_FIND_MAXDEPTH(
     1064//usage:     "\n    -maxdepth N Descend at most N levels. -maxdepth 0 applies"
     1065//usage:     "\n            tests/actions to command line arguments only"
     1066//usage:    )
     1067//usage:     "\n    -mindepth N Don't act on first N levels"
     1068//usage:     "\n    -name PATTERN   File name (w/o directory name) matches PATTERN"
     1069//usage:     "\n    -iname PATTERN  Case insensitive -name"
     1070//usage:    IF_FEATURE_FIND_PATH(
     1071//usage:     "\n    -path PATTERN   Path matches PATTERN"
     1072//usage:    )
     1073//usage:    IF_FEATURE_FIND_REGEX(
     1074//usage:     "\n    -regex PATTERN  Path matches regex PATTERN"
     1075//usage:    )
     1076//usage:    IF_FEATURE_FIND_TYPE(
     1077//usage:     "\n    -type X     File type is X (X is one of: f,d,l,b,c,...)"
     1078//usage:    )
     1079//usage:    IF_FEATURE_FIND_PERM(
     1080//usage:     "\n    -perm NNN   Permissions match any of (+NNN), all of (-NNN),"
     1081//usage:     "\n            or exactly NNN"
     1082//usage:    )
     1083//usage:    IF_FEATURE_FIND_MTIME(
     1084//usage:     "\n    -mtime DAYS Modified time is greater than (+N), less than (-N),"
     1085//usage:     "\n            or exactly N days"
     1086//usage:    )
     1087//usage:    IF_FEATURE_FIND_MMIN(
     1088//usage:     "\n    -mmin MINS  Modified time is greater than (+N), less than (-N),"
     1089//usage:     "\n            or exactly N minutes"
     1090//usage:    )
     1091//usage:    IF_FEATURE_FIND_NEWER(
     1092//usage:     "\n    -newer FILE Modified time is more recent than FILE's"
     1093//usage:    )
     1094//usage:    IF_FEATURE_FIND_INUM(
     1095//usage:     "\n    -inum N     File has inode number N"
     1096//usage:    )
     1097//usage:    IF_FEATURE_FIND_USER(
     1098//usage:     "\n    -user NAME  File is owned by user NAME (numeric user ID allowed)"
     1099//usage:    )
     1100//usage:    IF_FEATURE_FIND_GROUP(
     1101//usage:     "\n    -group NAME File belongs to group NAME (numeric group ID allowed)"
     1102//usage:    )
     1103//usage:    IF_FEATURE_FIND_DEPTH(
     1104//usage:     "\n    -depth      Process directory name after traversing it"
     1105//usage:    )
     1106//usage:    IF_FEATURE_FIND_SIZE(
     1107//usage:     "\n    -size N[bck]    File size is N (c:bytes,k:kbytes,b:512 bytes(def.))"
     1108//usage:     "\n            +/-N: file size is bigger/smaller than N"
     1109//usage:    )
     1110//usage:    IF_FEATURE_FIND_LINKS(
     1111//usage:     "\n    -links N    Number of links is greater than (+N), less than (-N),"
     1112//usage:     "\n            or exactly N"
     1113//usage:    )
     1114//usage:     "\n    -print      Print (default and assumed)"
     1115//usage:    IF_FEATURE_FIND_PRINT0(
     1116//usage:     "\n    -print0     Delimit output with null characters rather than"
     1117//usage:     "\n            newlines"
     1118//usage:    )
     1119//usage:    IF_FEATURE_FIND_CONTEXT(
     1120//usage:     "\n    -context    File has specified security context"
     1121//usage:    )
     1122//usage:    IF_FEATURE_FIND_EXEC(
     1123//usage:     "\n    -exec CMD ARG ; Run CMD with all instances of {} replaced by the"
     1124//usage:     "\n            matching files"
     1125//usage:    )
     1126//usage:    IF_FEATURE_FIND_PRUNE(
     1127//usage:     "\n    -prune      Stop traversing current subtree"
     1128//usage:    )
     1129//usage:    IF_FEATURE_FIND_DELETE(
     1130//usage:     "\n    -delete     Delete files, turns on -depth option"
     1131//usage:    )
     1132//usage:    IF_FEATURE_FIND_PAREN(
     1133//usage:     "\n    (EXPR)      Group an expression"
     1134//usage:    )
     1135//usage:
     1136//usage:#define find_example_usage
     1137//usage:       "$ find / -name passwd\n"
     1138//usage:       "/etc/passwd\n"
     1139
     1140int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     1141int find_main(int argc UNUSED_PARAM, char **argv)
    7991142{
    8001143    static const char options[] ALIGN1 =
    8011144                      "-follow\0"
    802 USE_FEATURE_FIND_XDEV(    "-xdev\0"    )
    803 USE_FEATURE_FIND_MAXDEPTH("-maxdepth\0")
     1145IF_FEATURE_FIND_XDEV(    "-xdev\0"    )
     1146IF_FEATURE_FIND_MAXDEPTH("-mindepth\0""-maxdepth\0")
    8041147                      ;
    8051148    enum {
    8061149                      OPT_FOLLOW,
    807 USE_FEATURE_FIND_XDEV(    OPT_XDEV    ,)
    808 USE_FEATURE_FIND_MAXDEPTH(OPT_MAXDEPTH,)
     1150IF_FEATURE_FIND_XDEV(    OPT_XDEV    ,)
     1151IF_FEATURE_FIND_MAXDEPTH(OPT_MINDEPTH,)
    8091152    };
    8101153
     
    8131156    int i, firstopt, status = EXIT_SUCCESS;
    8141157#if ENABLE_FEATURE_FIND_MAXDEPTH
    815     int maxdepth = INT_MAX;
    816 #endif
    817 
    818     for (firstopt = 1; firstopt < argc; firstopt++) {
     1158    int minmaxdepth[2] = { 0, INT_MAX };
     1159#else
     1160#define minmaxdepth NULL
     1161#endif
     1162
     1163    INIT_G();
     1164
     1165    for (firstopt = 1; argv[firstopt]; firstopt++) {
    8191166        if (argv[firstopt][0] == '-')
    8201167            break;
     
    8431190        int opt = index_in_strings(options, arg);
    8441191        if (opt == OPT_FOLLOW) {
    845             recurse_flags |= ACTION_FOLLOWLINKS;
     1192            G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
    8461193            argp[0] = (char*)"-a";
    8471194        }
     
    8491196        if (opt == OPT_XDEV) {
    8501197            struct stat stbuf;
    851             if (!xdev_count) {
    852                 xdev_count = firstopt - 1;
    853                 xdev_dev = xmalloc(xdev_count * sizeof(dev_t));
     1198            if (!G.xdev_count) {
     1199                G.xdev_count = firstopt - 1;
     1200                G.xdev_dev = xzalloc(G.xdev_count * sizeof(G.xdev_dev[0]));
    8541201                for (i = 1; i < firstopt; i++) {
    8551202                    /* not xstat(): shouldn't bomb out on
    8561203                     * "find not_exist exist -xdev" */
    857                     if (stat(argv[i], &stbuf))
    858                         stbuf.st_dev = -1L;
    859                     xdev_dev[i-1] = stbuf.st_dev;
     1204                    if (stat(argv[i], &stbuf) == 0)
     1205                        G.xdev_dev[i-1] = stbuf.st_dev;
     1206                    /* else G.xdev_dev[i-1] stays 0 and
     1207                     * won't match any real device dev_t */
    8601208                }
    8611209            }
     
    8641212#endif
    8651213#if ENABLE_FEATURE_FIND_MAXDEPTH
    866         if (opt == OPT_MAXDEPTH) {
     1214        if (opt == OPT_MINDEPTH || opt == OPT_MINDEPTH + 1) {
    8671215            if (!argp[1])
    8681216                bb_show_usage();
    869             maxdepth = xatoi_u(argp[1]);
     1217            minmaxdepth[opt - OPT_MINDEPTH] = xatoi_positive(argp[1]);
    8701218            argp[0] = (char*)"-a";
    8711219            argp[1] = (char*)"-a";
     
    8761224    }
    8771225
    878     actions = parse_params(&argv[firstopt]);
     1226    G.actions = parse_params(&argv[firstopt]);
    8791227
    8801228    for (i = 1; i < firstopt; i++) {
    8811229        if (!recursive_action(argv[i],
    882                 recurse_flags,  /* flags */
     1230                G.recurse_flags,/* flags */
    8831231                fileAction,     /* file action */
    8841232                fileAction,     /* dir action */
    8851233#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 */
     1234                minmaxdepth,    /* user data */
    8891235#else
    8901236                NULL,           /* user data */
  • branches/2.2.9/mindi-busybox/findutils/grep.c

    r1765 r2725  
    66 * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
    77 *
    8  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    99 */
    10 /* BB_AUDIT SUSv3 defects - unsupported option -x. */
     10/* BB_AUDIT SUSv3 defects - unsupported option -x "match whole line only". */
    1111/* BB_AUDIT GNU defects - always acts as -a.  */
    1212/* http://www.opengroup.org/onlinepubs/007904975/utilities/grep.html */
     
    1515 * correction "-e pattern1 -e pattern2" logic and more optimizations.
    1616 * precompiled regex
    17  */
    18 /*
     17 *
    1918 * (C) 2006 Jac Goudsmit added -o option
    2019 */
    2120
     21//applet:IF_GREP(APPLET(grep, _BB_DIR_BIN, _BB_SUID_DROP))
     22//applet:IF_FEATURE_GREP_EGREP_ALIAS(APPLET_ODDNAME(egrep, grep, _BB_DIR_BIN, _BB_SUID_DROP, egrep))
     23//applet:IF_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, _BB_DIR_BIN, _BB_SUID_DROP, fgrep))
     24
     25//kbuild:lib-$(CONFIG_GREP) += grep.o
     26
     27//config:config GREP
     28//config:   bool "grep"
     29//config:   default y
     30//config:   help
     31//config:     grep is used to search files for a specified pattern.
     32//config:
     33//config:config FEATURE_GREP_EGREP_ALIAS
     34//config:   bool "Enable extended regular expressions (egrep & grep -E)"
     35//config:   default y
     36//config:   depends on GREP
     37//config:   help
     38//config:     Enabled support for extended regular expressions. Extended
     39//config:     regular expressions allow for alternation (foo|bar), grouping,
     40//config:     and various repetition operators.
     41//config:
     42//config:config FEATURE_GREP_FGREP_ALIAS
     43//config:   bool "Alias fgrep to grep -F"
     44//config:   default y
     45//config:   depends on GREP
     46//config:   help
     47//config:     fgrep sees the search pattern as a normal string rather than
     48//config:     regular expressions.
     49//config:     grep -F always works, this just creates the fgrep alias.
     50//config:
     51//config:config FEATURE_GREP_CONTEXT
     52//config:   bool "Enable before and after context flags (-A, -B and -C)"
     53//config:   default y
     54//config:   depends on GREP
     55//config:   help
     56//config:     Print the specified number of leading (-B) and/or trailing (-A)
     57//config:     context surrounding our matching lines.
     58//config:     Print the specified number of context lines (-C).
     59
    2260#include "libbb.h"
    2361#include "xregex.h"
    2462
     63
    2564/* options */
     65//usage:#define grep_trivial_usage
     66//usage:       "[-HhnlLoqvsriw"
     67//usage:       "F"
     68//usage:    IF_FEATURE_GREP_EGREP_ALIAS("E")
     69//usage:    IF_EXTRA_COMPAT("z")
     70//usage:       "] [-m N] "
     71//usage:    IF_FEATURE_GREP_CONTEXT("[-A/B/C N] ")
     72//usage:       "PATTERN/-e PATTERN.../-f FILE [FILE]..."
     73//usage:#define grep_full_usage "\n\n"
     74//usage:       "Search for PATTERN in FILEs (or stdin)\n"
     75//usage:     "\nOptions:"
     76//usage:     "\n    -H  Add 'filename:' prefix"
     77//usage:     "\n    -h  Do not add 'filename:' prefix"
     78//usage:     "\n    -n  Add 'line_no:' prefix"
     79//usage:     "\n    -l  Show only names of files that match"
     80//usage:     "\n    -L  Show only names of files that don't match"
     81//usage:     "\n    -c  Show only count of matching lines"
     82//usage:     "\n    -o  Show only the matching part of line"
     83//usage:     "\n    -q  Quiet. Return 0 if PATTERN is found, 1 otherwise"
     84//usage:     "\n    -v  Select non-matching lines"
     85//usage:     "\n    -s  Suppress open and read errors"
     86//usage:     "\n    -r  Recurse"
     87//usage:     "\n    -i  Ignore case"
     88//usage:     "\n    -w  Match whole words only"
     89//usage:     "\n    -F  PATTERN is a literal (not regexp)"
     90//usage:    IF_FEATURE_GREP_EGREP_ALIAS(
     91//usage:     "\n    -E  PATTERN is an extended regexp"
     92//usage:    )
     93//usage:    IF_EXTRA_COMPAT(
     94//usage:     "\n    -z  Input is NUL terminated"
     95//usage:    )
     96//usage:     "\n    -m N    Match up to N times per file"
     97//usage:    IF_FEATURE_GREP_CONTEXT(
     98//usage:     "\n    -A N    Print N lines of trailing context"
     99//usage:     "\n    -B N    Print N lines of leading context"
     100//usage:     "\n    -C N    Same as '-A N -B N'"
     101//usage:    )
     102//usage:     "\n    -e PTRN Pattern to match"
     103//usage:     "\n    -f FILE Read pattern from file"
     104//usage:
     105//usage:#define grep_example_usage
     106//usage:       "$ grep root /etc/passwd\n"
     107//usage:       "root:x:0:0:root:/root:/bin/bash\n"
     108//usage:       "$ grep ^[rR]oo. /etc/passwd\n"
     109//usage:       "root:x:0:0:root:/root:/bin/bash\n"
     110//usage:
     111//usage:#define egrep_trivial_usage NOUSAGE_STR
     112//usage:#define egrep_full_usage ""
     113//usage:#define fgrep_trivial_usage NOUSAGE_STR
     114//usage:#define fgrep_full_usage ""
     115
    26116#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") \
     117    "lnqvscFiHhe:f:Lorm:w" \
     118    IF_FEATURE_GREP_CONTEXT("A:B:C:") \
     119    IF_FEATURE_GREP_EGREP_ALIAS("E") \
     120    IF_EXTRA_COMPAT("z") \
    31121    "aI"
    32122/* ignored: -a "assume all files to be text" */
    33123/* ignored: -I "assume binary files have no matches" */
    34 
    35124enum {
    36125    OPTBIT_l, /* list matched file names only */
    37126    OPTBIT_n, /* print line# */
    38     OPTBIT_q, /* quiet - exit(0) of first match */
     127    OPTBIT_q, /* quiet - exit(EXIT_SUCCESS) of first match */
    39128    OPTBIT_v, /* invert the match, to select non-matching lines */
    40129    OPTBIT_s, /* suppress errors about file open errors */
     
    50139    OPTBIT_r, /* recurse dirs */
    51140    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 */
     141    OPTBIT_w, /* -w whole word match */
     142    IF_FEATURE_GREP_CONTEXT(    OPTBIT_A ,) /* -A NUM: after-match context */
     143    IF_FEATURE_GREP_CONTEXT(    OPTBIT_B ,) /* -B NUM: before-match context */
     144    IF_FEATURE_GREP_CONTEXT(    OPTBIT_C ,) /* -C NUM: -A and -B combined */
     145    IF_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */
     146    IF_EXTRA_COMPAT(            OPTBIT_z ,) /* input is NUL terminated */
    57147    OPT_l = 1 << OPTBIT_l,
    58148    OPT_n = 1 << OPTBIT_n,
     
    71161    OPT_r = 1 << OPTBIT_r,
    72162    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,
     163    OPT_w = 1 << OPTBIT_w,
     164    OPT_A = IF_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_A)) + 0,
     165    OPT_B = IF_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_B)) + 0,
     166    OPT_C = IF_FEATURE_GREP_CONTEXT(    (1 << OPTBIT_C)) + 0,
     167    OPT_E = IF_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0,
     168    OPT_z = IF_EXTRA_COMPAT(            (1 << OPTBIT_z)) + 0,
    78169};
    79170
     
    85176#define FGREP_FLAG                  (option_mask32 & OPT_F)
    86177#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L)
    87 
    88 typedef unsigned char byte_t;
    89 
    90 static int max_matches;
    91 static int reflags;
    92 static byte_t invert_search;
    93 static byte_t print_filename;
    94 static byte_t open_errors;
    95 
     178#define NUL_DELIMITED               (option_mask32 & OPT_z)
     179
     180struct globals {
     181    int max_matches;
     182#if !ENABLE_EXTRA_COMPAT
     183    int reflags;
     184#else
     185    RE_TRANSLATE_TYPE case_fold; /* RE_TRANSLATE_TYPE is [[un]signed] char* */
     186#endif
     187    smalluint invert_search;
     188    smalluint print_filename;
     189    smalluint open_errors;
    96190#if ENABLE_FEATURE_GREP_CONTEXT
    97 static byte_t did_print_line;
    98 static int lines_before;
    99 static int lines_after;
    100 static char **before_buf;
    101 static int last_line_printed;
    102 #endif /* ENABLE_FEATURE_GREP_CONTEXT */
    103 /* globals used internally */
    104 static llist_t *pattern_head;   /* growable list of patterns to match */
    105 static const char *cur_file;    /* the current file we are reading */
     191    smalluint did_print_line;
     192    int lines_before;
     193    int lines_after;
     194    char **before_buf;
     195    IF_EXTRA_COMPAT(size_t *before_buf_size;)
     196    int last_line_printed;
     197#endif
     198    /* globals used internally */
     199    llist_t *pattern_head;   /* growable list of patterns to match */
     200    const char *cur_file;    /* the current file we are reading */
     201} FIX_ALIASING;
     202#define G (*(struct globals*)&bb_common_bufsiz1)
     203#define INIT_G() do { \
     204    struct G_sizecheck { \
     205        char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
     206    }; \
     207} while (0)
     208#define max_matches       (G.max_matches         )
     209#if !ENABLE_EXTRA_COMPAT
     210# define reflags          (G.reflags             )
     211#else
     212# define case_fold        (G.case_fold           )
     213/* http://www.delorie.com/gnu/docs/regex/regex_46.html */
     214# define reflags           re_syntax_options
     215# undef REG_NOSUB
     216# undef REG_EXTENDED
     217# undef REG_ICASE
     218# define REG_NOSUB    bug:is:here /* should not be used */
     219/* Just RE_SYNTAX_EGREP is not enough, need to enable {n[,[m]]} too */
     220# define REG_EXTENDED (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
     221# define REG_ICASE    bug:is:here /* should not be used */
     222#endif
     223#define invert_search     (G.invert_search       )
     224#define print_filename    (G.print_filename      )
     225#define open_errors       (G.open_errors         )
     226#define did_print_line    (G.did_print_line      )
     227#define lines_before      (G.lines_before        )
     228#define lines_after       (G.lines_after         )
     229#define before_buf        (G.before_buf          )
     230#define before_buf_size   (G.before_buf_size     )
     231#define last_line_printed (G.last_line_printed   )
     232#define pattern_head      (G.pattern_head        )
     233#define cur_file          (G.cur_file            )
     234
    106235
    107236typedef struct grep_list_data_t {
    108237    char *pattern;
    109     regex_t preg;
    110 #define PATTERN_MEM_A 1
     238/* for GNU regex, matched_range must be persistent across grep_file() calls */
     239#if !ENABLE_EXTRA_COMPAT
     240    regex_t compiled_regex;
     241    regmatch_t matched_range;
     242#else
     243    struct re_pattern_buffer compiled_regex;
     244    struct re_registers matched_range;
     245#endif
     246#define ALLOCATED 1
    111247#define COMPILED 2
    112248    int flg_mem_alocated_compiled;
    113249} grep_list_data_t;
    114250
    115 static void print_line(const char *line, int linenum, char decoration)
     251#if !ENABLE_EXTRA_COMPAT
     252#define print_line(line, line_len, linenum, decoration) \
     253    print_line(line, linenum, decoration)
     254#endif
     255static void print_line(const char *line, size_t line_len, int linenum, char decoration)
    116256{
    117257#if ENABLE_FEATURE_GREP_CONTEXT
     
    121261        return;
    122262    /* possibly print the little '--' separator */
    123     if ((lines_before || lines_after) && did_print_line &&
    124             last_line_printed != linenum - 1) {
     263    if ((lines_before || lines_after) && did_print_line
     264     && last_line_printed != linenum - 1
     265    ) {
    125266        puts("--");
    126267    }
     
    134275        printf("%i%c", linenum, decoration);
    135276    /* Emulate weird GNU grep behavior with -ov */
    136     if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o))
     277    if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o)) {
     278#if !ENABLE_EXTRA_COMPAT
    137279        puts(line);
     280#else
     281        fwrite(line, 1, line_len, stdout);
     282        putchar(NUL_DELIMITED ? '\0' : '\n');
     283#endif
     284    }
    138285}
     286
     287#if ENABLE_EXTRA_COMPAT
     288/* Unlike getline, this one removes trailing '\n' */
     289static ssize_t FAST_FUNC bb_getline(char **line_ptr, size_t *line_alloc_len, FILE *file)
     290{
     291    ssize_t res_sz;
     292    char *line;
     293    int delim = (NUL_DELIMITED ? '\0' : '\n');
     294
     295    res_sz = getdelim(line_ptr, line_alloc_len, delim, file);
     296    line = *line_ptr;
     297
     298    if (res_sz > 0) {
     299        if (line[res_sz - 1] == delim)
     300            line[--res_sz] = '\0';
     301    } else {
     302        free(line); /* uclibc allocates a buffer even on EOF. WTF? */
     303    }
     304    return res_sz;
     305}
     306#endif
    139307
    140308static int grep_file(FILE *file)
    141309{
    142     char *line;
    143     byte_t ret;
     310    smalluint found;
    144311    int linenum = 0;
    145312    int nmatches = 0;
    146     regmatch_t regmatch;
     313#if !ENABLE_EXTRA_COMPAT
     314    char *line;
     315#else
     316    char *line = NULL;
     317    ssize_t line_len;
     318    size_t line_alloc_len;
     319# define rm_so start[0]
     320# define rm_eo end[0]
     321#endif
    147322#if ENABLE_FEATURE_GREP_CONTEXT
    148323    int print_n_lines_after = 0;
     
    151326#else
    152327    enum { print_n_lines_after = 0 };
    153 #endif /* ENABLE_FEATURE_GREP_CONTEXT */
    154 
    155     while ((line = xmalloc_getline(file)) != NULL) {
     328#endif
     329
     330    while (
     331#if !ENABLE_EXTRA_COMPAT
     332        (line = xmalloc_fgetline(file)) != NULL
     333#else
     334        (line_len = bb_getline(&line, &line_alloc_len, file)) >= 0
     335#endif
     336    ) {
    156337        llist_t *pattern_ptr = pattern_head;
    157         grep_list_data_t * gl;
     338        grep_list_data_t *gl = gl; /* for gcc */
    158339
    159340        linenum++;
    160         ret = 0;
     341        found = 0;
    161342        while (pattern_ptr) {
    162343            gl = (grep_list_data_t *)pattern_ptr->data;
    163344            if (FGREP_FLAG) {
    164                 ret = strstr(line, gl->pattern) != NULL;
     345                found |= (((option_mask32 & OPT_i)
     346                    ? strcasestr(line, gl->pattern)
     347                    : strstr(line, gl->pattern)
     348                    ) != NULL);
    165349            } else {
    166                 /*
    167                  * test for a postitive-assertion match (regexec returns success (0)
    168                  * and the user did not specify invert search), or a negative-assertion
    169                  * match (regexec returns failure (REG_NOMATCH) and the user specified
    170                  * invert search)
    171                  */
    172350                if (!(gl->flg_mem_alocated_compiled & COMPILED)) {
    173351                    gl->flg_mem_alocated_compiled |= COMPILED;
    174                     xregcomp(&(gl->preg), gl->pattern, reflags);
     352#if !ENABLE_EXTRA_COMPAT
     353                    xregcomp(&gl->compiled_regex, gl->pattern, reflags);
     354#else
     355                    memset(&gl->compiled_regex, 0, sizeof(gl->compiled_regex));
     356                    gl->compiled_regex.translate = case_fold; /* for -i */
     357                    if (re_compile_pattern(gl->pattern, strlen(gl->pattern), &gl->compiled_regex))
     358                        bb_error_msg_and_die("bad regex '%s'", gl->pattern);
     359#endif
    175360                }
    176                 regmatch.rm_so = 0;
    177                 regmatch.rm_eo = 0;
    178                 if (regexec(&(gl->preg), line, 1, &regmatch, 0) == 0) {
     361#if !ENABLE_EXTRA_COMPAT
     362                gl->matched_range.rm_so = 0;
     363                gl->matched_range.rm_eo = 0;
     364#endif
     365                if (
     366#if !ENABLE_EXTRA_COMPAT
     367                    regexec(&gl->compiled_regex, line, 1, &gl->matched_range, 0) == 0
     368#else
     369                    re_search(&gl->compiled_regex, line, line_len,
     370                            /*start:*/ 0, /*range:*/ line_len,
     371                            &gl->matched_range) >= 0
     372#endif
     373                ) {
    179374                    if (!(option_mask32 & OPT_w))
    180                         ret = 1;
     375                        found = 1;
    181376                    else {
    182377                        char c = ' ';
    183                         if (regmatch.rm_so)
    184                             c = line[regmatch.rm_so - 1];
     378                        if (gl->matched_range.rm_so)
     379                            c = line[gl->matched_range.rm_so - 1];
    185380                        if (!isalnum(c) && c != '_') {
    186                             c = line[regmatch.rm_eo];
     381                            c = line[gl->matched_range.rm_eo];
    187382                            if (!c || (!isalnum(c) && c != '_'))
    188                                 ret = 1;
     383                                found = 1;
    189384                        }
    190385                    }
    191386                }
    192387            }
     388            /* If it's non-inverted search, we can stop
     389             * at first match */
     390            if (found && !invert_search)
     391                goto do_found;
    193392            pattern_ptr = pattern_ptr->link;
    194393        } /* while (pattern_ptr) */
    195394
    196         if (ret ^ invert_search) {
     395        if (found ^ invert_search) {
     396 do_found:
    197397            /* keep track of matches */
    198398            nmatches++;
     
    206406                     * if any match is found,
    207407                     * even if errors were detected" */
    208                     exit(0);
     408                    exit(EXIT_SUCCESS);
    209409                }
    210410                /* if we're just printing filenames, we stop after the first match */
    211411                if (PRINT_FILES_WITH_MATCHES) {
    212412                    puts(cur_file);
    213                     /* fall thru to "return 1" */
     413                    /* fall through to "return 1" */
    214414                }
    215415                /* OPT_L aka PRINT_FILES_WITHOUT_MATCHES: return early */
     
    244444                    /* now print each line in the buffer, clearing them as we go */
    245445                    while (before_buf[idx] != NULL) {
    246                         print_line(before_buf[idx], first_buf_entry_line_num, '-');
     446                        print_line(before_buf[idx], before_buf_size[idx], first_buf_entry_line_num, '-');
    247447                        free(before_buf[idx]);
    248448                        before_buf[idx] = NULL;
     
    256456#endif
    257457                if (option_mask32 & OPT_o) {
    258                     line[regmatch.rm_eo] = '\0';
    259                     print_line(line + regmatch.rm_so, linenum, ':');
     458                    if (FGREP_FLAG) {
     459                        /* -Fo just prints the pattern
     460                         * (unless -v: -Fov doesnt print anything at all) */
     461                        if (found)
     462                            print_line(gl->pattern, strlen(gl->pattern), linenum, ':');
     463                    } else while (1) {
     464                        unsigned start = gl->matched_range.rm_so;
     465                        unsigned end = gl->matched_range.rm_eo;
     466                        unsigned len = end - start;
     467                        char old = line[end];
     468                        line[end] = '\0';
     469                        /* Empty match is not printed: try "echo test | grep -o ''" */
     470                        if (len != 0)
     471                            print_line(line + start, len, linenum, ':');
     472                        if (old == '\0')
     473                            break;
     474                        line[end] = old;
     475                        if (len == 0)
     476                            end++;
     477#if !ENABLE_EXTRA_COMPAT
     478                        if (regexec(&gl->compiled_regex, line + end,
     479                                1, &gl->matched_range, REG_NOTBOL) != 0)
     480                            break;
     481                        gl->matched_range.rm_so += end;
     482                        gl->matched_range.rm_eo += end;
     483#else
     484                        if (re_search(&gl->compiled_regex, line, line_len,
     485                                end, line_len - end,
     486                                &gl->matched_range) < 0)
     487                            break;
     488#endif
     489                    }
    260490                } else {
    261                     print_line(line, linenum, ':');
     491                    print_line(line, line_len, linenum, ':');
    262492                }
    263493            }
     
    267497            /* if we need to print some context lines after the last match, do so */
    268498            if (print_n_lines_after) {
    269                 print_line(line, linenum, '-');
     499                print_line(line, strlen(line), linenum, '-');
    270500                print_n_lines_after--;
    271501            } else if (lines_before) {
     
    273503                free(before_buf[curpos]);
    274504                before_buf[curpos] = line;
     505                IF_EXTRA_COMPAT(before_buf_size[curpos] = line_len;)
    275506                curpos = (curpos + 1) % lines_before;
    276                 /* avoid free(line) - we took line */
     507                /* avoid free(line) - we took the line */
    277508                line = NULL;
    278509            }
     
    280511
    281512#endif /* ENABLE_FEATURE_GREP_CONTEXT */
     513#if !ENABLE_EXTRA_COMPAT
    282514        free(line);
    283 
     515#endif
    284516        /* Did we print all context after last requested match? */
    285517        if ((option_mask32 & OPT_m)
    286          && !print_n_lines_after && nmatches == max_matches)
     518         && !print_n_lines_after
     519         && nmatches == max_matches
     520        ) {
    287521            break;
    288     }
     522        }
     523    } /* while (read line) */
    289524
    290525    /* special-case file post-processing for options where we don't print line
     
    311546#if ENABLE_FEATURE_CLEAN_UP
    312547#define new_grep_list_data(p, m) add_grep_list_data(p, m)
    313 static char * add_grep_list_data(char *pattern, int flg_used_mem)
     548static char *add_grep_list_data(char *pattern, int flg_used_mem)
    314549#else
    315550#define new_grep_list_data(p, m) add_grep_list_data(p)
    316 static char * add_grep_list_data(char *pattern)
     551static char *add_grep_list_data(char *pattern)
    317552#endif
    318553{
    319     grep_list_data_t *gl = xmalloc(sizeof(grep_list_data_t));
     554    grep_list_data_t *gl = xzalloc(sizeof(*gl));
    320555    gl->pattern = pattern;
    321556#if ENABLE_FEATURE_CLEAN_UP
    322557    gl->flg_mem_alocated_compiled = flg_used_mem;
    323558#else
    324     gl->flg_mem_alocated_compiled = 0;
     559    /*gl->flg_mem_alocated_compiled = 0;*/
    325560#endif
    326561    return (char *)gl;
     
    338573        fopt = cur->link;
    339574        free(cur);
    340         f = xfopen(ffile, "r");
    341         while ((line = xmalloc_getline(f)) != NULL) {
     575        f = xfopen_stdin(ffile);
     576        while ((line = xmalloc_fgetline(f)) != NULL) {
    342577            llist_add_to(&pattern_head,
    343                 new_grep_list_data(line, PATTERN_MEM_A));
     578                new_grep_list_data(line, ALLOCATED));
    344579        }
    345580    }
    346581}
    347582
    348 static int file_action_grep(const char *filename, struct stat *statbuf, void* matched, int depth)
     583static int FAST_FUNC file_action_grep(const char *filename,
     584            struct stat *statbuf UNUSED_PARAM,
     585            void* matched,
     586            int depth UNUSED_PARAM)
    349587{
    350     FILE *file = fopen(filename, "r");
     588    FILE *file = fopen_for_read(filename);
    351589    if (file == NULL) {
    352590        if (!SUPPRESS_ERR_MSGS)
    353             bb_perror_msg("%s", cur_file);
     591            bb_simple_perror_msg(filename);
    354592        open_errors = 1;
    355593        return 0;
     
    375613}
    376614
    377 int grep_main(int argc, char **argv);
    378 int grep_main(int argc, char **argv)
     615int grep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     616int grep_main(int argc UNUSED_PARAM, char **argv)
    379617{
    380618    FILE *file;
    381619    int matched;
    382     char *mopt;
    383620    llist_t *fopt = NULL;
    384621
    385622    /* do normal option parsing */
    386623#if ENABLE_FEATURE_GREP_CONTEXT
    387     char *slines_after;
    388     char *slines_before;
    389     char *Copt;
    390 
    391     opt_complementary = "H-h:e::f::C-AB";
    392     getopt32(argv,
     624    int Copt, opts;
     625
     626    /* -H unsets -h; -C unsets -A,-B; -e,-f are lists;
     627     * -m,-A,-B,-C have numeric param */
     628    opt_complementary = "H-h:C-AB:e::f::m+:A+:B+:C+";
     629    opts = getopt32(argv,
    393630        OPTSTR_GREP,
    394         &pattern_head, &fopt, &mopt,
    395         &slines_after, &slines_before, &Copt);
    396 
    397     if (option_mask32 & OPT_C) {
     631        &pattern_head, &fopt, &max_matches,
     632        &lines_after, &lines_before, &Copt);
     633
     634    if (opts & OPT_C) {
    398635        /* -C unsets prev -A and -B, but following -A or -B
    399636           may override it */
    400         if (!(option_mask32 & OPT_A)) /* not overridden */
    401             slines_after = Copt;
    402         if (!(option_mask32 & OPT_B)) /* not overridden */
    403             slines_before = Copt;
    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);
     637        if (!(opts & OPT_A)) /* not overridden */
     638            lines_after = Copt;
     639        if (!(opts & OPT_B)) /* not overridden */
     640            lines_before = Copt;
    411641    }
    412642    /* sanity checks */
    413     if (option_mask32 & (OPT_c|OPT_q|OPT_l|OPT_L)) {
     643    if (opts & (OPT_c|OPT_q|OPT_l|OPT_L)) {
    414644        option_mask32 &= ~OPT_n;
    415645        lines_before = 0;
    416646        lines_after = 0;
    417     } else if (lines_before > 0)
    418         before_buf = xzalloc(lines_before * sizeof(char *));
     647    } else if (lines_before > 0) {
     648        if (lines_before > INT_MAX / sizeof(long long))
     649            lines_before = INT_MAX / sizeof(long long);
     650        /* overflow in (lines_before * sizeof(x)) is prevented (above) */
     651        before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
     652        IF_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));)
     653    }
    419654#else
    420655    /* with auto sanity checks */
    421     opt_complementary = "H-h:e::f::c-n:q-n:l-n";
     656    /* -H unsets -h; -c,-q or -l unset -n; -e,-f are lists; -m N */
     657    opt_complementary = "H-h:c-n:q-n:l-n:e::f::m+";
    422658    getopt32(argv, OPTSTR_GREP,
    423         &pattern_head, &fopt, &mopt);
    424 #endif
    425     if (option_mask32 & OPT_m) {
    426         max_matches = xatoi_u(mopt);
    427     }
     659        &pattern_head, &fopt, &max_matches);
     660#endif
    428661    invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */
    429662
     
    441674        option_mask32 |= OPT_F;
    442675
     676#if !ENABLE_EXTRA_COMPAT
    443677    if (!(option_mask32 & (OPT_o | OPT_w)))
    444678        reflags = REG_NOSUB;
    445 
    446     if (ENABLE_FEATURE_GREP_EGREP_ALIAS &&
    447             (applet_name[0] == 'e' || (option_mask32 & OPT_E)))
     679#endif
     680
     681    if (ENABLE_FEATURE_GREP_EGREP_ALIAS
     682     && (applet_name[0] == 'e' || (option_mask32 & OPT_E))
     683    ) {
    448684        reflags |= REG_EXTENDED;
    449 
    450     if (option_mask32 & OPT_i)
     685    }
     686#if ENABLE_EXTRA_COMPAT
     687    else {
     688        reflags = RE_SYNTAX_GREP;
     689    }
     690#endif
     691
     692    if (option_mask32 & OPT_i) {
     693#if !ENABLE_EXTRA_COMPAT
    451694        reflags |= REG_ICASE;
     695#else
     696        int i;
     697        case_fold = xmalloc(256);
     698        for (i = 0; i < 256; i++)
     699            case_fold[i] = (unsigned char)i;
     700        for (i = 'a'; i <= 'z'; i++)
     701            case_fold[i] = (unsigned char)(i - ('a' - 'A'));
     702#endif
     703    }
    452704
    453705    argv += optind;
    454     argc -= optind;
    455 
    456     /* if we didn't get a pattern from a -e and no command file was specified,
    457      * argv[optind] should be the pattern. no pattern, no worky */
     706
     707    /* if we didn't get a pattern from -e and no command file was specified,
     708     * first parameter should be the pattern. no pattern, no worky */
    458709    if (pattern_head == NULL) {
    459710        char *pattern;
     
    462713        pattern = new_grep_list_data(*argv++, 0);
    463714        llist_add_to(&pattern_head, pattern);
    464         argc--;
    465     }
    466 
    467     /* argv[(optind)..(argc-1)] should be names of file to grep through. If
     715    }
     716
     717    /* argv[0..(argc-1)] should be names of file to grep through. If
    468718     * there is more than one file to grep, we will print the filenames. */
    469     if (argc > 1)
     719    if (argv[0] && argv[1])
    470720        print_filename = 1;
    471721    /* -H / -h of course override */
     
    477727    /* If no files were specified, or '-' was specified, take input from
    478728     * stdin. Otherwise, we grep through all the files specified. */
    479     if (argc == 0)
    480         argc++;
    481729    matched = 0;
    482     while (argc--) {
    483         cur_file = *argv++;
     730    do {
     731        cur_file = *argv;
    484732        file = stdin;
    485         if (!cur_file || (*cur_file == '-' && !cur_file[1])) {
     733        if (!cur_file || LONE_DASH(cur_file)) {
    486734            cur_file = "(standard input)";
    487735        } else {
     
    496744            }
    497745            /* else: fopen(dir) will succeed, but reading won't */
    498             file = fopen(cur_file, "r");
     746            file = fopen_for_read(cur_file);
    499747            if (file == NULL) {
    500748                if (!SUPPRESS_ERR_MSGS)
    501                     bb_perror_msg("%s", cur_file);
     749                    bb_simple_perror_msg(cur_file);
    502750                open_errors = 1;
    503751                continue;
     
    507755        fclose_if_not_stdin(file);
    508756 grep_done: ;
    509     }
     757    } while (*argv && *++argv);
    510758
    511759    /* destroy all the elments in the pattern list */
     
    513761        while (pattern_head) {
    514762            llist_t *pattern_head_ptr = pattern_head;
    515             grep_list_data_t *gl =
    516                 (grep_list_data_t *)pattern_head_ptr->data;
     763            grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data;
    517764
    518765            pattern_head = pattern_head->link;
    519             if ((gl->flg_mem_alocated_compiled & PATTERN_MEM_A))
     766            if (gl->flg_mem_alocated_compiled & ALLOCATED)
    520767                free(gl->pattern);
    521             if ((gl->flg_mem_alocated_compiled & COMPILED))
    522                 regfree(&(gl->preg));
     768            if (gl->flg_mem_alocated_compiled & COMPILED)
     769                regfree(&gl->compiled_regex);
    523770            free(gl);
    524771            free(pattern_head_ptr);
     
    528775    if (open_errors)
    529776        return 2;
    530     return !matched; /* invert return value 0 = success, 1 = failed */
     777    return !matched; /* invert return value: 0 = success, 1 = failed */
    531778}
  • branches/2.2.9/mindi-busybox/findutils/xargs.c

    r1765 r2725  
    22/*
    33 * Mini xargs implementation for busybox
    4  * Options are supported: "-prtx -n max_arg -s max_chars -e[ouf_str]"
    54 *
    65 * (C) 2002,2003 by Vladimir Oleynik <dzo@simtreas.ru>
     
    1110 * and David MacKenzie <djm@gnu.ai.mit.edu>.
    1211 *
    13  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
     12 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    1413 *
    1514 * xargs is described in the Single Unix Specification v3 at
    1615 * http://www.opengroup.org/onlinepubs/007904975/utilities/xargs.html
    17  *
    1816 */
    1917
     18//applet:IF_XARGS(APPLET_NOEXEC(xargs, xargs, _BB_DIR_USR_BIN, _BB_SUID_DROP, xargs))
     19
     20//kbuild:lib-$(CONFIG_XARGS) += xargs.o
     21
     22//config:config XARGS
     23//config:   bool "xargs"
     24//config:   default y
     25//config:   help
     26//config:     xargs is used to execute a specified command for
     27//config:     every item from standard input.
     28//config:
     29//config:config FEATURE_XARGS_SUPPORT_CONFIRMATION
     30//config:   bool "Enable -p: prompt and confirmation"
     31//config:   default y
     32//config:   depends on XARGS
     33//config:   help
     34//config:     Support -p: prompt the user whether to run each command
     35//config:     line and read a line from the terminal.
     36//config:
     37//config:config FEATURE_XARGS_SUPPORT_QUOTES
     38//config:   bool "Enable single and double quotes and backslash"
     39//config:   default y
     40//config:   depends on XARGS
     41//config:   help
     42//config:     Support quoting in the input.
     43//config:
     44//config:config FEATURE_XARGS_SUPPORT_TERMOPT
     45//config:   bool "Enable -x: exit if -s or -n is exceeded"
     46//config:   default y
     47//config:   depends on XARGS
     48//config:   help
     49//config:     Support -x: exit if the command size (see the -s or -n option)
     50//config:     is exceeded.
     51//config:
     52//config:config FEATURE_XARGS_SUPPORT_ZERO_TERM
     53//config:   bool "Enable -0: NUL-terminated input"
     54//config:   default y
     55//config:   depends on XARGS
     56//config:   help
     57//config:     Support -0: input items are terminated by a NUL character
     58//config:     instead of whitespace, and the quotes and backslash
     59//config:     are not special.
     60
    2061#include "libbb.h"
    2162
     
    2364
    2465
    25 /* COMPAT:  SYSV version defaults size (and has a max value of) to 470.
    26    We try to make it as large as possible. */
    27 #if !defined(ARG_MAX) && defined(_SC_ARG_MAX)
    28 #define ARG_MAX sysconf (_SC_ARG_MAX)
    29 #endif
    30 #ifndef ARG_MAX
    31 #define ARG_MAX 470
    32 #endif
     66//#define dbg_msg(...) bb_error_msg(__VA_ARGS__)
     67#define dbg_msg(...) ((void)0)
    3368
    3469
     
    4883#endif
    4984
     85
     86struct globals {
     87    char **args;
     88    const char *eof_str;
     89    int idx;
     90} FIX_ALIASING;
     91#define G (*(struct globals*)&bb_common_bufsiz1)
     92#define INIT_G() do { } while (0)
     93
     94
    5095/*
    51    This function has special algorithm.
    52    Don't use fork and include to main!
    53 */
    54 static int xargs_exec(char **args)
     96 * This function has special algorithm.
     97 * Don't use fork and include to main!
     98 */
     99static int xargs_exec(void)
    55100{
    56101    int status;
    57102
    58     status = spawn_and_wait(args);
     103    status = spawn_and_wait(G.args);
    59104    if (status < 0) {
    60         bb_perror_msg("%s", args[0]);
     105        bb_simple_perror_msg(G.args[0]);
    61106        return errno == ENOENT ? 127 : 126;
    62107    }
    63108    if (status == 255) {
    64         bb_error_msg("%s: exited with status 255; aborting", args[0]);
     109        bb_error_msg("%s: exited with status 255; aborting", G.args[0]);
    65110        return 124;
    66111    }
    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) {
     112    if (status >= 0x180) {
    75113        bb_error_msg("%s: terminated by signal %d",
    76             args[0], status - 1000);
     114            G.args[0], status - 0x180);
    77115        return 125;
    78116    }
     
    82120}
    83121
    84 
    85 typedef struct xlist_t {
    86     struct xlist_t *link;
    87     size_t length;
    88     char xstr[1];
    89 } xlist_t;
    90 
    91 static smallint eof_stdin_detected;
    92 
    93 #define ISBLANK(c) ((c) == ' ' || (c) == '\t')
    94 #define ISSPACE(c) (ISBLANK(c) || (c) == '\n' || (c) == '\r' \
    95             || (c) == '\f' || (c) == '\v')
     122/* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space.
     123 * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
     124 */
     125#define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); })
     126
     127static void store_param(char *s)
     128{
     129    /* Grow by 256 elements at once */
     130    if (!(G.idx & 0xff)) { /* G.idx == N*256 */
     131        /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */
     132        G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100));
     133    }
     134    G.args[G.idx++] = s;
     135}
     136
     137/* process[0]_stdin:
     138 * Read characters into buf[n_max_chars+1], and when parameter delimiter
     139 * is seen, store the address of a new parameter to args[].
     140 * If reading discovers that last chars do not form the complete
     141 * parameter, the pointer to the first such "tail character" is returned.
     142 * (buf has extra byte at the end to accomodate terminating NUL
     143 * of "tail characters" string).
     144 * Otherwise, the returned pointer points to NUL byte.
     145 * On entry, buf[] may contain some "seed chars" which are to become
     146 * the beginning of the first parameter.
     147 */
    96148
    97149#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
    98 static xlist_t *process_stdin(xlist_t *list_arg,
    99     const char *eof_str, size_t mc, char *buf)
     150static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
    100151{
    101152#define NORM      0
     
    103154#define BACKSLASH 2
    104155#define SPACE     4
    105 
    106     char *s = NULL;         /* start word */
    107     char *p = NULL;         /* pointer to end word */
    108     char q = '\0';          /* quote char */
     156    char q = '\0';             /* quote char */
    109157    char state = NORM;
    110     char eof_str_detected = 0;
    111     size_t line_l = 0;      /* size loaded args line */
    112     int c;                  /* current char */
    113     xlist_t *cur;
    114     xlist_t *prev;
    115 
    116     prev = cur = list_arg;
     158    char *s = buf;             /* start of the word */
     159    char *p = s + strlen(buf); /* end of the word */
     160
     161    buf += n_max_chars;        /* past buffer's end */
     162
     163    /* "goto ret" is used instead of "break" to make control flow
     164     * more obvious: */
     165
    117166    while (1) {
    118         if (!cur) break;
    119         prev = cur;
    120         line_l += cur->length;
    121         cur = cur->link;
    122     }
    123 
    124     while (!eof_stdin_detected) {
    125         c = getchar();
     167        int c = getchar();
    126168        if (c == EOF) {
    127             eof_stdin_detected = 1;
    128             if (s)
    129                 goto unexpected_eof;
    130             break;
    131         }
    132         if (eof_str_detected)
    133             continue;
     169            if (p != s)
     170                goto close_word;
     171            goto ret;
     172        }
    134173        if (state == BACKSLASH) {
    135174            state = NORM;
    136175            goto set;
    137         } else if (state == QUOTE) {
     176        }
     177        if (state == QUOTE) {
    138178            if (c != q)
    139179                goto set;
     
    142182        } else { /* if (state == NORM) */
    143183            if (ISSPACE(c)) {
    144                 if (s) {
    145  unexpected_eof:
     184                if (p != s) {
     185 close_word:
    146186                    state = SPACE;
    147187                    c = '\0';
     
    149189                }
    150190            } else {
    151                 if (s == NULL)
    152                     s = p = buf;
    153191                if (c == '\\') {
    154192                    state = BACKSLASH;
     
    158196                } else {
    159197 set:
    160                     if ((size_t)(p - buf) >= mc)
    161                         bb_error_msg_and_die("argument line too long");
    162198                    *p++ = c;
    163199                }
     
    169205                    q == '\'' ? "single" : "double");
    170206            }
    171             /* word loaded */
    172             if (eof_str) {
    173                 eof_str_detected = (strcmp(s, eof_str) == 0);
    174             }
    175             if (!eof_str_detected) {
    176                 size_t length = (p - buf);
    177                 /* Dont xzalloc - it can be quite big */
    178                 cur = xmalloc(offsetof(xlist_t, xstr) + length);
    179                 cur->link = NULL;
    180                 cur->length = length;
    181                 memcpy(cur->xstr, s, length);
    182                 if (prev == NULL) {
    183                     list_arg = cur;
    184                 } else {
    185                     prev->link = cur;
     207            /* A full word is loaded */
     208            if (G.eof_str) {
     209                if (strcmp(s, G.eof_str) == 0) {
     210                    while (getchar() != EOF)
     211                        continue;
     212                    p = s;
     213                    goto ret;
    186214                }
    187                 prev = cur;
    188                 line_l += length;
    189                 if (line_l > mc) {
    190                     /* stop memory usage :-) */
    191                     break;
    192                 }
    193             }
    194             s = NULL;
     215            }
     216            store_param(s);
     217            dbg_msg("args[]:'%s'", s);
     218            s = p;
     219            n_max_arg--;
     220            if (n_max_arg == 0) {
     221                goto ret;
     222            }
    195223            state = NORM;
    196224        }
    197     }
    198     return list_arg;
     225        if (p == buf) {
     226            goto ret;
     227        }
     228    }
     229 ret:
     230    *p = '\0';
     231    /* store_param(NULL) - caller will do it */
     232    dbg_msg("return:'%s'", s);
     233    return s;
    199234}
    200235#else
    201236/* The variant does not support single quotes, double quotes or backslash */
    202 static xlist_t *process_stdin(xlist_t *list_arg,
    203         const char *eof_str, size_t mc, char *buf)
    204 {
    205 
    206     int c;                  /* current char */
    207     char eof_str_detected = 0;
    208     char *s = NULL;         /* start word */
    209     char *p = NULL;         /* pointer to end word */
    210     size_t line_l = 0;      /* size loaded args line */
    211     xlist_t *cur;
    212     xlist_t *prev;
    213 
    214     prev = cur = list_arg;
     237static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf)
     238{
     239    char *s = buf;             /* start of the word */
     240    char *p = s + strlen(buf); /* end of the word */
     241
     242    buf += n_max_chars;        /* past buffer's end */
     243
    215244    while (1) {
    216         if (!cur) break;
    217         prev = cur;
    218         line_l += cur->length;
    219         cur = cur->link;
    220     }
    221 
    222     while (!eof_stdin_detected) {
    223         c = getchar();
     245        int c = getchar();
    224246        if (c == EOF) {
    225             eof_stdin_detected = 1;
    226         }
    227         if (eof_str_detected)
    228             continue;
     247            if (p == s)
     248                goto ret;
     249        }
    229250        if (c == EOF || ISSPACE(c)) {
    230             if (s == NULL)
     251            if (p == s)
    231252                continue;
    232253            c = EOF;
    233254        }
    234         if (s == NULL)
    235             s = p = buf;
    236         if ((p - buf) >= mc)
    237             bb_error_msg_and_die("argument line too long");
    238255        *p++ = (c == EOF ? '\0' : c);
    239256        if (c == EOF) { /* word's delimiter or EOF detected */
    240             /* word loaded */
    241             if (eof_str) {
    242                 eof_str_detected = (strcmp(s, eof_str) == 0);
    243             }
    244             if (!eof_str_detected) {
    245                 size_t length = (p - buf);
    246                 /* Dont xzalloc - it can be quite big */
    247                 cur = xmalloc(offsetof(xlist_t, xstr) + length);
    248                 cur->link = NULL;
    249                 cur->length = length;
    250                 memcpy(cur->xstr, s, length);
    251                 if (prev == NULL) {
    252                     list_arg = cur;
    253                 } else {
    254                     prev->link = cur;
     257            /* A full word is loaded */
     258            if (G.eof_str) {
     259                if (strcmp(s, G.eof_str) == 0) {
     260                    while (getchar() != EOF)
     261                        continue;
     262                    p = s;
     263                    goto ret;
    255264                }
    256                 prev = cur;
    257                 line_l += length;
    258                 if (line_l > mc) {
    259                     /* stop memory usage :-) */
    260                     break;
    261                 }
    262                 s = NULL;
    263             }
    264         }
    265     }
    266     return list_arg;
     265            }
     266            store_param(s);
     267            dbg_msg("args[]:'%s'", s);
     268            s = p;
     269            n_max_arg--;
     270            if (n_max_arg == 0) {
     271                goto ret;
     272            }
     273        }
     274        if (p == buf) {
     275            goto ret;
     276        }
     277    }
     278 ret:
     279    *p = '\0';
     280    /* store_param(NULL) - caller will do it */
     281    dbg_msg("return:'%s'", s);
     282    return s;
    267283}
    268284#endif /* FEATURE_XARGS_SUPPORT_QUOTES */
    269285
     286#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
     287static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf)
     288{
     289    char *s = buf;             /* start of the word */
     290    char *p = s + strlen(buf); /* end of the word */
     291
     292    buf += n_max_chars;        /* past buffer's end */
     293
     294    while (1) {
     295        int c = getchar();
     296        if (c == EOF) {
     297            if (p == s)
     298                goto ret;
     299            c = '\0';
     300        }
     301        *p++ = c;
     302        if (c == '\0') {   /* word's delimiter or EOF detected */
     303            /* A full word is loaded */
     304            store_param(s);
     305            dbg_msg("args[]:'%s'", s);
     306            s = p;
     307            n_max_arg--;
     308            if (n_max_arg == 0) {
     309                goto ret;
     310            }
     311        }
     312        if (p == buf) {
     313            goto ret;
     314        }
     315    }
     316 ret:
     317    *p = '\0';
     318    /* store_param(NULL) - caller will do it */
     319    dbg_msg("return:'%s'", s);
     320    return s;
     321}
     322#endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
    270323
    271324#if ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION
     
    278331    int c, savec;
    279332
    280     tty_stream = xfopen(CURRENT_TTY, "r");
     333    tty_stream = xfopen_for_read(CURRENT_TTY);
    281334    fputs(" ?...", stderr);
    282     fflush(stderr);
     335    fflush_all();
    283336    c = savec = getc(tty_stream);
    284337    while (c != EOF && c != '\n')
     
    289342#else
    290343# define xargs_ask_confirmation() 1
    291 #endif /* FEATURE_XARGS_SUPPORT_CONFIRMATION */
    292 
    293 #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
    294 static xlist_t *process0_stdin(xlist_t *list_arg,
    295         const char *eof_str ATTRIBUTE_UNUSED, size_t mc, char *buf)
    296 {
    297     int c;                  /* current char */
    298     char *s = NULL;         /* start word */
    299     char *p = NULL;         /* pointer to end word */
    300     size_t line_l = 0;      /* size loaded args line */
    301     xlist_t *cur;
    302     xlist_t *prev;
    303 
    304     prev = cur = list_arg;
    305     while (1) {
    306         if (!cur) break;
    307         prev = cur;
    308         line_l += cur->length;
    309         cur = cur->link;
    310     }
    311 
    312     while (!eof_stdin_detected) {
    313         c = getchar();
    314         if (c == EOF) {
    315             eof_stdin_detected = 1;
    316             if (s == NULL)
    317                 break;
    318             c = '\0';
    319         }
    320         if (s == NULL)
    321             s = p = buf;
    322         if ((size_t)(p - buf) >= mc)
    323             bb_error_msg_and_die("argument line too long");
    324         *p++ = c;
    325         if (c == '\0') {   /* word's delimiter or EOF detected */
    326             /* word loaded */
    327             size_t length = (p - buf);
    328             /* Dont xzalloc - it can be quite big */
    329             cur = xmalloc(offsetof(xlist_t, xstr) + length);
    330             cur->link = NULL;
    331             cur->length = length;
    332             memcpy(cur->xstr, s, length);
    333             if (prev == NULL) {
    334                 list_arg = cur;
    335             } else {
    336                 prev->link = cur;
    337             }
    338             prev = cur;
    339             line_l += length;
    340             if (line_l > mc) {
    341                 /* stop memory usage :-) */
    342                 break;
    343             }
    344             s = NULL;
    345         }
    346     }
    347     return list_arg;
    348 }
    349 #endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */
     344#endif
     345
     346//usage:#define xargs_trivial_usage
     347//usage:       "[OPTIONS] [PROG ARGS]"
     348//usage:#define xargs_full_usage "\n\n"
     349//usage:       "Run PROG on every item given by stdin\n"
     350//usage:     "\nOptions:"
     351//usage:    IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(
     352//usage:     "\n    -p  Ask user whether to run each command"
     353//usage:    )
     354//usage:     "\n    -r  Don't run command if input is empty"
     355//usage:    IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(
     356//usage:     "\n    -0  Input is separated by NUL characters"
     357//usage:    )
     358//usage:     "\n    -t  Print the command on stderr before execution"
     359//usage:     "\n    -e[STR] STR stops input processing"
     360//usage:     "\n    -n N    Pass no more than N args to PROG"
     361//usage:     "\n    -s N    Pass command line of no more than N bytes"
     362//usage:    IF_FEATURE_XARGS_SUPPORT_TERMOPT(
     363//usage:     "\n    -x  Exit if size is exceeded"
     364//usage:    )
     365//usage:#define xargs_example_usage
     366//usage:       "$ ls | xargs gzip\n"
     367//usage:       "$ find . -name '*.c' -print | xargs rm\n"
    350368
    351369/* Correct regardless of combination of CONFIG_xxx */
     
    356374    OPTBIT_UPTO_SIZE,
    357375    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,
     376    OPTBIT_EOF_STRING1,
     377    IF_FEATURE_XARGS_SUPPORT_CONFIRMATION(OPTBIT_INTERACTIVE,)
     378    IF_FEATURE_XARGS_SUPPORT_TERMOPT(     OPTBIT_TERMINATE  ,)
     379    IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   OPTBIT_ZEROTERM   ,)
     380
     381    OPT_VERBOSE     = 1 << OPTBIT_VERBOSE    ,
     382    OPT_NO_EMPTY    = 1 << OPTBIT_NO_EMPTY   ,
     383    OPT_UPTO_NUMBER = 1 << OPTBIT_UPTO_NUMBER,
     384    OPT_UPTO_SIZE   = 1 << OPTBIT_UPTO_SIZE  ,
     385    OPT_EOF_STRING  = 1 << OPTBIT_EOF_STRING , /* GNU: -e[<param>] */
     386    OPT_EOF_STRING1 = 1 << OPTBIT_EOF_STRING1, /* SUS: -E<param> */
     387    OPT_INTERACTIVE = IF_FEATURE_XARGS_SUPPORT_CONFIRMATION((1 << OPTBIT_INTERACTIVE)) + 0,
     388    OPT_TERMINATE   = IF_FEATURE_XARGS_SUPPORT_TERMOPT(     (1 << OPTBIT_TERMINATE  )) + 0,
     389    OPT_ZEROTERM    = IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   (1 << OPTBIT_ZEROTERM   )) + 0,
    370390};
    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 
    376 int xargs_main(int argc, char **argv);
     391#define OPTION_STR "+trn:s:e::E:" \
     392    IF_FEATURE_XARGS_SUPPORT_CONFIRMATION("p") \
     393    IF_FEATURE_XARGS_SUPPORT_TERMOPT(     "x") \
     394    IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(   "0")
     395
     396int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
    377397int xargs_main(int argc, char **argv)
    378398{
    379     char **args;
    380     int i, n;
    381     xlist_t *list = NULL;
    382     xlist_t *cur;
     399    int i;
    383400    int child_error = 0;
    384     char *max_args, *max_chars;
     401    char *max_args;
     402    char *max_chars;
     403    char *buf;
     404    unsigned opt;
     405    int n_max_chars;
    385406    int n_max_arg;
    386     size_t n_chars = 0;
    387     long orig_arg_max;
    388     const char *eof_str = "_";
    389     unsigned opt;
    390     size_t n_max_chars;
    391407#if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM
    392     xlist_t* (*read_args)(xlist_t*, const char*, size_t, char*) = process_stdin;
     408    char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin;
    393409#else
    394410#define read_args process_stdin
    395411#endif
    396412
    397     opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &eof_str);
     413    INIT_G();
     414
     415    G.eof_str = NULL;
     416    opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str);
     417
     418    /* -E ""? You may wonder why not just omit -E?
     419     * This is used for portability:
     420     * old xargs was using "_" as default for -E / -e */
     421    if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0')
     422        G.eof_str = NULL;
    398423
    399424    if (opt & OPT_ZEROTERM)
    400         USE_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin);
     425        IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin);
    401426
    402427    argv += optind;
    403428    argc -= optind;
    404     if (!argc) {
     429    if (!argv[0]) {
    405430        /* default behavior is to echo all the filenames */
    406         *argv = (char*)"echo";
     431        *--argv = (char*)"echo";
    407432        argc++;
    408433    }
    409434
    410     orig_arg_max = ARG_MAX;
    411     if (orig_arg_max == -1)
    412         orig_arg_max = LONG_MAX;
    413     orig_arg_max -= 2048;   /* POSIX.2 requires subtracting 2048 */
    414 
     435    /* -s NUM default. fileutils-4.4.2 uses 128k, but I heasitate
     436     * to use such a big value - first need to change code to use
     437     * growable buffer instead of fixed one.
     438     */
     439    n_max_chars = 32 * 1024;
     440    /* Make smaller if system does not allow our default value.
     441     * The Open Group Base Specifications Issue 6:
     442     * "The xargs utility shall limit the command line length such that
     443     * when the command line is invoked, the combined argument
     444     * and environment lists (see the exec family of functions
     445     * in the System Interfaces volume of IEEE Std 1003.1-2001)
     446     * shall not exceed {ARG_MAX}-2048 bytes".
     447     */
     448    {
     449        long arg_max = 0;
     450#if defined _SC_ARG_MAX
     451        arg_max = sysconf(_SC_ARG_MAX) - 2048;
     452#elif defined ARG_MAX
     453        arg_max = ARG_MAX - 2048;
     454#endif
     455        if (arg_max > 0 && n_max_chars > arg_max)
     456            n_max_chars = arg_max;
     457    }
    415458    if (opt & OPT_UPTO_SIZE) {
    416         n_max_chars = xatoul_range(max_chars, 1, orig_arg_max);
    417         for (i = 0; i < argc; i++) {
    418             n_chars += strlen(*argv) + 1;
    419         }
    420         if (n_max_chars < n_chars) {
    421             bb_error_msg_and_die("cannot fit single argument within argument list size limit");
     459        n_max_chars = xatou_range(max_chars, 1, INT_MAX);
     460    }
     461    /* Account for prepended fixed arguments */
     462    {
     463        size_t n_chars = 0;
     464        for (i = 0; argv[i]; i++) {
     465            n_chars += strlen(argv[i]) + 1;
    422466        }
    423467        n_max_chars -= n_chars;
    424     } else {
    425         /* Sanity check for systems with huge ARG_MAX defines (e.g., Suns which
    426            have it at 1 meg).  Things will work fine with a large ARG_MAX but it
    427            will probably hurt the system more than it needs to; an array of this
    428            size is allocated.  */
    429         if (orig_arg_max > 20 * 1024)
    430             orig_arg_max = 20 * 1024;
    431         n_max_chars = orig_arg_max;
    432     }
    433     max_chars = xmalloc(n_max_chars);
    434 
     468    }
     469    /* Sanity check */
     470    if (n_max_chars <= 0) {
     471        bb_error_msg_and_die("can't fit single argument within argument list size limit");
     472    }
     473
     474    buf = xzalloc(n_max_chars + 1);
     475
     476    n_max_arg = n_max_chars;
    435477    if (opt & OPT_UPTO_NUMBER) {
    436         n_max_arg = xatoul_range(max_args, 1, INT_MAX);
    437     } else {
    438         n_max_arg = n_max_chars;
    439     }
    440 
    441     while ((list = read_args(list, eof_str, n_max_chars, max_chars)) != NULL ||
    442         !(opt & OPT_NO_EMPTY))
    443     {
     478        n_max_arg = xatou_range(max_args, 1, INT_MAX);
     479        /* Not necessary, we use growable args[]: */
     480        /* if (n_max_arg > n_max_chars) n_max_arg = n_max_chars */
     481    }
     482
     483    /* Allocate pointers for execvp */
     484    /* We can statically allocate (argc + n_max_arg + 1) elements
     485     * and do not bother with resizing args[], but on 64-bit machines
     486     * this results in args[] vector which is ~8 times bigger
     487     * than n_max_chars! That is, with n_max_chars == 20k,
     488     * args[] will take 160k (!), which will most likely be
     489     * almost entirely unused.
     490     */
     491    /* See store_param() for matching 256-step growth logic */
     492    G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff));
     493
     494    /* Store the command to be executed, part 1 */
     495    for (i = 0; argv[i]; i++)
     496        G.args[i] = argv[i];
     497
     498    while (1) {
     499        char *rem;
     500
     501        G.idx = argc;
     502        rem = read_args(n_max_chars, n_max_arg, buf);
     503        store_param(NULL);
     504
     505        if (!G.args[argc]) {
     506            if (*rem != '\0')
     507                bb_error_msg_and_die("argument line too long");
     508            if (opt & OPT_NO_EMPTY)
     509                break;
     510        }
    444511        opt |= OPT_NO_EMPTY;
    445         n = 0;
    446         n_chars = 0;
    447 #if ENABLE_FEATURE_XARGS_SUPPORT_TERMOPT
    448         for (cur = list; cur;) {
    449             n_chars += cur->length;
    450             n++;
    451             cur = cur->link;
    452             if (n_chars > n_max_chars || (n == n_max_arg && cur)) {
    453                 if (opt & OPT_TERMINATE)
    454                     bb_error_msg_and_die("argument list too long");
    455                 break;
    456             }
    457         }
    458 #else
    459         for (cur = list; cur; cur = cur->link) {
    460             n_chars += cur->length;
    461             n++;
    462             if (n_chars > n_max_chars || n == n_max_arg) {
    463                 break;
    464             }
    465         }
    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
    473            (taken from the command line) */
    474         for (i = 0; i < argc; i++)
    475             args[i] = argv[i];
    476         /* (taken from stdin) */
    477         for (cur = list; n; cur = cur->link) {
    478             args[i++] = cur->xstr;
    479             n--;
    480         }
    481512
    482513        if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) {
     514            const char *fmt = " %s" + 1;
     515            char **args = G.args;
    483516            for (i = 0; args[i]; i++) {
    484                 if (i)
    485                     fputc(' ', stderr);
    486                 fputs(args[i], stderr);
     517                fprintf(stderr, fmt, args[i]);
     518                fmt = " %s";
    487519            }
    488520            if (!(opt & OPT_INTERACTIVE))
    489                 fputc('\n', stderr);
    490         }
     521                bb_putchar_stderr('\n');
     522        }
     523
    491524        if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
    492             child_error = xargs_exec(args);
    493         }
    494 
    495         /* clean up */
    496         for (i = argc; args[i]; i++) {
    497             cur = list;
    498             list = list->link;
    499             free(cur);
    500         }
    501         free(args);
     525            child_error = xargs_exec();
     526        }
     527
    502528        if (child_error > 0 && child_error != 123) {
    503529            break;
    504530        }
    505     }
    506     if (ENABLE_FEATURE_CLEAN_UP)
    507         free(max_chars);
     531
     532        overlapping_strcpy(buf, rem);
     533    } /* while */
     534
     535    if (ENABLE_FEATURE_CLEAN_UP) {
     536        free(G.args);
     537        free(buf);
     538    }
     539
    508540    return child_error;
    509541}
     
    518550    fprintf(stderr, "Usage: %s [-p] [-r] [-t] -[x] [-n max_arg] [-s max_chars]\n",
    519551        applet_name);
    520     exit(1);
     552    exit(EXIT_FAILURE);
    521553}
    522554
Note: See TracChangeset for help on using the changeset viewer.