X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=findutils%2Ffind.c;h=edb8482d80d7dccf8550d0ae5f5a4f826784feb8;hb=e5667c1cfe339b2f84abaabb90259ec29b91cf89;hp=603c20643f56b363abec010a5bdb3bcab25bcec5;hpb=72d1a2357d2168f241458e4d6cebb7589ac82f4f;p=oweals%2Fbusybox.git diff --git a/findutils/find.c b/findutils/find.c index 603c20643..edb8482d8 100644 --- a/findutils/find.c +++ b/findutils/find.c @@ -7,346 +7,562 @@ * Reworked by David Douthitt and * Matt Kraai . * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. + * Licensed under the GPL version 2, see the file LICENSE in this tarball. + */ + +/* findutils-4.1.20: * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * # find file.txt -exec 'echo {}' '{} {}' ';' + * find: echo file.txt: No such file or directory + * # find file.txt -exec 'echo' '{} {}' '; ' + * find: missing argument to `-exec' + * # find file.txt -exec 'echo {}' '{} {}' ';' junk + * find: paths must precede expression + * # find file.txt -exec 'echo {}' '{} {}' ';' junk ';' + * find: paths must precede expression + * # find file.txt -exec 'echo' '{} {}' ';' + * file.txt file.txt + * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ])) + * # find file.txt -exec 'echo' '{} {}' ';' -print -exec pwd ';' + * file.txt file.txt + * file.txt + * /tmp + * # find -name '*.c' -o -name '*.h' + * [shows files, *.c and *.h intermixed] + * # find file.txt -name '*f*' -o -name '*t*' + * file.txt + * # find file.txt -name '*z*' -o -name '*t*' + * file.txt + * # find file.txt -name '*f*' -o -name '*z*' + * file.txt * + * # find t z -name '*t*' -print -o -name '*z*' + * t + * # find t z t z -name '*t*' -o -name '*z*' -print + * z + * z + * # find t z t z '(' -name '*t*' -o -name '*z*' ')' -o -print + * (no output) */ -#include -#include -#include -#include -#include -#include -#include -#include #include "busybox.h" +#include -//XXX just found out about libbb/messages.c . maybe move stuff there ? - ghoz -static const char msg_req_arg[] = "option `%s' requires an argument"; -static const char msg_invalid_arg[] = "invalid argument `%s' to `%s'"; +USE_FEATURE_FIND_XDEV(static dev_t *xdev_dev;) +USE_FEATURE_FIND_XDEV(static int xdev_count;) -static char *pattern; +typedef int (*action_fp)(const char *fileName, struct stat *statbuf, void *); -#ifdef CONFIG_FEATURE_FIND_TYPE -static int type_mask = 0; -#endif +typedef struct { + action_fp f; +} action; +#define ACTS(name, arg...) typedef struct { action a; arg; } action_##name; +#define ACTF(name) static int func_##name(const char *fileName, struct stat *statbuf, action_##name* ap) + ACTS(print) + ACTS(name, char *pattern;) +USE_FEATURE_FIND_PRINT0(ACTS(print0)) +USE_FEATURE_FIND_TYPE( ACTS(type, int type_mask;)) +USE_FEATURE_FIND_PERM( ACTS(perm, char perm_char; int perm_mask;)) +USE_FEATURE_FIND_MTIME( ACTS(mtime, char mtime_char; int mtime_days;)) +USE_FEATURE_FIND_MMIN( ACTS(mmin, char mmin_char; int mmin_mins;)) +USE_FEATURE_FIND_NEWER( ACTS(newer, time_t newer_mtime;)) +USE_FEATURE_FIND_INUM( ACTS(inum, ino_t inode_num;)) +USE_FEATURE_FIND_EXEC( ACTS(exec, char **exec_argv; int *subst_count; int exec_argc;)) +USE_DESKTOP( ACTS(paren, action ***subexpr;)) +USE_DESKTOP( ACTS(prune)) -#ifdef CONFIG_FEATURE_FIND_PERM -static char perm_char = 0; -static int perm_mask = 0; -#endif +static action ***actions; +static int need_print = 1; -#ifdef CONFIG_FEATURE_FIND_MTIME -static char mtime_char; -static int mtime_days; -#endif +static inline int one_char(const char* str, char c) +{ + return (str[0] == c && str[1] == '\0'); +} -#ifdef CONFIG_FEATURE_FIND_MMIN -static char mmin_char; -static int mmin_mins; -#endif -#ifdef CONFIG_FEATURE_FIND_XDEV -static dev_t *xdev_dev; -static int xdev_count = 0; -#endif +static int count_subst(const char *str) +{ + int count = 0; + while ((str = strstr(str, "{}"))) { + count++; + str++; + } + return count; +} -#ifdef CONFIG_FEATURE_FIND_NEWER -static time_t newer_mtime; -#endif -#ifdef CONFIG_FEATURE_FIND_INUM -static ino_t inode_num; -#endif +static char* subst(const char *src, int count, const char* filename) +{ + char *buf, *dst, *end; + int flen = strlen(filename); + /* we replace each '{}' with filename: growth by strlen-2 */ + buf = dst = xmalloc(strlen(src) + count*(flen-2) + 1); + while ((end = strstr(src, "{}"))) { + memcpy(dst, src, end - src); + dst += end - src; + src = end + 2; + memcpy(dst, filename, flen); + dst += flen; + } + strcpy(dst, src); + return buf; +} -#ifdef CONFIG_FEATURE_FIND_EXEC -static char **exec_str; -static int num_matches; -static int exec_opt; -#endif -static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +static int exec_actions(action ***appp, const char *fileName, struct stat *statbuf) { - if (pattern != NULL) { - const char *tmp = strrchr(fileName, '/'); + int cur_group; + int cur_action; + int rc = TRUE; + action **app, *ap; - if (tmp == NULL) - tmp = fileName; - else - tmp++; - if (!(fnmatch(pattern, tmp, FNM_PERIOD) == 0)) - goto no_match; - } -#ifdef CONFIG_FEATURE_FIND_TYPE - if (type_mask != 0) { - if (!((statbuf->st_mode & S_IFMT) == type_mask)) - goto no_match; + cur_group = -1; + while ((app = appp[++cur_group])) { + cur_action = -1; + do { + ap = app[++cur_action]; + } while (ap && (rc = ap->f(fileName, statbuf, ap))); + if (!ap) { + /* all actions in group were successful */ + break; + } } + return rc; +} + + +ACTF(name) +{ + const char *tmp = strrchr(fileName, '/'); + if (tmp == NULL) + tmp = fileName; + else + tmp++; + return fnmatch(ap->pattern, tmp, FNM_PERIOD) == 0; +} +#if ENABLE_FEATURE_FIND_TYPE +ACTF(type) +{ + return ((statbuf->st_mode & S_IFMT) == ap->type_mask); +} #endif -#ifdef CONFIG_FEATURE_FIND_PERM - if (perm_mask != 0) { - if (!((isdigit(perm_char) && (statbuf->st_mode & 07777) == perm_mask) || - (perm_char == '-' && (statbuf->st_mode & perm_mask) == perm_mask) || - (perm_char == '+' && (statbuf->st_mode & perm_mask) != 0))) - goto no_match; - } +#if ENABLE_FEATURE_FIND_PERM +ACTF(perm) +{ + return !((isdigit(ap->perm_char) && (statbuf->st_mode & 07777) == ap->perm_mask) + || (ap->perm_char == '-' && (statbuf->st_mode & ap->perm_mask) == ap->perm_mask) + || (ap->perm_char == '+' && (statbuf->st_mode & ap->perm_mask) != 0)); +} #endif -#ifdef CONFIG_FEATURE_FIND_MTIME - if (mtime_char != 0) { - time_t file_age = time(NULL) - statbuf->st_mtime; - time_t mtime_secs = mtime_days * 24 * 60 * 60; - if (!((isdigit(mtime_char) && file_age >= mtime_secs && - file_age < mtime_secs + 24 * 60 * 60) || - (mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) || - (mtime_char == '-' && file_age < mtime_secs))) - goto no_match; - } +#if ENABLE_FEATURE_FIND_MTIME +ACTF(mtime) +{ + time_t file_age = time(NULL) - statbuf->st_mtime; + time_t mtime_secs = ap->mtime_days * 24 * 60 * 60; + return !((isdigit(ap->mtime_char) && file_age >= mtime_secs + && file_age < mtime_secs + 24 * 60 * 60) + || (ap->mtime_char == '+' && file_age >= mtime_secs + 24 * 60 * 60) + || (ap->mtime_char == '-' && file_age < mtime_secs)); +} #endif -#ifdef CONFIG_FEATURE_FIND_MMIN - if (mmin_char != 0) { - time_t file_age = time(NULL) - statbuf->st_mtime; - time_t mmin_secs = mmin_mins * 60; - if (!((isdigit(mmin_char) && file_age >= mmin_secs && - file_age < mmin_secs + 60) || - (mmin_char == '+' && file_age >= mmin_secs + 60) || - (mmin_char == '-' && file_age < mmin_secs))) - goto no_match; - } +#if ENABLE_FEATURE_FIND_MMIN +ACTF(mmin) +{ + time_t file_age = time(NULL) - statbuf->st_mtime; + time_t mmin_secs = ap->mmin_mins * 60; + return !((isdigit(ap->mmin_char) && file_age >= mmin_secs + && file_age < mmin_secs + 60) + || (ap->mmin_char == '+' && file_age >= mmin_secs + 60) + || (ap->mmin_char == '-' && file_age < mmin_secs)); +} #endif -#ifdef CONFIG_FEATURE_FIND_XDEV - if (xdev_count) { - int i; - for (i=0; i st_dev) - break; - } - if (i == xdev_count) { - if(S_ISDIR(statbuf->st_mode)) - return SKIP; - else - goto no_match; - } - } +#if ENABLE_FEATURE_FIND_NEWER +ACTF(newer) +{ + return (ap->newer_mtime >= statbuf->st_mtime); +} #endif -#ifdef CONFIG_FEATURE_FIND_NEWER - if (newer_mtime != 0) { - time_t file_age = newer_mtime - statbuf->st_mtime; - if (file_age >= 0) - goto no_match; - } +#if ENABLE_FEATURE_FIND_INUM +ACTF(inum) +{ + return (statbuf->st_ino != ap->inode_num); +} #endif -#ifdef CONFIG_FEATURE_FIND_INUM - if (inode_num != 0) { - if (!(statbuf->st_ino == inode_num)) - goto no_match; - } +#if ENABLE_FEATURE_FIND_EXEC +ACTF(exec) +{ + int i, rc; + char *argv[ap->exec_argc+1]; + for (i = 0; i < ap->exec_argc; i++) + argv[i] = subst(ap->exec_argv[i], ap->subst_count[i], fileName); + argv[i] = NULL; /* terminate the list */ + errno = 0; + rc = wait4pid(spawn(argv)); + if (errno) + bb_perror_msg("%s", argv[0]); + for (i = 0; i < ap->exec_argc; i++) + free(argv[i]); + return rc == 0; /* return 1 if success */ +} +#endif + +#if ENABLE_FEATURE_FIND_PRINT0 +ACTF(print0) +{ + printf("%s%c", fileName, '\0'); + return TRUE; +} #endif -#ifdef CONFIG_FEATURE_FIND_EXEC - if (exec_opt) { + +ACTF(print) +{ + puts(fileName); + return TRUE; +} + +#if ENABLE_DESKTOP +ACTF(paren) +{ + return exec_actions(ap->subexpr, fileName, statbuf); +} +/* + * -prune: if -depth is not given, return true and do not descend + * current dir; if -depth is given, return false with no effect. + * Example: + * find dir -name 'asm-*' -prune -o -name '*.[chS]' -print + */ +ACTF(prune) +{ + return SKIP; +} +#endif + + +static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth) +{ + int rc; +#ifdef CONFIG_FEATURE_FIND_XDEV + if (S_ISDIR(statbuf->st_mode) && xdev_count) { int i; - char *cmd_string = ""; - for (i = 0; i < num_matches; i++) - cmd_string = bb_xasprintf("%s%s%s", cmd_string, exec_str[i], fileName); - cmd_string = bb_xasprintf("%s%s", cmd_string, exec_str[num_matches]); - system(cmd_string); - goto no_match; + for (i = 0; i < xdev_count; i++) { + if (xdev_dev[i] != statbuf->st_dev) + return SKIP; + } } #endif - - puts(fileName); -no_match: - return (TRUE); + rc = exec_actions(actions, fileName, statbuf); + /* Had no explicit -print[0] or -exec? then print */ + if (rc && need_print) + puts(fileName); + /* Cannot return 0: our caller, recursive_action(), + * will perror() and skip dirs (if called on dir) */ + return rc == 0 ? TRUE : rc; } -#ifdef CONFIG_FEATURE_FIND_TYPE + +#if ENABLE_FEATURE_FIND_TYPE static int find_type(char *type) { int mask = 0; switch (type[0]) { - case 'b': - mask = S_IFBLK; - break; - case 'c': - mask = S_IFCHR; - break; - case 'd': - mask = S_IFDIR; - break; - case 'p': - mask = S_IFIFO; - break; - case 'f': - mask = S_IFREG; - break; - case 'l': - mask = S_IFLNK; - break; - case 's': - mask = S_IFSOCK; - break; + case 'b': + mask = S_IFBLK; + break; + case 'c': + mask = S_IFCHR; + break; + case 'd': + mask = S_IFDIR; + break; + case 'p': + mask = S_IFIFO; + break; + case 'f': + mask = S_IFREG; + break; + case 'l': + mask = S_IFLNK; + break; + case 's': + mask = S_IFSOCK; + break; } if (mask == 0 || type[1] != '\0') - bb_error_msg_and_die(msg_invalid_arg, type, "-type"); + bb_error_msg_and_die(bb_msg_invalid_arg, type, "-type"); return mask; } #endif -int find_main(int argc, char **argv) +action*** parse_params(char **argv) { - int dereference = FALSE; - int i, firstopt, status = EXIT_SUCCESS; + action*** appp; + int cur_group = 0; + int cur_action = 0; - for (firstopt = 1; firstopt < argc; firstopt++) { - if (argv[firstopt][0] == '-') - break; + action* alloc_action(int sizeof_struct, action_fp f) + { + action *ap; + appp[cur_group] = xrealloc(appp[cur_group], (cur_action+2) * sizeof(*appp)); + appp[cur_group][cur_action++] = ap = xmalloc(sizeof_struct); + appp[cur_group][cur_action] = NULL; + ap->f = f; + return ap; } +#define ALLOC_ACTION(name) (action_##name*)alloc_action(sizeof(action_##name), (action_fp) func_##name) - /* Parse any options */ - for (i = firstopt; i < argc; i++) { - if (strcmp(argv[i], "-follow") == 0) - dereference = TRUE; - else if (strcmp(argv[i], "-print") == 0) { - ; - } - else if (strcmp(argv[i], "-name") == 0) { - if (++i == argc) - bb_error_msg_and_die(msg_req_arg, "-name"); - pattern = argv[i]; -#ifdef CONFIG_FEATURE_FIND_TYPE - } else if (strcmp(argv[i], "-type") == 0) { - if (++i == argc) - bb_error_msg_and_die(msg_req_arg, "-type"); - type_mask = find_type(argv[i]); + appp = xzalloc(2 * sizeof(*appp)); /* appp[0],[1] == NULL */ + +// Actions have side effects and return a true or false value +// We implement: -print, -print0, -exec + +// The rest are tests. + +// Tests and actions are grouped by operators +// ( expr ) Force precedence +// ! expr True if expr is false +// -not expr Same as ! expr +// expr1 [-a[nd]] expr2 And; expr2 is not evaluated if expr1 is false +// expr1 -o[r] expr2 Or; expr2 is not evaluated if expr1 is true +// expr1 , expr2 List; both expr1 and expr2 are always evaluated +// We implement: (), -a, -o + + while (*argv) { + char *arg = argv[0]; + char *arg1 = argv[1]; + /* --- Operators --- */ + if (strcmp(arg, "-a") == 0 + USE_DESKTOP(|| strcmp(arg, "-and") == 0) + ) { + /* no special handling required */ + } + else if (strcmp(arg, "-o") == 0 + USE_DESKTOP(|| strcmp(arg, "-or") == 0) + ) { + /* start new OR group */ + cur_group++; + appp = xrealloc(appp, (cur_group+2) * sizeof(*appp)); + appp[cur_group] = NULL; + appp[cur_group+1] = NULL; + cur_action = 0; + } + + /* --- Tests and actions --- */ + else if (strcmp(arg, "-print") == 0) { + need_print = 0; + (void) ALLOC_ACTION(print); + } +#if ENABLE_FEATURE_FIND_PRINT0 + else if (strcmp(arg, "-print0") == 0) { + need_print = 0; + (void) ALLOC_ACTION(print0); + } #endif -#ifdef CONFIG_FEATURE_FIND_PERM - } else if (strcmp(argv[i], "-perm") == 0) { - char *end; - if (++i == argc) - bb_error_msg_and_die(msg_req_arg, "-perm"); - perm_mask = strtol(argv[i], &end, 8); - if ((end[0] != '\0') || (perm_mask > 07777)) - bb_error_msg_and_die(msg_invalid_arg, argv[i], "-perm"); - if ((perm_char = argv[i][0]) == '-') - perm_mask = -perm_mask; + else if (strcmp(arg, "-name") == 0) { + action_name *ap; + if (!*++argv) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ap = ALLOC_ACTION(name); + ap->pattern = arg1; + } +#if ENABLE_FEATURE_FIND_TYPE + else if (strcmp(arg, "-type") == 0) { + action_type *ap; + if (!*++argv) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ap = ALLOC_ACTION(type); + ap->type_mask = find_type(arg1); + } #endif -#ifdef CONFIG_FEATURE_FIND_MTIME - } else if (strcmp(argv[i], "-mtime") == 0) { - char *end; - if (++i == argc) - bb_error_msg_and_die(msg_req_arg, "-mtime"); - mtime_days = strtol(argv[i], &end, 10); - if (end[0] != '\0') - bb_error_msg_and_die(msg_invalid_arg, argv[i], "-mtime"); - if ((mtime_char = argv[i][0]) == '-') - mtime_days = -mtime_days; +#if ENABLE_FEATURE_FIND_PERM +/* TODO: + * -perm mode File's permission bits are exactly mode (octal or symbolic). + * Symbolic modes use mode 0 as a point of departure. + * -perm -mode All of the permission bits mode are set for the file. + * -perm +mode Any of the permission bits mode are set for the file. + */ + else if (strcmp(arg, "-perm") == 0) { + action_perm *ap; + if (!*++argv) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ap = ALLOC_ACTION(perm); + ap->perm_mask = xstrtol_range(arg1, 8, 0, 07777); + ap->perm_char = arg1[0]; + if (ap->perm_char == '-') + ap->perm_mask = -ap->perm_mask; + } #endif -#ifdef CONFIG_FEATURE_FIND_MMIN - } else if (strcmp(argv[i], "-mmin") == 0) { - char *end; - if (++i == argc) - bb_error_msg_and_die(msg_req_arg, "-mmin"); - mmin_mins = strtol(argv[i], &end, 10); - if (end[0] != '\0') - bb_error_msg_and_die(msg_invalid_arg, argv[i], "-mmin"); - if ((mmin_char = argv[i][0]) == '-') - mmin_mins = -mmin_mins; +#if ENABLE_FEATURE_FIND_MTIME + else if (strcmp(arg, "-mtime") == 0) { + action_mtime *ap; + if (!*++argv) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ap = ALLOC_ACTION(mtime); + ap->mtime_days = xatol(arg1); + ap->mtime_char = arg1[0]; + if (ap->mtime_char == '-') + ap->mtime_days = -ap->mtime_days; + } #endif -#ifdef CONFIG_FEATURE_FIND_XDEV - } else if (strcmp(argv[i], "-xdev") == 0) { - struct stat stbuf; - - xdev_count = ( firstopt - 1 ) ? ( firstopt - 1 ) : 1; - xdev_dev = xmalloc ( xdev_count * sizeof( dev_t )); - - if ( firstopt == 1 ) { - if ( stat ( ".", &stbuf ) < 0 ) - bb_error_msg_and_die("could not stat '.'" ); - xdev_dev [0] = stbuf. st_dev; - } - else { - - for (i = 1; i < firstopt; i++) { - if ( stat ( argv [i], &stbuf ) < 0 ) - bb_error_msg_and_die("could not stat '%s'", argv [i] ); - xdev_dev [i-1] = stbuf. st_dev; - } - } +#if ENABLE_FEATURE_FIND_MMIN + else if (strcmp(arg, "-mmin") == 0) { + action_mmin *ap; + if (!*++argv) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ap = ALLOC_ACTION(mmin); + ap->mmin_mins = xatol(arg1); + ap->mmin_char = arg1[0]; + if (ap->mmin_char == '-') + ap->mmin_mins = -ap->mmin_mins; + } #endif -#ifdef CONFIG_FEATURE_FIND_NEWER - } else if (strcmp(argv[i], "-newer") == 0) { +#if ENABLE_FEATURE_FIND_NEWER + else if (strcmp(arg, "-newer") == 0) { + action_newer *ap; struct stat stat_newer; - if (++i == argc) - bb_error_msg_and_die(msg_req_arg, "-newer"); - if (stat (argv[i], &stat_newer) != 0) - bb_error_msg_and_die("file %s not found", argv[i]); - newer_mtime = stat_newer.st_mtime; + if (!*++argv) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + xstat(arg1, &stat_newer); + ap = ALLOC_ACTION(newer); + ap->newer_mtime = stat_newer.st_mtime; + } #endif -#ifdef CONFIG_FEATURE_FIND_INUM - } else if (strcmp(argv[i], "-inum") == 0) { - char *end; - if (++i == argc) - bb_error_msg_and_die(msg_req_arg, "-inum"); - inode_num = strtol(argv[i], &end, 10); - if (end[0] != '\0') - bb_error_msg_and_die(msg_invalid_arg, argv[i], "-inum"); +#if ENABLE_FEATURE_FIND_INUM + else if (strcmp(arg, "-inum") == 0) { + action_inum *ap; + if (!*++argv) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ap = ALLOC_ACTION(inum); + ap->inode_num = xatoul(arg1); + } #endif -#ifdef CONFIG_FEATURE_FIND_EXEC - } else if (strcmp(argv[i], "-exec") == 0) { - int b_pos; - char *cmd_string = ""; - - while (i++) { - if (i == argc) - bb_error_msg_and_die(msg_req_arg, "-exec"); - if (*argv[i] == ';') +#if ENABLE_FEATURE_FIND_EXEC + else if (strcmp(arg, "-exec") == 0) { + int i; + action_exec *ap; + need_print = 0; + ap = ALLOC_ACTION(exec); + ap->exec_argv = ++argv; /* first arg after -exec */ + ap->exec_argc = 0; + while (1) { + if (!*argv) /* did not see ';' till end */ + bb_error_msg_and_die(bb_msg_requires_arg, arg); + if (one_char(argv[0], ';')) break; - cmd_string = bb_xasprintf("%s %s", cmd_string, argv[i]); + argv++; + ap->exec_argc++; } - - if (*cmd_string == 0) - bb_error_msg_and_die(msg_req_arg, "-exec"); - cmd_string++; - exec_str = xmalloc(sizeof(char *)); - - while ((b_pos = strstr(cmd_string, "{}") - cmd_string), (b_pos >= 0)) { - num_matches++; - exec_str = xrealloc(exec_str, (num_matches + 1) * sizeof(char *)); - exec_str[num_matches - 1] = bb_xstrndup(cmd_string, b_pos); - cmd_string += b_pos + 2; + if (ap->exec_argc == 0) + bb_error_msg_and_die(bb_msg_requires_arg, arg); + ap->subst_count = xmalloc(ap->exec_argc * sizeof(int)); + i = ap->exec_argc; + while (i--) + ap->subst_count[i] = count_subst(ap->exec_argv[i]); + } +#endif +#if ENABLE_DESKTOP + else if (one_char(arg, '(')) { + action_paren *ap; + char **endarg; + int nested = 1; + + endarg = argv; + while (1) { + if (!*++endarg) + bb_error_msg_and_die("unpaired '('"); + if (one_char(*endarg, '(')) + nested++; + else if (one_char(*endarg, ')') && !--nested) { + *endarg = NULL; + break; + } } - exec_str[num_matches] = bb_xstrdup(cmd_string); - exec_opt = 1; + ap = ALLOC_ACTION(paren); + ap->subexpr = parse_params(argv + 1); + *endarg = ")"; /* restore NULLed parameter */ + argv = endarg; + } + else if (strcmp(arg, "-prune") == 0) { + (void) ALLOC_ACTION(prune); + } #endif - } else + else bb_show_usage(); + argv++; } + return appp; +#undef ALLOC_ACTION +} + + +int find_main(int argc, char **argv) +{ + int dereference = FALSE; + char *arg; + char **argp; + int i, firstopt, status = EXIT_SUCCESS; + + for (firstopt = 1; firstopt < argc; firstopt++) { + if (argv[firstopt][0] == '-') + break; +#if ENABLE_DESKTOP + if (one_char(argv[firstopt], '(')) + break; +#endif + } if (firstopt == 1) { - if (! recursive_action(".", TRUE, dereference, FALSE, fileAction, - fileAction, NULL)) - status = EXIT_FAILURE; - } else { - for (i = 1; i < firstopt; i++) { - if (! recursive_action(argv[i], TRUE, dereference, FALSE, fileAction, - fileAction, NULL)) - status = EXIT_FAILURE; + argv[0] = "."; + argv--; + firstopt++; + } + +// All options always return true. They always take effect, +// rather than being processed only when their place in the +// expression is reached +// We implement: -follow, -xdev + + /* Process options, and replace then with -a */ + /* (-a will be ignored by recursive parser later) */ + argp = &argv[firstopt]; + while ((arg = argp[0])) { + if (strcmp(arg, "-follow") == 0) { + dereference = TRUE; + argp[0] = "-a"; } +#if ENABLE_FEATURE_FIND_XDEV + else if (strcmp(arg, "-xdev") == 0) { + struct stat stbuf; + if (!xdev_count) { + xdev_count = firstopt - 1; + xdev_dev = xmalloc(xdev_count * sizeof(dev_t)); + for (i = 1; i < firstopt; i++) { + /* not xstat(): shouldn't bomb out on + * "find not_exist exist -xdev" */ + if (stat(argv[i], &stbuf)) stbuf.st_dev = -1L; + xdev_dev[i-1] = stbuf.st_dev; + } + } + argp[0] = "-a"; + } + argp++; } +#endif + + actions = parse_params(&argv[firstopt]); + for (i = 1; i < firstopt; i++) { + if (!recursive_action(argv[i], + TRUE, // recurse + dereference, // follow links + FALSE, // depth first + fileAction, // file action + fileAction, // dir action + NULL, // user data + 0)) // depth + status = EXIT_FAILURE; + } return status; }