diff options
Diffstat (limited to 'src/eval.c')
-rw-r--r-- | src/eval.c | 136 |
1 files changed, 96 insertions, 40 deletions
@@ -5,12 +5,13 @@ * Implementation of all the primary expressions. */ +#include "prelude.h" #include "eval.h" +#include "atomic.h" #include "bar.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" @@ -22,6 +23,7 @@ #include "printf.h" #include "pwcache.h" #include "sanity.h" +#include "sighook.h" #include "stat.h" #include "trie.h" #include "xregex.h" @@ -63,7 +65,6 @@ static void eval_error(struct bfs_eval *state, const char *format, ...) { // By POSIX, any errors should be accompanied by a non-zero exit status *state->ret = EXIT_FAILURE; - int error = errno; const struct bfs_ctx *ctx = state->ctx; CFILE *cerr = ctx->cerr; @@ -71,7 +72,6 @@ static void eval_error(struct bfs_eval *state, const char *format, ...) { va_list args; va_start(args, format); - errno = error; cvfprintf(cerr, format, args); va_end(args); } @@ -90,7 +90,7 @@ static bool eval_should_ignore(const struct bfs_eval *state, int error) { */ static void eval_report_error(struct bfs_eval *state) { if (!eval_should_ignore(state, errno)) { - eval_error(state, "%m.\n"); + eval_error(state, "%s.\n", errstr()); } } @@ -99,9 +99,9 @@ static void eval_report_error(struct bfs_eval *state) { */ static void eval_io_error(const struct bfs_expr *expr, struct bfs_eval *state) { if (expr->path) { - eval_error(state, "'%s': %m.\n", expr->path); + eval_error(state, "'%s': %s.\n", expr->path, errstr()); } else { - eval_error(state, "(standard output): %m.\n"); + eval_error(state, "(standard output): %s.\n", errstr()); } // Don't report the error again in bfs_ctx_free() @@ -228,7 +228,7 @@ bool eval_context(const struct bfs_expr *expr, struct bfs_eval *state) { static const struct timespec *eval_stat_time(const struct bfs_stat *statbuf, enum bfs_stat_field field, struct bfs_eval *state) { const struct timespec *ret = bfs_stat_time(statbuf, field); if (!ret) { - eval_error(state, "Couldn't get file %s: %m.\n", bfs_stat_field_name(field)); + eval_error(state, "Couldn't get file %s: %s.\n", bfs_stat_field_name(field), errstr()); } return ret; } @@ -398,13 +398,13 @@ static int eval_exec_finish(const struct bfs_expr *expr, const struct bfs_ctx *c if (expr->eval_fn == eval_exec) { if (bfs_exec_finish(expr->exec) != 0) { if (errno != 0) { - bfs_error(ctx, "%s %s: %m.\n", expr->argv[0], expr->argv[1]); + bfs_error(ctx, "%s %s: %s.\n", expr->argv[0], expr->argv[1], errstr()); } ret = -1; } } - for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + for_expr (child, expr) { if (eval_exec_finish(child, ctx) != 0) { ret = -1; } @@ -419,7 +419,7 @@ static int eval_exec_finish(const struct bfs_expr *expr, const struct bfs_ctx *c bool eval_exec(const struct bfs_expr *expr, struct bfs_eval *state) { bool ret = bfs_exec(expr->exec, state->ftwbuf) == 0; if (errno != 0) { - eval_error(state, "%s %s: %m.\n", expr->argv[0], expr->argv[1]); + eval_error(state, "%s %s: %s.\n", expr->argv[0], expr->argv[1], errstr()); } return ret; } @@ -445,38 +445,42 @@ bool eval_depth(const struct bfs_expr *expr, struct bfs_eval *state) { * -empty test. */ bool eval_empty(const struct bfs_expr *expr, struct bfs_eval *state) { - bool ret = false; const struct BFTW *ftwbuf = state->ftwbuf; + const struct bfs_stat *statbuf; + struct bfs_dir *dir; + + switch (ftwbuf->type) { + case BFS_REG: + statbuf = eval_stat(state); + return statbuf && statbuf->size == 0; - if (ftwbuf->type == BFS_DIR) { - struct bfs_dir *dir = bfs_allocdir(); + case BFS_DIR: + dir = bfs_allocdir(); if (!dir) { - eval_report_error(state); - return ret; + goto error; } if (bfs_opendir(dir, ftwbuf->at_fd, ftwbuf->at_path, 0) != 0) { - eval_report_error(state); - return ret; + goto error; } int did_read = bfs_readdir(dir, NULL); + bfs_closedir(dir); + if (did_read < 0) { - eval_report_error(state); - } else { - ret = !did_read; + goto error; } - bfs_closedir(dir); free(dir); - } else if (ftwbuf->type == BFS_REG) { - const struct bfs_stat *statbuf = eval_stat(state); - if (statbuf) { - ret = statbuf->size == 0; - } - } + return did_read == 0; + error: + eval_report_error(state); + free(dir); + return false; - return ret; + default: + return false; + } } /** @@ -898,7 +902,7 @@ bool eval_regex(const struct bfs_expr *expr, struct bfs_eval *state) { eval_error(state, "%s.\n", str); free(str); } else { - eval_error(state, "bfs_regerror(): %m.\n"); + eval_error(state, "bfs_regerror(): %s.\n", errstr()); } } @@ -995,6 +999,13 @@ bool eval_xtype(const struct bfs_expr *expr, struct bfs_eval *state) { const struct BFTW *ftwbuf = state->ftwbuf; enum bfs_stat_flags flags = ftwbuf->stat_flags ^ (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW); enum bfs_type type = bftw_type(ftwbuf, flags); + + // GNU find treats ELOOP as a broken symbolic link for -xtype l + // (but not -L -type l) + if ((flags & BFS_STAT_TRYFOLLOW) && type == BFS_ERROR && errno == ELOOP) { + type = BFS_LNK; + } + if (type == BFS_ERROR) { eval_report_error(state); return false; @@ -1016,7 +1027,7 @@ static int eval_gettime(struct bfs_eval *state, struct timespec *ts) { #ifdef BFS_CLOCK int ret = clock_gettime(BFS_CLOCK, ts); if (ret != 0) { - bfs_warning(state->ctx, "%pP: clock_gettime(): %m.\n", state->ftwbuf); + bfs_warning(state->ctx, "%pP: clock_gettime(): %s.\n", state->ftwbuf, errstr()); } return ret; #else @@ -1087,7 +1098,7 @@ bool eval_not(const struct bfs_expr *expr, struct bfs_eval *state) { * Evaluate a conjunction. */ bool eval_and(const struct bfs_expr *expr, struct bfs_eval *state) { - for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + for_expr (child, expr) { if (!eval_expr(child, state) || state->quit) { return false; } @@ -1100,7 +1111,7 @@ bool eval_and(const struct bfs_expr *expr, struct bfs_eval *state) { * Evaluate a disjunction. */ bool eval_or(const struct bfs_expr *expr, struct bfs_eval *state) { - for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + for_expr (child, expr) { if (eval_expr(child, state) || state->quit) { return true; } @@ -1115,7 +1126,7 @@ bool eval_or(const struct bfs_expr *expr, struct bfs_eval *state) { bool eval_comma(const struct bfs_expr *expr, struct bfs_eval *state) { bool ret uninit(false); - for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + for_expr (child, expr) { ret = eval_expr(child, state); if (state->quit) { break; @@ -1266,7 +1277,7 @@ static void debug_stat(const struct bfs_ctx *ctx, const struct BFTW *ftwbuf, enu bfs_debug_prefix(ctx, DEBUG_STAT); fprintf(stderr, "bfs_stat("); - if (ftwbuf->at_fd == AT_FDCWD) { + if (ftwbuf->at_fd == (int)AT_FDCWD) { fprintf(stderr, "AT_FDCWD"); } else { size_t baselen = strlen(ftwbuf->path) - strlen(ftwbuf->at_path); @@ -1282,7 +1293,7 @@ static void debug_stat(const struct bfs_ctx *ctx, const struct BFTW *ftwbuf, enu DEBUG_FLAG(flags, BFS_STAT_TRYFOLLOW); DEBUG_FLAG(flags, BFS_STAT_NOSYNC); - fprintf(stderr, ") == %d", err ? 0 : -1); + fprintf(stderr, ") == %d", err == 0 ? 0 : -1); if (err) { fprintf(stderr, " [%d]", err); @@ -1371,6 +1382,11 @@ struct callback_args { struct bfs_bar *bar; /** The time of the last status update. */ struct timespec last_status; + /** SIGINFO hook. */ + struct sighook *info_hook; + /** Flag set by SIGINFO hook. */ + atomic bool info_flag; + /** The number of files visited so far. */ size_t count; @@ -1397,15 +1413,38 @@ static enum bftw_action eval_callback(const struct BFTW *ftwbuf, void *ptr) { state.ret = &args->ret; state.quit = false; + // Check whether SIGINFO was delivered and show/hide the bar + if (exchange(&args->info_flag, false, relaxed)) { + if (args->bar) { + bfs_bar_hide(args->bar); + args->bar = NULL; + } else { + args->bar = bfs_bar_show(); + if (!args->bar) { + bfs_warning(ctx, "Couldn't show status bar: %s.\n", errstr()); + } + } + } + if (args->bar) { eval_status(&state, args->bar, &args->last_status, args->count); } if (ftwbuf->type == BFS_ERROR) { - if (!eval_should_ignore(&state, ftwbuf->error)) { - eval_error(&state, "%s.\n", xstrerror(ftwbuf->error)); - } state.action = BFTW_PRUNE; + + if (ftwbuf->error == ELOOP && ftwbuf->loopoff > 0) { + char *loop = strndup(ftwbuf->path, ftwbuf->loopoff); + if (loop) { + eval_error(&state, "Filesystem loop back to ${di}%pq${rs}\n", loop); + free(loop); + goto done; + } + } else if (eval_should_ignore(&state, ftwbuf->error)) { + goto done; + } + + eval_error(&state, "%s.\n", xstrerror(ftwbuf->error)); goto done; } @@ -1460,10 +1499,19 @@ done: return state.action; } +/** Show/hide the bar in response to SIGINFO. */ +static void eval_siginfo(int sig, siginfo_t *info, void *ptr) { + struct callback_args *args = ptr; + store(&args->info_flag, true, relaxed); +} + /** Raise RLIMIT_NOFILE if possible, and return the new limit. */ static int raise_fdlimit(struct bfs_ctx *ctx) { rlim_t cur = ctx->orig_nofile.rlim_cur; rlim_t max = ctx->orig_nofile.rlim_max; + if (!ctx->raise_nofile) { + max = cur; + } rlim_t target = 64 << 10; if (rlim_cmp(target, max) > 0) { @@ -1592,7 +1640,7 @@ static bool eval_must_buffer(const struct bfs_expr *expr) { return true; } - for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + for_expr (child, expr) { if (eval_must_buffer(child)) { return true; } @@ -1615,10 +1663,17 @@ int bfs_eval(struct bfs_ctx *ctx) { if (ctx->status) { args.bar = bfs_bar_show(); if (!args.bar) { - bfs_warning(ctx, "Couldn't show status bar: %m.\n\n"); + bfs_warning(ctx, "Couldn't show status bar: %s.\n\n", errstr()); } } +#ifdef SIGINFO + int siginfo = SIGINFO; +#else + int siginfo = SIGUSR1; +#endif + args.info_hook = sighook(siginfo, eval_siginfo, &args, SH_CONTINUE); + struct trie seen; if (ctx->unique) { trie_init(&seen); @@ -1686,6 +1741,7 @@ int bfs_eval(struct bfs_ctx *ctx) { trie_destroy(&seen); } + sigunhook(args.info_hook); bfs_bar_hide(args.bar); return args.ret; |