diff options
author | Rob Landley <rob@landley.net> | 2016-01-14 12:17:38 -0600 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2016-01-14 14:23:58 -0600 |
commit | 192155553c01f39a774d169cb3b28dc5c7417c08 (patch) | |
tree | 30a83caa58dbc7454d7dcfae4ff9f72c6a3a2398 /toys | |
parent | 456b363175ae9a9bf2128d3e39a325552e1f0c43 (diff) | |
download | toybox-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.
Diffstat (limited to 'toys')
-rw-r--r-- | toys/posix/find.c | 101 |
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); |