aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toys/posix/find.c113
1 files changed, 77 insertions, 36 deletions
diff --git a/toys/posix/find.c b/toys/posix/find.c
index 94179cb3..4f8db5af 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -7,6 +7,8 @@
* Our "unspecified" behavior for no paths is to use "."
* Parentheses can only stack 4096 deep
* Not treating two {} as an error, but only using last
+ *
+ * TODO: -empty (dirs too!) -delete -execdir +
USE_FIND(NEWTOY(find, "?^HL[-HL]", TOYFLAG_USR|TOYFLAG_BIN))
@@ -32,7 +34,7 @@ config FIND
-ctime N created N days ago -mtime N modified N days ago
-newer FILE newer mtime than FILE -mindepth # at least # dirs down
-depth ignore contents of dir -maxdepth # at most # dirs down
- -inum N inode number N
+ -inum N inode number N -empty empty files and dirs
-type [bcdflps] (block, char, dir, file, symlink, pipe, socket)
Numbers N may be prefixed by a - (less than) or + (greater than):
@@ -59,32 +61,37 @@ GLOBALS(
time_t now;
)
+struct execdir_data {
+ struct execdir_data *next;
+
+ int namecount;
+ struct double_list *names;
+};
+
// None of this can go in TT because you can have more than one -exec
struct exec_range {
char *next, *prev; // layout compatible with struct double_list
- int dir, plus, arglen, argsize, curly, namecount;
+ int dir, plus, arglen, argsize, curly;
char **argstart;
- struct double_list *names;
+ struct execdir_data exec, *execdir;
};
// Perform pending -exec (if any)
static int flush_exec(struct dirtree *new, struct exec_range *aa)
{
- struct double_list **dl;
+ struct execdir_data *bb = aa->execdir ? aa->execdir : &aa->exec;
char **newargs;
- int rc = 0;
+ int rc;
- if (!aa->namecount) return 0;
+ if (!bb->namecount) return 0;
- if (aa->dir && new->parent) dl = (void *)&new->parent->extra;
- else dl = &aa->names;
- dlist_terminate(*dl);
+ dlist_terminate(bb->names);
// switch to directory for -execdir, or back to top if we have an -execdir
// _and_ a normal -exec, or are at top of tree in -execdir
if (aa->dir && new->parent) rc = fchdir(new->parent->dirfd);
- else if (TT.topdir != -1) rc = fchdir(TT.topdir);
+ else rc = fchdir(TT.topdir);
if (rc) {
perror_msg("%s", new->name);
@@ -92,17 +99,17 @@ static int flush_exec(struct dirtree *new, struct exec_range *aa)
}
// execdir: accumulated execs in this directory's children.
- newargs = xmalloc(sizeof(char *)*(aa->arglen+aa->namecount+1));
+ newargs = xmalloc(sizeof(char *)*(aa->arglen+bb->namecount+1));
if (aa->curly < 0) {
memcpy(newargs, aa->argstart, sizeof(char *)*aa->arglen);
newargs[aa->arglen] = 0;
} else {
- struct double_list *dl2 = *dl;
int pos = aa->curly, rest = aa->arglen - aa->curly;
+ struct double_list *dl;
// Collate argument list
memcpy(newargs, aa->argstart, sizeof(char *)*pos);
- for (dl2 = *dl; dl2; dl2 = dl2->next) newargs[pos++] = dl2->data;
+ for (dl = bb->names; dl; dl = dl->next) newargs[pos++] = dl->data;
rest = aa->arglen - aa->curly - 1;
memcpy(newargs+pos, aa->argstart+aa->curly+1, sizeof(char *)*rest);
newargs[pos+rest] = 0;
@@ -110,9 +117,9 @@ static int flush_exec(struct dirtree *new, struct exec_range *aa)
rc = xrun(newargs);
- llist_traverse(*dl, llist_free_double);
- *dl = 0;
- aa->namecount = 0;
+ llist_traverse(bb->names, llist_free_double);
+ bb->names = 0;
+ bb->namecount = 0;
return rc;
}
@@ -159,10 +166,13 @@ static int do_find(struct dirtree *new)
if (TT.xdev && new->st.st_dev != new->parent->st.st_dev) recurse = 0;
}
if (S_ISDIR(new->st.st_mode)) {
+ struct double_list *dl;
+ struct exec_range *aa;
+ struct execdir_data *bb;
+
if (!new->again) {
struct dirtree *n;
- if (TT.depth) return recurse;
for (n = new->parent; n; n = n->parent) {
if (n->st.st_ino==new->st.st_ino && n->st.st_dev==new->st.st_dev) {
error_msg("'%s': loop detected", s = dirtree_path(new, 0));
@@ -171,15 +181,37 @@ static int do_find(struct dirtree *new)
return 0;
}
}
+ // Push new per-directory struct for -execdir/okdir + codepath. (Can't
+ // use new->extra because command line may have multiple -execdir)
+ if (TT.topdir != -1) for (dl = TT.argdata; dl; dl = dl->next) {
+ if (dl->prev != (void *)1) continue;
+ aa = (void *)dl;
+ if (!aa->plus || !aa->dir) continue;
+ bb = xzalloc(sizeof(struct execdir_data));
+ bb->next = aa->execdir;
+ aa->execdir = bb;
+ }
+ if (TT.depth) return recurse;
+ // On COMEAGAIN call flush pending "-execdir +" instances for this dir
+ // or flush everything for -exec at top
} else {
struct double_list *dl;
- if (TT.topdir != -1)
- for (dl = TT.argdata; dl; dl = dl->next)
- if (dl->prev == (void *)1 || !new->parent)
- toys.exitval |= flush_exec(new, (void *)dl);
+ if (TT.topdir != -1) for (dl = TT.argdata; dl; dl = dl->next) {
+ if (dl->prev != (void *)1) continue;
+ aa = (void *)dl;
+ if (!aa->plus) continue;
+ if (!new->parent || aa->dir) toys.exitval |= flush_exec(new, aa);
+
+ // pop per-directory struct
+ if ((bb = aa->execdir)) {
+ aa->execdir = bb->next;
+ free(bb);
+ }
+ }
- return 0;
+ recurse = 0;
+ if (!TT.depth) return 0;
}
}
}
@@ -394,16 +426,17 @@ static int do_find(struct dirtree *new)
}
} else aa->argsize += sizeof(char *) + strlen(ss[len]) + 1;
}
- if (!ss[len]) error_exit("-exec without \\;");
+ if (!ss[len]) error_exit("-exec without %s",
+ aa->curly!=-1 ? "\\;" : "{}");
ss += len;
aa->arglen = len;
aa->dir = !!strchr(s, 'd');
- if (aa->dir && TT.topdir == -1) TT.topdir = xopen(".", 0);
+ if (TT.topdir == -1) TT.topdir = xopen(".", 0);
// collect names and execute commands
} else {
char *name, *ss1 = ss[1];
- struct double_list **ddl;
+ struct execdir_data *bb;
// Grab command line exec argument list
aa = (void *)llist_pop(&argdata);
@@ -413,10 +446,6 @@ static int do_find(struct dirtree *new)
// name is always a new malloc, so we can always free it.
name = aa->dir ? xstrdup(new->name) : dirtree_path(new, 0);
- // Mark entry so COMEAGAIN can call flush_exec() in parent.
- // This is never a valid pointer value for prev to have otherwise
- if (aa->dir) aa->prev = (void *)1;
-
if (*s == 'o') {
fprintf(stderr, "[%s] %s", ss1, name);
if (!(test = yesno(0))) {
@@ -426,13 +455,25 @@ static int do_find(struct dirtree *new)
}
// Add next name to list (global list without -dir, local with)
- if (aa->dir && new->parent)
- ddl = (struct double_list **)&new->parent->extra;
- else ddl = &aa->names;
-
- dlist_add(ddl, name);
- aa->namecount++;
- if (!aa->plus) test = flush_exec(new, aa);
+ bb = aa->execdir ? aa->execdir : &aa->exec;
+ dlist_add(&bb->names, name);
+ bb->namecount++;
+
+ // -exec + collates and saves result in exitval
+ if (aa->plus) {
+ // Mark entry so COMEAGAIN can call flush_exec() in parent.
+ // This is never a valid pointer value for prev to have otherwise
+ // Done here vs argument parsing pass so it's after dlist_terminate
+ aa->prev = (void *)1;
+
+ // Flush if we pass 16 megs of environment space.
+ // An insanely long path (>2 gigs) could wrap the counter and
+ // defeat this test, which could potentially trigger OOM killer.
+ if ((aa->plus += sizeof(char *)+strlen(name)+1) > 1<<24) {
+ aa->plus = 1;
+ toys.exitval |= flush_exec(new, aa);
+ }
+ } else test = flush_exec(new, aa);
}
// Argument consumed, skip the check.