aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2016-01-14 12:17:38 -0600
committerRob Landley <rob@landley.net>2016-01-14 14:23:58 -0600
commit192155553c01f39a774d169cb3b28dc5c7417c08 (patch)
tree30a83caa58dbc7454d7dcfae4ff9f72c6a3a2398
parent456b363175ae9a9bf2128d3e39a325552e1f0c43 (diff)
downloadtoybox-192155553c01f39a774d169cb3b28dc5c7417c08.tar.gz
Make "find -execdir toys echo {} +" batch correctly and show topdir results.
I dunno if find -execdir should show depth-first like it's doing, bit given that ubuntu's treating "+" and ";" the same for execdir... eh? Also, testing "find toys tests -mindepth 2 -execdir echo {} +" against the toybox source is easy (and why if (revert) fchdir() is needed), but adding that to the test suite means making a nontrivial hierarchy of files to test against (don't wanna use the project source because it's expected to change in ways that would break the tests)... The old "real world data vs test data" problem.
-rw-r--r--toys/posix/find.c101
1 files changed, 63 insertions, 38 deletions
diff --git a/toys/posix/find.c b/toys/posix/find.c
index 4f8db5af..e16c0df0 100644
--- a/toys/posix/find.c
+++ b/toys/posix/find.c
@@ -82,7 +82,7 @@ static int flush_exec(struct dirtree *new, struct exec_range *aa)
{
struct execdir_data *bb = aa->execdir ? aa->execdir : &aa->exec;
char **newargs;
- int rc;
+ int rc, revert = 0;
if (!bb->namecount) return 0;
@@ -90,12 +90,16 @@ static int flush_exec(struct dirtree *new, struct exec_range *aa)
// 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 rc = fchdir(TT.topdir);
- if (rc) {
- perror_msg("%s", new->name);
-
- return rc;
+ if (TT.topdir != -1) {
+ if (aa->dir && new && new->parent) {
+ revert++;
+ rc = fchdir(new->parent->dirfd);
+ } else rc = fchdir(TT.topdir);
+ if (rc) {
+ perror_msg_raw(revert ? new->name : ".");
+
+ return rc;
+ }
}
// execdir: accumulated execs in this directory's children.
@@ -121,6 +125,8 @@ static int flush_exec(struct dirtree *new, struct exec_range *aa)
bb->names = 0;
bb->namecount = 0;
+ if (revert) revert = fchdir(TT.topdir);
+
return rc;
}
@@ -148,6 +154,42 @@ static void do_print(struct dirtree *new, char c)
free(s);
}
+// Descend or ascend -execdir + directory level
+static void execdir(struct dirtree *new, int flush)
+{
+ struct double_list *dl;
+ struct exec_range *aa;
+ struct execdir_data *bb;
+
+ if (new && TT.topdir == -1) return;
+
+ for (dl = TT.argdata; dl; dl = dl->next) {
+ if (dl->prev != (void *)1) continue;
+ aa = (void *)dl;
+ if (!aa->plus || (new && !aa->dir)) continue;
+
+ if (flush) {
+
+ // Flush pending "-execdir +" instances for this dir
+ // or flush everything for -exec at top
+ toys.exitval |= flush_exec(new, aa);
+
+ // pop per-directory struct
+ if ((bb = aa->execdir)) {
+ aa->execdir = bb->next;
+ free(bb);
+ }
+ } else if (aa->dir) {
+
+ // Push new per-directory struct for -execdir/okdir + codepath. (Can't
+ // use new->extra because command line may have multiple -execdir)
+ bb = xzalloc(sizeof(struct execdir_data));
+ bb->next = aa->execdir;
+ aa->execdir = bb;
+ }
+ }
+}
+
// Call this with 0 for first pass argument parsing and syntax checking (which
// populates argdata). Later commands traverse argdata (in order) when they
// need "do once" results.
@@ -165,11 +207,9 @@ static int do_find(struct dirtree *new)
if (!dirtree_notdotdot(new)) return 0;
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 (S_ISDIR(new->st.st_mode)) {
+ // Descending into new directory
if (!new->again) {
struct dirtree *n;
@@ -181,35 +221,15 @@ 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) {
+ execdir(new, 0);
+
+ return recurse;
}
- if (TT.depth) return recurse;
- // On COMEAGAIN call flush pending "-execdir +" instances for this dir
- // or flush everything for -exec at top
+ // Done with directory (COMEAGAIN call)
} else {
- struct double_list *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);
- }
- }
-
+ execdir(new, 1);
recurse = 0;
if (!TT.depth) return 0;
}
@@ -494,6 +514,9 @@ cont:
if (new) {
// If there was no action, print
if (!print && test) do_print(new, '\n');
+
+ if (S_ISDIR(new->st.st_mode)) execdir(new, 0);
+
} else dlist_terminate(TT.argdata);
return recurse;
@@ -529,6 +552,8 @@ void find_main(void)
dirtree_handle_callback(dirtree_start(ss[i], toys.optflags&(FLAG_H|FLAG_L)),
do_find);
+ execdir(0, 1);
+
if (CFG_TOYBOX_FREE) {
close(TT.topdir);
llist_traverse(TT.argdata, free);