From 4242283189a94a79dda78540efe78b2666a944cc Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 20 Jul 2017 23:33:43 -0400 Subject: Implement -exit [STATUS] From NetBSD again. --- bfs.h | 1 + eval.c | 28 +++++++++++++++++++--------- main.c | 4 +--- parse.c | 23 +++++++++++++++++++++++ tests.sh | 15 +++++++++++++++ tests/test_exit.out | 16 ++++++++++++++++ 6 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 tests/test_exit.out diff --git a/bfs.h b/bfs.h index f63bda7..cc00f6b 100644 --- a/bfs.h +++ b/bfs.h @@ -320,6 +320,7 @@ bool eval_regex(const struct expr *expr, struct eval_state *state); bool eval_delete(const struct expr *expr, struct eval_state *state); bool eval_exec(const struct expr *expr, struct eval_state *state); +bool eval_exit(const struct expr *expr, struct eval_state *state); bool eval_nohidden(const struct expr *expr, struct eval_state *state); bool eval_fls(const struct expr *expr, struct eval_state *state); bool eval_fprint(const struct expr *expr, struct eval_state *state); diff --git a/eval.c b/eval.c index daee9d6..b095279 100644 --- a/eval.c +++ b/eval.c @@ -61,7 +61,7 @@ static bool eval_should_ignore(const struct eval_state *state, int error) { static void eval_error(struct eval_state *state) { if (!eval_should_ignore(state, errno)) { cfprintf(state->cmdline->cerr, "%{er}'%s': %s%{rs}\n", state->ftwbuf->path, strerror(errno)); - *state->ret = -1; + *state->ret = EXIT_FAILURE; } } @@ -304,6 +304,16 @@ bool eval_exec(const struct expr *expr, struct eval_state *state) { return ret; } +/** + * -exit action. + */ +bool eval_exit(const struct expr *expr, struct eval_state *state) { + state->action = BFTW_STOP; + *state->ret = expr->idata; + *state->quit = true; + return true; +} + /** * -depth N test. */ @@ -703,7 +713,7 @@ bool eval_regex(const struct expr *expr, struct eval_state *state) { perror("xregerror()"); } - *state->ret = -1; + *state->ret = EXIT_FAILURE; } return false; @@ -971,7 +981,7 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) { if (ftwbuf->typeflag == BFTW_ERROR) { if (!eval_should_ignore(&state, ftwbuf->error)) { - args->ret = -1; + args->ret = EXIT_FAILURE; cfprintf(cmdline->cerr, "%{er}'%s': %s%{rs}\n", ftwbuf->path, strerror(ftwbuf->error)); } state.action = BFTW_SKIP_SUBTREE; @@ -979,7 +989,7 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) { } if (cmdline->xargs_safe && strpbrk(ftwbuf->path, " \t\n\'\"\\")) { - args->ret = -1; + args->ret = EXIT_FAILURE; cfprintf(cmdline->cerr, "%{er}'%s': Path is not safe for xargs.%{rs}\n", ftwbuf->path); state.action = BFTW_SKIP_SUBTREE; goto done; @@ -1064,33 +1074,33 @@ static int infer_fdlimit(const struct cmdline *cmdline) { */ int eval_cmdline(const struct cmdline *cmdline) { if (!cmdline->expr) { - return 0; + return EXIT_SUCCESS; } if (cmdline->optlevel >= 4 && cmdline->expr->eval == eval_false) { if (cmdline->debug & DEBUG_OPT) { fputs("-O4: skipping evaluation of top-level -false\n", stderr); } - return 0; + return EXIT_SUCCESS; } int nopenfd = infer_fdlimit(cmdline); struct callback_args args = { .cmdline = cmdline, - .ret = 0, + .ret = EXIT_SUCCESS, .quit = false, }; for (struct root *root = cmdline->roots; root && !args.quit; root = root->next) { if (bftw(root->path, cmdline_callback, nopenfd, cmdline->flags, &args) != 0) { - args.ret = -1; + args.ret = EXIT_FAILURE; perror("bftw()"); } } if (eval_exec_finish(cmdline->expr) != 0) { - args.ret = -1; + args.ret = EXIT_FAILURE; } if (cmdline->debug & DEBUG_RATES) { diff --git a/main.c b/main.c index 0b957d3..f925915 100644 --- a/main.c +++ b/main.c @@ -48,9 +48,7 @@ int main(int argc, char *argv[]) { struct cmdline *cmdline = parse_cmdline(argc, argv); if (cmdline) { - if (eval_cmdline(cmdline) == 0) { - ret = EXIT_SUCCESS; - } + ret = eval_cmdline(cmdline); } free_cmdline(cmdline); diff --git a/parse.c b/parse.c index 98f603d..f5bb5e1 100644 --- a/parse.c +++ b/parse.c @@ -959,6 +959,26 @@ static struct expr *parse_exec(struct parser_state *state, int flags, int arg2) return expr; } +/** + * Parse -exit [STATUS]. + */ +static struct expr *parse_exit(struct parser_state *state, int arg1, int arg2) { + size_t argc = 1; + const char *value = state->argv[1]; + + int status = EXIT_SUCCESS; + if (value && parse_int(state, value, &status, IF_INT | IF_UNSIGNED | IF_QUIET)) { + argc = 2; + } + + struct expr *expr = parse_action(state, eval_exit, argc); + if (expr) { + expr->never_returns = true; + expr->idata = status; + } + return expr; +} + /** * Parse -f PATH. */ @@ -2156,6 +2176,8 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { cfprintf(cout, " %{blu}-sparse%{rs}\n"); cfprintf(cout, " Find files that occupy fewer disk blocks than expected\n\n"); + cfprintf(cout, " %{blu}-exit%{rs} %{bld}[STATUS]%{rs}\n"); + cfprintf(cout, " Exit immediately with the given status (%d if unspecified)\n", EXIT_SUCCESS); cfprintf(cout, " %{blu}-rm%{rs}\n"); cfprintf(cout, " Delete any found files (same as %{blu}-delete%{rs}; implies %{blu}-depth%{rs})\n\n"); @@ -2228,6 +2250,7 @@ static const struct table_entry parse_table[] = { {"-exec", false, parse_exec, 0}, {"-execdir", false, parse_exec, BFS_EXEC_CHDIR}, {"-executable", false, parse_access, X_OK}, + {"-exit", false, parse_exit}, {"-f", false, parse_f}, {"-false", false, parse_const, false}, {"-fls", false, parse_fls}, diff --git a/tests.sh b/tests.sh index a701e3d..0e337bd 100755 --- a/tests.sh +++ b/tests.sh @@ -255,6 +255,7 @@ bsd_tests=( test_inum test_nogroup test_nouser + test_exit ) gnu_tests=( @@ -1202,6 +1203,20 @@ function test_deep_strict() { bfs_diff deep -mindepth 18 } +function test_exit() { + $BFS basic -name foo -exit 42 + if [ $? -ne 42 ]; then + return 1 + fi + + $BFS basic -name qux -exit 42 + if [ $? -ne 0 ]; then + return 1 + fi + + bfs_diff basic -name bar -exit -o -print +} + passed=0 failed=0 diff --git a/tests/test_exit.out b/tests/test_exit.out new file mode 100644 index 0000000..b79fef1 --- /dev/null +++ b/tests/test_exit.out @@ -0,0 +1,16 @@ +basic +basic/a +basic/b +basic/c +basic/e +basic/g +basic/i +basic/j +basic/k +basic/l +basic/c/d +basic/e/f +basic/g/h +basic/j/foo +basic/k/foo +basic/l/foo -- cgit v1.2.3