diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2019-03-01 08:50:49 -0500 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2019-03-01 08:51:44 -0500 |
commit | b921fb2f43869ed12822ee99a80ccba9fd67c640 (patch) | |
tree | 374a249116e60fbe38be6c098f36fcd8d1a193c9 /eval.c | |
parent | b59a6f99a18246b08beb7997d9d2f59bac14d2de (diff) | |
download | bfs-b921fb2f43869ed12822ee99a80ccba9fd67c640.tar.xz |
Implement -unique
Closes #48
Diffstat (limited to 'eval.c')
-rw-r--r-- | eval.c | 43 |
1 files changed, 43 insertions, 0 deletions
@@ -29,6 +29,7 @@ #include "posix1e.h" #include "printf.h" #include "stat.h" +#include "trie.h" #include "util.h" #include <assert.h> #include <dirent.h> @@ -1134,6 +1135,30 @@ bool eval_comma(const struct expr *expr, struct eval_state *state) { return eval_expr(expr->rhs, state); } +/** 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); + if (!statbuf) { + return false; + } + + // Glue the device and inode numbers together as a unique ID + unsigned char id[sizeof(statbuf->dev) + sizeof(statbuf->ino)]; + memcpy(id, &statbuf->dev, sizeof(statbuf->dev)); + memcpy(id + sizeof(statbuf->dev), &statbuf->ino, sizeof(statbuf->ino)); + + int ret = trie_meminsert(seen, id, sizeof(id)); + if (ret > 0) { + state->action = BFTW_SKIP_SUBTREE; + return false; + } else if (ret < 0) { + eval_report_error(state); + return false; + } else { + return true; + } +} + /** * Dump the bftw_typeflag for -D search. */ @@ -1193,6 +1218,8 @@ static const char *dump_bftw_action(enum bftw_action action) { struct callback_args { /** The parsed command line. */ const struct cmdline *cmdline; + /** The set of seen files. */ + struct trie *seen; /** Eventual return value from eval_cmdline(). */ int ret; /** Whether to quit immediately. */ @@ -1228,6 +1255,12 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) { goto done; } + if (cmdline->unique) { + if (!eval_file_unique(&state, args->seen)) { + goto done; + } + } + if (cmdline->xargs_safe && strpbrk(ftwbuf->path, " \t\n\'\"\\")) { args->ret = EXIT_FAILURE; eval_error(&state, "Path is not safe for xargs.\n"); @@ -1357,6 +1390,12 @@ int eval_cmdline(const struct cmdline *cmdline) { .quit = false, }; + struct trie seen; + if (cmdline->unique) { + trie_init(&seen); + args.seen = &seen; + } + for (struct root *root = cmdline->roots; root && !args.quit; root = root->next) { if (cmdline->debug & DEBUG_SEARCH) { fprintf(stderr, "bftw(\"%s\", cmdline_callback, %d, ", root->path, nopenfd); @@ -1378,5 +1417,9 @@ int eval_cmdline(const struct cmdline *cmdline) { dump_cmdline(cmdline, true); } + if (cmdline->unique) { + trie_destroy(&seen); + } + return args.ret; } |