X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=findutils%2Ffind.c;h=edb8482d80d7dccf8550d0ae5f5a4f826784feb8;hb=e5667c1cfe339b2f84abaabb90259ec29b91cf89;hp=913a778eb84382d08bd3a9323db57bdfcca65d77;hpb=1385899416a4396385ad421ae1f532be7103738a;p=oweals%2Fbusybox.git diff --git a/findutils/find.c b/findutils/find.c index 913a778eb..edb8482d8 100644 --- a/findutils/find.c +++ b/findutils/find.c @@ -10,170 +10,283 @@ * Licensed under the GPL version 2, see the file LICENSE in this tarball. */ +/* findutils-4.1.20: + * + * # 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 "busybox.h" #include -static char *pattern; -#ifdef CONFIG_FEATURE_FIND_PRINT0 -static char printsep = '\n'; -#endif +USE_FEATURE_FIND_XDEV(static dev_t *xdev_dev;) +USE_FEATURE_FIND_XDEV(static int xdev_count;) -#ifdef CONFIG_FEATURE_FIND_TYPE -static int type_mask = 0; -#endif +typedef int (*action_fp)(const char *fileName, struct stat *statbuf, void *); -#ifdef CONFIG_FEATURE_FIND_PERM -static char perm_char = 0; -static int perm_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_MTIME -static char mtime_char; -static int mtime_days; -#endif +static action ***actions; +static int need_print = 1; -#ifdef CONFIG_FEATURE_FIND_MMIN -static char mmin_char; -static int mmin_mins; -#endif +static inline int one_char(const char* str, char c) +{ + return (str[0] == c && str[1] == '\0'); +} -#ifdef CONFIG_FEATURE_FIND_XDEV -static dev_t *xdev_dev; -static int xdev_count = 0; -#endif -#ifdef CONFIG_FEATURE_FIND_NEWER -static time_t newer_mtime; -#endif +static int count_subst(const char *str) +{ + int count = 0; + while ((str = strstr(str, "{}"))) { + count++; + str++; + } + return count; +} -#ifdef CONFIG_FEATURE_FIND_INUM -static ino_t inode_num; -#endif -#ifdef CONFIG_FEATURE_FIND_EXEC -static char **exec_str; -static int num_matches; -static int exec_opt; -#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; +} + -static int fileAction(const char *fileName, struct stat *statbuf, void* junk) +static int exec_actions(action ***appp, const char *fileName, struct stat *statbuf) { -#ifdef CONFIG_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; - } -#ifdef CONFIG_FEATURE_FIND_TYPE - if (type_mask != 0) { - if (!((statbuf->st_mode & S_IFMT) == type_mask)) - 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 +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_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_NEWER +ACTF(newer) +{ + return (ap->newer_mtime >= statbuf->st_mtime); +} #endif -#ifdef CONFIG_FEATURE_FIND_INUM - if (inode_num != 0) { - if (!(statbuf->st_ino == inode_num)) - goto no_match; - } +#if ENABLE_FEATURE_FIND_INUM +ACTF(inum) +{ + return (statbuf->st_ino != ap->inode_num); +} #endif -#ifdef CONFIG_FEATURE_FIND_EXEC - if (exec_opt) { - int i; - char *cmd_string = ""; - for (i = 0; i < num_matches; i++) - cmd_string = xasprintf("%s%s%s", cmd_string, exec_str[i], fileName); - cmd_string = xasprintf("%s%s", cmd_string, exec_str[num_matches]); - system(cmd_string); - 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 -#ifdef CONFIG_FEATURE_FIND_PRINT0 - printf("%s%c", fileName, printsep); -#else +#if ENABLE_FEATURE_FIND_PRINT0 +ACTF(print0) +{ + printf("%s%c", fileName, '\0'); + return TRUE; +} +#endif + +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 -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') @@ -183,139 +296,273 @@ static int find_type(char *type) } #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) { - ; - } -#ifdef CONFIG_FEATURE_FIND_PRINT0 - else if (strcmp(argv[i], "-print0") == 0) - printsep = '\0'; -#endif - else if (strcmp(argv[i], "-name") == 0) { - if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_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(bb_msg_requires_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) { - if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-perm"); - perm_mask = xstrtol_range(argv[i], 8, 0, 07777); - perm_char = argv[i][0]; - if (perm_char == '-') - 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) { - if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-mtime"); - mtime_days = xatol(argv[i]); - mtime_char = argv[i][0]; - if (mtime_char == '-') - 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) { - if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-mmin"); - mmin_mins = xatol(argv[i]); - mmin_char = argv[i][0]; - if (mmin_char == '-') - 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) { - xstat(".", &stbuf); - xdev_dev[0] = stbuf.st_dev; - } - else { - - for (i = 1; i < firstopt; i++) { - xstat(argv[i], &stbuf); - 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(bb_msg_requires_arg, "-newer"); - xstat(argv[i], &stat_newer); - 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) { - if (++i == argc) - bb_error_msg_and_die(bb_msg_requires_arg, "-inum"); - inode_num = xatoul(argv[i]); +#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(bb_msg_requires_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 = xasprintf("%s %s", cmd_string, argv[i]); + argv++; + ap->exec_argc++; } + 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; - if (*cmd_string == 0) - bb_error_msg_and_die(bb_msg_requires_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] = xstrndup(cmd_string, b_pos); - cmd_string += b_pos + 2; + 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] = 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; }