X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=findutils%2Ffind.c;h=edb8482d80d7dccf8550d0ae5f5a4f826784feb8;hb=e5667c1cfe339b2f84abaabb90259ec29b91cf89;hp=a3cbe668daadd29a37663851fb238201f9af81cd;hpb=e2fb719ba3463955f349ce63228981943f00bfaa;p=oweals%2Fbusybox.git diff --git a/findutils/find.c b/findutils/find.c index a3cbe668d..edb8482d8 100644 --- a/findutils/find.c +++ b/findutils/find.c @@ -23,62 +23,62 @@ * # find file.txt -exec 'echo' '{} {}' ';' * file.txt file.txt * (strace: execve("/bin/echo", ["echo", "file.txt file.txt"], [ 30 vars ])) - * - * bboxed find rev 16467: above - works, below - doesn't - * * # 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 "busybox.h" #include -static char *pattern; -#if ENABLE_FEATURE_FIND_PRINT0 -static char printsep = '\n'; -#endif - -#if ENABLE_FEATURE_FIND_TYPE -static int type_mask = 0; -#endif - -#if ENABLE_FEATURE_FIND_PERM -static char perm_char = 0; -static int perm_mask = 0; -#endif - -#if ENABLE_FEATURE_FIND_MTIME -static char mtime_char; -static int mtime_days; -#endif +USE_FEATURE_FIND_XDEV(static dev_t *xdev_dev;) +USE_FEATURE_FIND_XDEV(static int xdev_count;) -#if ENABLE_FEATURE_FIND_MMIN -static char mmin_char; -static int mmin_mins; -#endif +typedef int (*action_fp)(const char *fileName, struct stat *statbuf, void *); -#if ENABLE_FEATURE_FIND_XDEV -static dev_t *xdev_dev; -static int xdev_count = 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)) -#if ENABLE_FEATURE_FIND_NEWER -static time_t newer_mtime; -#endif +static action ***actions; +static int need_print = 1; -#if ENABLE_FEATURE_FIND_INUM -static ino_t inode_num; -#endif +static inline int one_char(const char* str, char c) +{ + return (str[0] == c && str[1] == '\0'); +} -#if ENABLE_FEATURE_FIND_EXEC -static char **exec_argv; -static int *subst_count; -static int exec_argc; -#endif static int count_subst(const char *str) { @@ -109,102 +109,157 @@ static char* subst(const char *src, int count, const char* filename) } -static int fileAction(const char *fileName, struct stat *statbuf, void* junk, int depth) +static int exec_actions(action ***appp, const char *fileName, struct stat *statbuf) { -#if ENABLE_FEATURE_FIND_XDEV - if (S_ISDIR(statbuf->st_mode) && xdev_count) { - int i; - for (i=0; ist_dev) - return SKIP; + int cur_group; + int cur_action; + int rc = TRUE; + action **app, *ap; + + 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; } } -#endif - if (pattern != NULL) { - const char *tmp = strrchr(fileName, '/'); + return rc; +} - if (tmp == NULL) - tmp = fileName; - else - tmp++; - if (fnmatch(pattern, tmp, FNM_PERIOD) != 0) - goto no_match; - } + +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 - if (type_mask != 0) { - if (!((statbuf->st_mode & S_IFMT) == type_mask)) - goto no_match; - } +ACTF(type) +{ + return ((statbuf->st_mode & S_IFMT) == ap->type_mask); +} #endif #if ENABLE_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; - } +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 #if ENABLE_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; - } +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 #if ENABLE_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; - } +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 #if ENABLE_FEATURE_FIND_NEWER - if (newer_mtime != 0) { - time_t file_age = newer_mtime - statbuf->st_mtime; - if (file_age >= 0) - goto no_match; - } +ACTF(newer) +{ + return (ap->newer_mtime >= statbuf->st_mtime); +} #endif #if ENABLE_FEATURE_FIND_INUM - if (inode_num != 0) { - if (!(statbuf->st_ino == inode_num)) - goto no_match; - } +ACTF(inum) +{ + return (statbuf->st_ino != ap->inode_num); +} #endif #if ENABLE_FEATURE_FIND_EXEC - if (exec_argc) { - int i; - char *argv[exec_argc+1]; - for (i = 0; i < exec_argc; i++) - argv[i] = subst(exec_argv[i], subst_count[i], fileName); - argv[i] = NULL; /* terminate the list */ - errno = 0; - wait4pid(spawn(argv)); - if (errno) - bb_perror_msg("%s", argv[0]); - for (i = 0; i < exec_argc; i++) - free(argv[i]); - goto no_match; - } +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 - printf("%s%c", fileName, printsep); -#else - puts(fileName); +ACTF(print0) +{ + printf("%s%c", fileName, '\0'); + return TRUE; +} #endif - no_match: + +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; + for (i = 0; i < xdev_count; i++) { + if (xdev_dev[i] != statbuf->st_dev) + return SKIP; + } + } +#endif + 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; +} + + #if ENABLE_FEATURE_FIND_TYPE static int find_type(char *type) { @@ -241,38 +296,85 @@ static int find_type(char *type) } #endif -int find_main(int argc, char **argv) +action*** parse_params(char **argv) { - int dereference = FALSE; - int i, j, 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++) { - char *arg = argv[i]; - char *arg1 = argv[i+1]; - if (strcmp(arg, "-follow") == 0) - dereference = TRUE; + 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) - printsep = '\0'; + else if (strcmp(arg, "-print0") == 0) { + need_print = 0; + (void) ALLOC_ACTION(print0); + } #endif else if (strcmp(arg, "-name") == 0) { - if (++i == argc) + action_name *ap; + if (!*++argv) bb_error_msg_and_die(bb_msg_requires_arg, arg); - pattern = arg1; + ap = ALLOC_ACTION(name); + ap->pattern = arg1; + } #if ENABLE_FEATURE_FIND_TYPE - } else if (strcmp(arg, "-type") == 0) { - if (++i == argc) + else if (strcmp(arg, "-type") == 0) { + action_type *ap; + if (!*++argv) bb_error_msg_and_die(bb_msg_requires_arg, arg); - type_mask = find_type(arg1); + ap = ALLOC_ACTION(type); + ap->type_mask = find_type(arg1); + } #endif #if ENABLE_FEATURE_FIND_PERM /* TODO: @@ -281,92 +383,176 @@ int find_main(int argc, char **argv) * -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) { - if (++i == argc) + else if (strcmp(arg, "-perm") == 0) { + action_perm *ap; + if (!*++argv) bb_error_msg_and_die(bb_msg_requires_arg, arg); - perm_mask = xstrtol_range(arg1, 8, 0, 07777); - perm_char = arg1[0]; - if (perm_char == '-') - perm_mask = -perm_mask; + 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 #if ENABLE_FEATURE_FIND_MTIME - } else if (strcmp(arg, "-mtime") == 0) { - if (++i == argc) + else if (strcmp(arg, "-mtime") == 0) { + action_mtime *ap; + if (!*++argv) bb_error_msg_and_die(bb_msg_requires_arg, arg); - mtime_days = xatol(arg1); - mtime_char = arg1[0]; - if (mtime_char == '-') - mtime_days = -mtime_days; + 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 #if ENABLE_FEATURE_FIND_MMIN - } else if (strcmp(arg, "-mmin") == 0) { - if (++i == argc) + else if (strcmp(arg, "-mmin") == 0) { + action_mmin *ap; + if (!*++argv) bb_error_msg_and_die(bb_msg_requires_arg, arg); - mmin_mins = xatol(arg1); - mmin_char = arg1[0]; - if (mmin_char == '-') - mmin_mins = -mmin_mins; -#endif -#if ENABLE_FEATURE_FIND_XDEV - } else if (strcmp(arg, "-xdev") == 0) { - struct stat stbuf; - - xdev_count = (firstopt - 1) ? (firstopt - 1) : 1; - xdev_dev = xmalloc(xdev_count * sizeof(dev_t)); - - if (firstopt == 1) { - xstat(".", &stbuf); - xdev_dev[0] = stbuf.st_dev; - } else { - for (j = 1; j < firstopt; i++) { - xstat(argv[j], &stbuf); - xdev_dev[j-1] = stbuf.st_dev; - } - } + 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 #if ENABLE_FEATURE_FIND_NEWER - } else if (strcmp(arg, "-newer") == 0) { + else if (strcmp(arg, "-newer") == 0) { + action_newer *ap; struct stat stat_newer; - if (++i == argc) + if (!*++argv) bb_error_msg_and_die(bb_msg_requires_arg, arg); xstat(arg1, &stat_newer); - newer_mtime = stat_newer.st_mtime; + ap = ALLOC_ACTION(newer); + ap->newer_mtime = stat_newer.st_mtime; + } #endif #if ENABLE_FEATURE_FIND_INUM - } else if (strcmp(arg, "-inum") == 0) { - if (++i == argc) + else if (strcmp(arg, "-inum") == 0) { + action_inum *ap; + if (!*++argv) bb_error_msg_and_die(bb_msg_requires_arg, arg); - inode_num = xatoul(arg1); + ap = ALLOC_ACTION(inum); + ap->inode_num = xatoul(arg1); + } #endif #if ENABLE_FEATURE_FIND_EXEC - } else if (strcmp(arg, "-exec") == 0) { - i++; /* now: argv[i] is the first arg after -exec */ - exec_argv = &argv[i]; - exec_argc = i; + 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 (i == argc) /* did not see ';' till end */ + if (!*argv) /* did not see ';' till end */ bb_error_msg_and_die(bb_msg_requires_arg, arg); - if (argv[i][0] == ';' && argv[i][1] == '\0') + if (one_char(argv[0], ';')) break; - i++; + argv++; + ap->exec_argc++; } - exec_argc = i - exec_argc; /* number of --exec arguments */ - if (exec_argc == 0) + if (ap->exec_argc == 0) bb_error_msg_and_die(bb_msg_requires_arg, arg); - subst_count = xmalloc(exec_argc * sizeof(int)); - j = exec_argc; - while (j--) - subst_count[j] = count_subst(exec_argv[j]); + 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; + } + } + 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) { - static const char *const dot[] = { ".", NULL }; + argv[0] = "."; + argv--; firstopt++; - argv = (char**)dot - 1; } + +// 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