summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile18
-rw-r--r--src/main.c225
-rw-r--r--tests/getopts.sh2
-rw-r--r--tests/run.sh10
4 files changed, 236 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index 69048cf..28dd59c 100644
--- a/Makefile
+++ b/Makefile
@@ -35,12 +35,12 @@ gen/config.mk::
## Build phase (`make`)
# The main binary
-bfs: bin/bfs
+bfs: bin/find2fd
.PHONY: bfs
# All binaries
BINS := \
- bin/bfs \
+ bin/find2fd \
bin/tests/mksock \
bin/tests/units \
bin/tests/xspawnee \
@@ -50,7 +50,7 @@ all: ${BINS}
.PHONY: all
# The main binary
-bin/bfs: ${LIBBFS} obj/src/main.o
+bin/find2fd: ${LIBBFS} obj/src/main.o
${BINS}:
@${MKDIR} ${@D}
@@ -120,19 +120,19 @@ INTEGRATIONS := default dfs ids eds j1 j2 j3 s
INTEGRATION_TESTS := ${INTEGRATIONS:%=check-%}
# Check just `bfs`
-check-default: bin/bfs ${ITEST_BINS}
+check-default: bin/find2fd ${ITEST_BINS}
+${MSG} "[TEST] bfs" \
- ./tests/tests.sh --make="${MAKE}" --bfs="bin/bfs" ${TEST_FLAGS}
+ ./tests/tests.sh --make="${MAKE}" --bfs="bin/find2fd" ${TEST_FLAGS}
# Check the different search strategies
-check-dfs check-ids check-eds: bin/bfs ${ITEST_BINS}
+check-dfs check-ids check-eds: bin/find2fd ${ITEST_BINS}
+${MSG} "[TEST] bfs -S ${@:check-%=%}" \
- ./tests/tests.sh --make="${MAKE}" --bfs="bin/bfs -S ${@:check-%=%}" ${TEST_FLAGS}
+ ./tests/tests.sh --make="${MAKE}" --bfs="bin/find2fd -S ${@:check-%=%}" ${TEST_FLAGS}
# Check various flags
-check-j1 check-j2 check-j3 check-s: bin/bfs ${ITEST_BINS}
+check-j1 check-j2 check-j3 check-s: bin/find2fd ${ITEST_BINS}
+${MSG} "[TEST] bfs -${@:check-%=%}" \
- ./tests/tests.sh --make="${MAKE}" --bfs="bin/bfs -${@:check-%=%}" ${TEST_FLAGS}
+ ./tests/tests.sh --make="${MAKE}" --bfs="bin/find2fd -${@:check-%=%}" ${TEST_FLAGS}
# Run the integration tests
integration-tests: ${INTEGRATION_TESTS}
diff --git a/src/main.c b/src/main.c
index 9d8b206..ec1d794 100644
--- a/src/main.c
+++ b/src/main.c
@@ -50,13 +50,15 @@
#include "ctx.h"
#include "diag.h"
#include "eval.h"
+#include "exec.h"
+#include "expr.h"
#include "parse.h"
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
-#include <time.h>
#include <unistd.h>
/**
@@ -108,6 +110,21 @@ static int open_std_streams(void) {
return 0;
}
+static void find2fd_extract(struct bfs_exprs *exprs, struct bfs_expr *expr) {
+ SLIST_INIT(exprs);
+
+ if (expr->eval_fn == eval_and) {
+ SLIST_EXTEND(exprs, &expr->children);
+ } else {
+ SLIST_APPEND(exprs, expr);
+ }
+}
+
+static void shellesc(dchar **cmdline, const char *str) {
+ dstrcat(cmdline, " ");
+ dstrescat(cmdline, str, WESC_SHELL | WESC_TTY);
+}
+
/**
* bfs entry point.
*/
@@ -137,13 +154,207 @@ int main(int argc, char *argv[]) {
bfs_warning(ctx, "Failed to set locale: %s\n\n", xstrerror(locale_err));
}
- // Walk the file system tree, evaluating the expression on each file
- int ret = bfs_eval(ctx);
+ bool hidden = true;
+ if (ctx->exclude->eval_fn == eval_hidden) {
+ hidden = false;
+ } else if (ctx->exclude->eval_fn != eval_false) {
+ bfs_expr_error(ctx, ctx->exclude);
+ bfs_error(ctx, "${ex}fd${rs} does not support ${red}-exclude${rs}.\n");
+ return EXIT_FAILURE;
+ }
+
+ struct bfs_exprs exprs;
+ find2fd_extract(&exprs, ctx->expr);
+
+ struct bfs_expr *pattern = NULL;
+ struct bfs_expr *type = NULL;
+ struct bfs_expr *executable = NULL;
+ struct bfs_expr *empty = NULL;
+ struct bfs_expr *action = NULL;
+ for_slist (struct bfs_expr, expr, &exprs) {
+ struct bfs_expr **target = NULL;
+ if (expr->eval_fn == eval_name
+ || expr->eval_fn == eval_path
+ || expr->eval_fn == eval_regex) {
+ target = &pattern;
+ } else if (expr->eval_fn == eval_type) {
+ target = &type;
+ } else if (expr->eval_fn == eval_access && expr->num == X_OK) {
+ target = &executable;
+ } else if (expr->eval_fn == eval_empty) {
+ target = &empty;
+ } else if ((expr->eval_fn == eval_fprint
+ || expr->eval_fn == eval_fprint0
+ || expr->eval_fn == eval_fls)
+ && expr->cfile == ctx->cout) {
+ target = &action;
+ } else if (expr->eval_fn == eval_exec
+ && !(expr->exec->flags & (BFS_EXEC_CONFIRM | BFS_EXEC_CHDIR))) {
+ target = &action;
+ }
+
+ if (!target) {
+ bfs_expr_error(ctx, expr);
+ if (bfs_expr_is_parent(expr)) {
+ bfs_error(ctx, "Too complicated to convert to ${ex}fd${rs}.\n");
+ } else {
+ bfs_error(ctx, "No equivalent ${ex}fd${rs} option.\n");
+ }
+ return EXIT_FAILURE;
+ }
+
+ if (*target) {
+ bfs_expr_error(ctx, *target);
+ bfs_expr_error(ctx, expr);
+ bfs_error(ctx, "${ex}fd${rs} doesn't support both of these at once.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (action && target != &action) {
+ bfs_expr_error(ctx, expr);
+ bfs_error(ctx, "${ex}fd${rs} doesn't support this ...\n");
+ bfs_expr_error(ctx, *target);
+ bfs_error(ctx, "... after this.\n");
+ return EXIT_FAILURE;
+ }
+
+ *target = expr;
+ }
+
+ if (!action) {
+ bfs_expr_error(ctx, ctx->expr);
+ bfs_error(ctx, "Missing action.\n");
+ return EXIT_FAILURE;
+ }
+
+ dchar *cmdline = dstralloc(0);
+
+ dstrcat(&cmdline, "fd --no-ignore");
- // Free the parsed command line, and detect any last-minute errors
- if (bfs_ctx_free(ctx) != 0 && ret == EXIT_SUCCESS) {
- ret = EXIT_FAILURE;
+ if (hidden) {
+ dstrcat(&cmdline, " --hidden");
}
- return ret;
+ if (ctx->flags & BFTW_POST_ORDER) {
+ bfs_error(ctx, "${ex}fd${rs} doesn't support ${blu}-depth${rs}.\n");
+ return EXIT_FAILURE;
+ }
+ if (ctx->flags & BFTW_SORT) {
+ bfs_error(ctx, "${ex}fd${rs} doesn't support ${cyn}-s${rs}.\n");
+ return EXIT_FAILURE;
+ }
+ if (ctx->flags & BFTW_FOLLOW_ALL) {
+ dstrcat(&cmdline, " --follow");
+ }
+ if (ctx->flags & (BFTW_SKIP_MOUNTS | BFTW_PRUNE_MOUNTS)) {
+ dstrcat(&cmdline, " --one-file-system");
+ }
+
+ if (ctx->mindepth == ctx->maxdepth) {
+ dstrcatf(&cmdline, " --exact-depth %d", ctx->mindepth);
+ } else {
+ if (ctx->mindepth > 0) {
+ dstrcatf(&cmdline, " --min-depth %d", ctx->mindepth);
+ }
+ if (ctx->maxdepth < INT_MAX) {
+ dstrcatf(&cmdline, " --max-depth %d", ctx->mindepth);
+ }
+ }
+
+ if (type) {
+ unsigned int types = type->num;
+ if (types & (1 << BFS_REG)) {
+ dstrcat(&cmdline, " --type file");
+ types ^= (1 << BFS_REG);
+ }
+ if (types & (1 << BFS_DIR)) {
+ dstrcat(&cmdline, " --type directory");
+ types ^= (1 << BFS_DIR);
+ }
+ if (types & (1 << BFS_LNK)) {
+ dstrcat(&cmdline, " --type symlink");
+ types ^= (1 << BFS_LNK);
+ }
+ if (types & (1 << BFS_SOCK)) {
+ dstrcat(&cmdline, " --type socket");
+ types ^= (1 << BFS_SOCK);
+ }
+ if (types & (1 << BFS_FIFO)) {
+ dstrcat(&cmdline, " --type pipe");
+ types ^= (1 << BFS_FIFO);
+ }
+ if (types) {
+ bfs_expr_error(ctx, type);
+ bfs_error(ctx, "${ex}fd${rs} doesn't support this type.\n");
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (executable) {
+ dstrcat(&cmdline, " --type executable");
+ }
+
+ if (empty) {
+ dstrcat(&cmdline, " --type empty");
+ }
+
+ if (action->eval_fn == eval_fprint0) {
+ dstrcat(&cmdline, " --print0");
+ } else if (action->eval_fn == eval_fls) {
+ dstrcat(&cmdline, " --list-details");
+ }
+
+ if (pattern) {
+ if (pattern->eval_fn != eval_name) {
+ dstrcat(&cmdline, " --full-path");
+ }
+ if (pattern->eval_fn != eval_regex) {
+ dstrcat(&cmdline, " --glob");
+ }
+ if (pattern->argv[0][1] == 'i') {
+ dstrcat(&cmdline, " --ignore-case");
+ } else {
+ dstrcat(&cmdline, " --case-sensitive");
+ }
+ shellesc(&cmdline, pattern->argv[1]);
+ }
+
+ for (size_t i = 0; i < ctx->npaths; ++i) {
+ const char *path = ctx->paths[i];
+ if (!pattern || path[0] == '-') {
+ dstrcat(&cmdline, " --search-path");
+ }
+ shellesc(&cmdline, path);
+ }
+
+ if (action->eval_fn == eval_exec) {
+ struct bfs_exec *execbuf = action->exec;
+
+ dstrcat(&cmdline, " --exec");
+ if (execbuf->flags & BFS_EXEC_MULTI) {
+ dstrcat(&cmdline, "-batch");
+ }
+
+ bool placeholder = false;
+ for (size_t i = 0; i < execbuf->tmpl_argc; ++i) {
+ const char *arg = execbuf->tmpl_argv[i];
+ if (strstr(arg, "{}")) {
+ placeholder = true;
+ if (i == execbuf->tmpl_argc - 1 && strcmp(arg, "{}") == 0) {
+ // fd adds it automatically
+ break;
+ }
+ }
+ shellesc(&cmdline, arg);
+ }
+
+ if (!placeholder) {
+ bfs_expr_error(ctx, action);
+ bfs_error(ctx, "${ex}fd${rs} doesn't support ${blu}%s${rs} without a placeholder.\n", action->argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ printf("%s\n", cmdline);
+ return EXIT_SUCCESS;
}
diff --git a/tests/getopts.sh b/tests/getopts.sh
index 5214e9f..b987e49 100644
--- a/tests/getopts.sh
+++ b/tests/getopts.sh
@@ -150,7 +150,7 @@ parse_args() {
# Try to resolve the path to $BFS before we cd, while also supporting
# --bfs="./bin/bfs -S ids"
- read -a BFS <<<"${BFS:-$BIN/bfs}"
+ read -a BFS <<<"${BFS:-$BIN/find2fd}"
BFS[0]=$(_realpath "$(command -v "${BFS[0]}")")
if ((${#PATTERNS[@]} == 0)); then
diff --git a/tests/run.sh b/tests/run.sh
index ad9c0be..df3bd88 100644
--- a/tests/run.sh
+++ b/tests/run.sh
@@ -359,6 +359,7 @@ bfs_verbose_impl() {
# Run the bfs we're testing
invoke_bfs() {
+ skip
bfs_verbose "$@"
local ret=0
@@ -454,9 +455,14 @@ diff_output() {
# Run bfs, and diff it against the expected output
bfs_diff() {
- local ret=0
- invoke_bfs "$@" >"$OUT" || ret=$?
+ local fd
+ if ! fd=$("${BFS[@]}" "$@"); then
+ skip
+ fi
+ local ret=0
+ eval "$fd" >"$OUT" || ret=$?
+ sed -i 's|/$||' "$OUT"
sort_output
diff_output || exit $EX_DIFF