summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bfs.13
-rw-r--r--ctx.c1
-rw-r--r--ctx.h2
-rw-r--r--eval.c111
-rw-r--r--parse.c19
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
@@ -2306,6 +2306,14 @@ static struct expr *parse_sparse(struct parser_state *state, int arg1, int arg2)
}
/**
+ * 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].
*/
static struct expr *parse_type(struct parser_state *state, int x, int arg2) {
@@ -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} ");
}