+ }
+
+ /* We can use big switch() here, but on i386
+ * it doesn't give smaller code. Other arches? */
+
+/* Options always return true. They always take effect
+ * rather than being processed only when their place in the
+ * expression is reached.
+ */
+ /* Options */
+ if (parm == OPT_FOLLOW) {
+ dbg("follow enabled: %d", __LINE__);
+ G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
+ }
+#if ENABLE_FEATURE_FIND_XDEV
+ else if (parm == OPT_XDEV) {
+ dbg("%d", __LINE__);
+ G.xdev_on = 1;
+ }
+#endif
+#if ENABLE_FEATURE_FIND_MAXDEPTH
+ else if (parm == OPT_MINDEPTH || parm == OPT_MINDEPTH + 1) {
+ dbg("%d", __LINE__);
+ G.minmaxdepth[parm - OPT_MINDEPTH] = xatoi_positive(arg1);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_DEPTH
+ else if (parm == OPT_DEPTH) {
+ dbg("%d", __LINE__);
+ G.recurse_flags |= ACTION_DEPTHFIRST;
+ }
+#endif
+/* 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
+ */
+ /* Operators */
+ else if (parm == PARM_a IF_DESKTOP(|| parm == PARM_and)) {
+ dbg("%d", __LINE__);
+ /* no further special handling required */
+ }
+ else if (parm == PARM_o IF_DESKTOP(|| parm == PARM_or)) {
+ dbg("%d", __LINE__);
+ /* start new OR group */
+ cur_group++;
+ appp = xrealloc(appp, (cur_group+2) * sizeof(appp[0]));
+ /*appp[cur_group] = NULL; - already NULL */
+ appp[cur_group+1] = NULL;
+ cur_action = 0;
+ }
+#if ENABLE_FEATURE_FIND_NOT
+ else if (parm == PARM_char_not IF_DESKTOP(|| parm == PARM_not)) {
+ /* also handles "find ! ! -name 'foo*'" */
+ invert_flag ^= 1;
+ dbg("invert_flag:%d", invert_flag);
+ }
+#endif
+ /* Actions */
+ else if (parm == PARM_print) {
+ dbg("%d", __LINE__);
+ G.need_print = 0;
+ (void) ALLOC_ACTION(print);
+ }
+#if ENABLE_FEATURE_FIND_PRINT0
+ else if (parm == PARM_print0) {
+ dbg("%d", __LINE__);
+ G.need_print = 0;
+ (void) ALLOC_ACTION(print0);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_PRUNE
+ else if (parm == PARM_prune) {
+ dbg("%d", __LINE__);
+ (void) ALLOC_ACTION(prune);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_QUIT
+ else if (parm == PARM_quit) {
+ dbg("%d", __LINE__);
+ (void) ALLOC_ACTION(quit);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_DELETE
+ else if (parm == PARM_delete) {
+ dbg("%d", __LINE__);
+ G.need_print = 0;
+ G.recurse_flags |= ACTION_DEPTHFIRST;
+ (void) ALLOC_ACTION(delete);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_EXEC
+ else if (parm == PARM_exec) {
+ int i;
+ action_exec *ap;
+ IF_FEATURE_FIND_EXEC_PLUS(int all_subst = 0;)
+ dbg("%d", __LINE__);
+ G.need_print = 0;
+ ap = ALLOC_ACTION(exec);
+ ap->exec_argv = ++argv; /* first arg after -exec */
+ /*ap->exec_argc = 0; - ALLOC_ACTION did it */
+ while (1) {
+ if (!*argv) /* did not see ';' or '+' until end */
+ bb_error_msg_and_die(bb_msg_requires_arg, "-exec");
+ // find -exec echo Foo ">{}<" ";"
+ // executes "echo Foo >FILENAME<",
+ // find -exec echo Foo ">{}<" "+"
+ // executes "echo Foo FILENAME1 FILENAME2 FILENAME3...".
+ if ((argv[0][0] == ';' || argv[0][0] == '+')
+ && argv[0][1] == '\0'
+ ) {
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+ if (argv[0][0] == '+')
+ ap->filelist = xzalloc(sizeof(ap->filelist[0]));
+# endif
+ break;
+ }
+ 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_strstr(ap->exec_argv[i], "{}");
+ IF_FEATURE_FIND_EXEC_PLUS(all_subst += ap->subst_count[i];)
+ }
+# if ENABLE_FEATURE_FIND_EXEC_PLUS
+ /*
+ * coreutils expects {} to appear only once in "-exec +"
+ */
+ if (all_subst != 1 && ap->filelist)
+ bb_error_msg_and_die("only one '{}' allowed for -exec +");
+# endif
+ }
+#endif
+#if ENABLE_FEATURE_FIND_PAREN
+ else if (parm == PARM_char_brace) {
+ action_paren *ap;
+ char **endarg;
+ unsigned nested = 1;
+
+ dbg("%d", __LINE__);
+ endarg = argv;
+ while (1) {
+ if (!*++endarg)
+ bb_error_msg_and_die("unpaired '('");
+ if (LONE_CHAR(*endarg, '('))
+ nested++;
+ else if (LONE_CHAR(*endarg, ')') && !--nested) {
+ *endarg = NULL;
+ break;
+ }
+ }
+ ap = ALLOC_ACTION(paren);
+ ap->subexpr = parse_params(argv + 1);
+ *endarg = (char*) ")"; /* restore NULLed parameter */
+ argv = endarg;
+ }
+#endif
+ else if (parm == PARM_name || parm == PARM_iname) {
+ action_name *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(name);
+ ap->pattern = arg1;
+ ap->iname = (parm == PARM_iname);
+ }
+#if ENABLE_FEATURE_FIND_PATH
+ else if (parm == PARM_path IF_DESKTOP(|| parm == PARM_wholename) || parm == PARM_ipath) {
+ action_path *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(path);
+ ap->pattern = arg1;
+ ap->ipath = (parm == PARM_ipath);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_REGEX
+ else if (parm == PARM_regex) {
+ action_regex *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(regex);
+ xregcomp(&ap->compiled_pattern, arg1, 0 /*cflags*/);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_TYPE
+ else if (parm == PARM_type) {
+ action_type *ap;
+ ap = ALLOC_ACTION(type);
+ ap->type_mask = find_type(arg1);
+ dbg("created:type mask:%x", ap->type_mask);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_EXECUTABLE
+ else if (parm == PARM_executable) {
+ (void) ALLOC_ACTION(executable);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_PERM
+/* -perm BITS File's mode bits are exactly BITS (octal or symbolic).
+ * Symbolic modes use mode 0 as a point of departure.
+ * -perm -BITS All of the BITS are set in file's mode.
+ * -perm [+/]BITS At least one of the BITS is set in file's mode.
+ */
+ else if (parm == PARM_perm) {
+ action_perm *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(perm);
+ ap->perm_char = arg1[0];
+ arg1 = (arg1[0] == '/' ? arg1+1 : plus_minus_num(arg1));
+ /*ap->perm_mask = 0; - ALLOC_ACTION did it */
+ ap->perm_mask = bb_parse_mode(arg1, ap->perm_mask);
+ if (ap->perm_mask == (mode_t)-1)
+ bb_error_msg_and_die("invalid mode '%s'", arg1);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_MTIME
+ else if (parm == PARM_mtime) {
+ action_mtime *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(mtime);
+ ap->mtime_char = arg1[0];
+ ap->mtime_days = xatoul(plus_minus_num(arg1));
+ }
+#endif
+#if ENABLE_FEATURE_FIND_MMIN
+ else if (parm == PARM_mmin) {
+ action_mmin *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(mmin);
+ ap->mmin_char = arg1[0];
+ ap->mmin_mins = xatoul(plus_minus_num(arg1));
+ }
+#endif
+#if ENABLE_FEATURE_FIND_NEWER
+ else if (parm == PARM_newer) {
+ struct stat stat_newer;
+ action_newer *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(newer);
+ xstat(arg1, &stat_newer);
+ ap->newer_mtime = stat_newer.st_mtime;
+ }
+#endif
+#if ENABLE_FEATURE_FIND_INUM
+ else if (parm == PARM_inum) {
+ action_inum *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(inum);
+ ap->inode_num = xatoul(arg1);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_USER
+ else if (parm == PARM_user) {
+ action_user *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(user);
+ ap->uid = bb_strtou(arg1, NULL, 10);
+ if (errno)
+ ap->uid = xuname2uid(arg1);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_GROUP
+ else if (parm == PARM_group) {
+ action_group *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(group);
+ ap->gid = bb_strtou(arg1, NULL, 10);
+ if (errno)
+ ap->gid = xgroup2gid(arg1);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_SIZE
+ else if (parm == PARM_size) {
+/* -size n[bckw]: file uses n units of space
+ * b (default): units are 512-byte blocks
+ * c: 1 byte
+ * k: kilobytes
+ * w: 2-byte words
+ */
+#if ENABLE_LFS
+#define XATOU_SFX xatoull_sfx
+#else
+#define XATOU_SFX xatoul_sfx
+#endif
+ static const struct suffix_mult find_suffixes[] = {
+ { "c", 1 },
+ { "w", 2 },
+ { "", 512 },
+ { "b", 512 },
+ { "k", 1024 },
+ { "", 0 }
+ };
+ action_size *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(size);
+ ap->size_char = arg1[0];
+ ap->size = XATOU_SFX(plus_minus_num(arg1), find_suffixes);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_CONTEXT
+ else if (parm == PARM_context) {
+ action_context *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(context);
+ /*ap->context = NULL; - ALLOC_ACTION did it */
+ /* SELinux headers erroneously declare non-const parameter */
+ if (selinux_raw_to_trans_context((char*)arg1, &ap->context))
+ bb_simple_perror_msg(arg1);
+ }
+#endif
+#if ENABLE_FEATURE_FIND_LINKS
+ else if (parm == PARM_links) {
+ action_links *ap;
+ dbg("%d", __LINE__);
+ ap = ALLOC_ACTION(links);
+ ap->links_char = arg1[0];
+ ap->links_count = xatoul(plus_minus_num(arg1));
+ }
+#endif
+ else {
+ bb_error_msg("unrecognized: %s", arg);
+ bb_show_usage();
+ }
+ argv++;
+ }
+ dbg("exiting %s", __func__);
+ return appp;
+#undef ALLOC_ACTION
+#undef appp
+#undef cur_action
+#undef invert_flag
+}
+
+int find_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int find_main(int argc UNUSED_PARAM, char **argv)
+{
+ int i, firstopt;
+ char **past_HLP, *saved;
+
+ INIT_G();
+
+ /* "find -type f" + getopt("+HLP") => disaster.
+ * Need to avoid getopt running into a non-HLP option.
+ * Do this by temporarily storing NULL there:
+ */
+ past_HLP = argv;
+ for (;;) {
+ saved = *++past_HLP;
+ if (!saved)
+ break;
+ if (saved[0] != '-')
+ break;
+ if (!saved[1])
+ break; /* it is "-" */
+ if ((saved+1)[strspn(saved+1, "HLP")] != '\0')
+ break;
+ }
+ *past_HLP = NULL;
+ /* "+": stop on first non-option */
+ i = getopt32(argv, "+HLP");
+ if (i & (1<<0))
+ G.recurse_flags |= ACTION_FOLLOWLINKS_L0 | ACTION_DANGLING_OK;
+ if (i & (1<<1))
+ G.recurse_flags |= ACTION_FOLLOWLINKS | ACTION_DANGLING_OK;
+ /* -P is default and is ignored */
+ argv = past_HLP; /* same result as "argv += optind;" */
+ *past_HLP = saved;
+
+ for (firstopt = 0; argv[firstopt]; firstopt++) {
+ if (argv[firstopt][0] == '-')