From 77e594145c6c3da49f5e65a793d7cba90091f6bd Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 3 Nov 2020 13:29:57 -0500 Subject: New -status option to display a status bar --- bfs.1 | 3 ++ ctx.c | 1 + ctx.h | 2 ++ eval.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- parse.c | 19 +++++++++-- 5 files changed, 122 insertions(+), 14 deletions(-) diff --git a/bfs.1 b/bfs.1 index 6cda4fb..0d95fd7 100644 --- a/bfs.1 +++ b/bfs.1 @@ -306,6 +306,9 @@ see .B \-regextype .IR help ). .TP +.B \-status +Display a status bar while searching. +.TP .B \-unique Skip any files that have already been seen. Particularly useful along with diff --git a/ctx.c b/ctx.c index aafc08b..6fc6ffb 100644 --- a/ctx.c +++ b/ctx.c @@ -43,6 +43,7 @@ struct bfs_ctx *bfs_ctx_new(void) { ctx->optlevel = 3; ctx->debug = 0; ctx->ignore_races = false; + ctx->status = false; ctx->unique = false; ctx->warn = false; ctx->xargs_safe = false; diff --git a/ctx.h b/ctx.h index ba9bca4..2046ce6 100644 --- a/ctx.h +++ b/ctx.h @@ -75,6 +75,8 @@ struct bfs_ctx { enum debug_flags debug; /** Whether to ignore deletions that race with bfs (-ignore_readdir_race). */ bool ignore_races; + /** Whether to show a status bar (-status). */ + bool status; /** Whether to only return unique files (-unique). */ bool unique; /** Whether to print warnings (-warn/-nowarn). */ diff --git a/eval.c b/eval.c index e3806d8..09c253f 100644 --- a/eval.c +++ b/eval.c @@ -19,6 +19,7 @@ */ #include "eval.h" +#include "bar.h" #include "bftw.h" #include "color.h" #include "darray.h" @@ -929,17 +930,17 @@ static int eval_gettime(struct eval_state *state, struct timespec *ts) { } /** - * Record the time that elapsed evaluating an expression. + * Record an elapsed time. */ -static void add_elapsed(struct expr *expr, const struct timespec *start, const struct timespec *end) { - expr->elapsed.tv_sec += end->tv_sec - start->tv_sec; - expr->elapsed.tv_nsec += end->tv_nsec - start->tv_nsec; - if (expr->elapsed.tv_nsec < 0) { - expr->elapsed.tv_nsec += 1000000000L; - --expr->elapsed.tv_sec; - } else if (expr->elapsed.tv_nsec >= 1000000000L) { - expr->elapsed.tv_nsec -= 1000000000L; - ++expr->elapsed.tv_sec; +static void timespec_elapsed(struct timespec *elapsed, const struct timespec *start, const struct timespec *end) { + elapsed->tv_sec += end->tv_sec - start->tv_sec; + elapsed->tv_nsec += end->tv_nsec - start->tv_nsec; + if (elapsed->tv_nsec < 0) { + elapsed->tv_nsec += 1000000000L; + --elapsed->tv_sec; + } else if (elapsed->tv_nsec >= 1000000000L) { + elapsed->tv_nsec -= 1000000000L; + ++elapsed->tv_sec; } } @@ -961,7 +962,7 @@ static bool eval_expr(struct expr *expr, struct eval_state *state) { if (time) { if (eval_gettime(state, &end) == 0) { - add_elapsed(expr, &start, &end); + timespec_elapsed(&expr->elapsed, &start, &end); } } @@ -1030,6 +1031,71 @@ bool eval_comma(const struct expr *expr, struct eval_state *state) { return eval_expr(expr->rhs, state); } +/** Update the status bar. */ +static void eval_status(struct eval_state *state, struct bfs_bar *bar, struct timespec *last_status, size_t count) { + struct timespec now; + if (eval_gettime(state, &now) == 0) { + struct timespec elapsed = {0}; + timespec_elapsed(&elapsed, last_status, &now); + + // Update every 0.1s + if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= 100000000L) { + *last_status = now; + } else { + return; + } + } + + size_t width = bfs_bar_width(bar); + if (width < 3) { + return; + } + + const struct BFTW *ftwbuf = state->ftwbuf; + + char *rhs = dstrprintf(" (visited: %zu, depth: %2zu)", count, ftwbuf->depth); + if (!rhs) { + return; + } + + size_t rhslen = dstrlen(rhs); + if (3 + rhslen > width) { + dstresize(&rhs, 0); + rhslen = 0; + } + + size_t pathmax = width - rhslen - 3; + size_t pathlen = ftwbuf->nameoff; + if (ftwbuf->depth == 0) { + pathlen = strlen(ftwbuf->path); + } + if (pathlen > pathmax) { + pathlen = pathmax; + } + + char *status = dstrndup(ftwbuf->path, pathlen); + if (!status) { + goto out_rhs; + } + if (dstrcat(&status, "...") != 0) { + goto out_rhs; + } + while (dstrlen(status) < pathmax + 3) { + if (dstrapp(&status, ' ') != 0) { + goto out_rhs; + } + } + if (dstrcat(&status, rhs) != 0) { + goto out_rhs; + } + + bfs_bar_update(bar, status); + + dstrfree(status); +out_rhs: + dstrfree(rhs); +} + /** Check if we've seen a file before. */ static bool eval_file_unique(struct eval_state *state, struct trie *seen) { const struct bfs_stat *statbuf = eval_stat(state); @@ -1172,8 +1238,17 @@ static const char *dump_bftw_action(enum bftw_action action) { struct callback_args { /** The bfs context. */ const struct bfs_ctx *ctx; + + /** The status bar. */ + struct bfs_bar *bar; + /** The time of the last status update. */ + struct timespec last_status; + /** The number of files visited so far. */ + size_t count; + /** The set of seen files. */ struct trie *seen; + /** Eventual return value from bfs_eval(). */ int ret; }; @@ -1183,6 +1258,7 @@ struct callback_args { */ static enum bftw_action eval_callback(const struct BFTW *ftwbuf, void *ptr) { struct callback_args *args = ptr; + ++args->count; const struct bfs_ctx *ctx = args->ctx; @@ -1193,6 +1269,10 @@ static enum bftw_action eval_callback(const struct BFTW *ftwbuf, void *ptr) { state.ret = &args->ret; state.quit = false; + if (args->bar) { + eval_status(&state, args->bar, &args->last_status, args->count); + } + if (ftwbuf->type == BFTW_ERROR) { if (!eval_should_ignore(&state, ftwbuf->error)) { args->ret = EXIT_FAILURE; @@ -1341,6 +1421,13 @@ int bfs_eval(const struct bfs_ctx *ctx) { .ret = EXIT_SUCCESS, }; + if (ctx->status) { + args.bar = bfs_bar_show(); + if (!args.bar) { + bfs_warning(ctx, "Couldn't show status bar: %m.\n"); + } + } + struct trie seen; if (ctx->unique) { trie_init(&seen); @@ -1395,5 +1482,7 @@ int bfs_eval(const struct bfs_ctx *ctx) { trie_destroy(&seen); } + bfs_bar_hide(args.bar); + return args.ret; } diff --git a/parse.c b/parse.c index 7113a1f..458d736 100644 --- a/parse.c +++ b/parse.c @@ -2305,6 +2305,14 @@ static struct expr *parse_sparse(struct parser_state *state, int arg1, int arg2) return expr; } +/** + * Parse -status. + */ +static struct expr *parse_status(struct parser_state *state, int arg1, int arg2) { + state->ctx->status = true; + return parse_nullary_option(state); +} + /** * Parse -x?type [bcdpflsD]. */ @@ -2658,8 +2666,9 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { cfprintf(cout, " ${blu}-noleaf${rs}\n"); cfprintf(cout, " Ignored; for compatibility with GNU find\n"); cfprintf(cout, " ${blu}-regextype${rs} ${bld}TYPE${rs}\n"); - cfprintf(cout, " Use ${bld}TYPE${rs}-flavored regexes (default: ${bld}posix-basic${rs}; see ${blu}-regextype${rs}" - " ${bld}help${rs})\n"); + cfprintf(cout, " Use ${bld}TYPE${rs}-flavored regexes (default: ${bld}posix-basic${rs}; see ${blu}-regextype${rs} ${bld}help${rs})\n"); + cfprintf(cout, " ${blu}-status${rs}\n"); + cfprintf(cout, " Display a status bar while searching\n"); cfprintf(cout, " ${blu}-unique${rs}\n"); cfprintf(cout, " Skip any files that have already been seen\n"); cfprintf(cout, " ${blu}-warn${rs}\n"); @@ -2951,10 +2960,11 @@ static const struct table_entry parse_table[] = { {"-since", T_TEST, parse_since, BFS_STAT_MTIME}, {"-size", T_TEST, parse_size}, {"-sparse", T_TEST, parse_sparse}, + {"-status", T_OPTION, parse_status}, {"-true", T_TEST, parse_const, true}, {"-type", T_TEST, parse_type, false}, {"-uid", T_TEST, parse_user}, - {"-unique", T_ACTION, parse_unique}, + {"-unique", T_OPTION, parse_unique}, {"-used", T_TEST, parse_used}, {"-user", T_TEST, parse_user}, {"-version", T_ACTION, parse_version}, @@ -3410,6 +3420,9 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { if (ctx->flags & BFTW_SKIP_MOUNTS) { cfprintf(cerr, "${blu}-mount${rs} "); } + if (ctx->status) { + cfprintf(cerr, "${blu}-status${rs} "); + } if (ctx->unique) { cfprintf(cerr, "${blu}-unique${rs} "); } -- cgit v1.2.3