summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2022-11-14 10:50:34 -0500
committerTavian Barnes <tavianator@tavianator.com>2022-11-14 10:50:34 -0500
commita4299f9bc1d3e60a7e628561e8d650c2a241e1c2 (patch)
tree91bff63782b54fa0fb6b1121536aa50faccc0d29
parent723113d5cf37a158a5d647fcb56079047c3d9f2a (diff)
downloadbfs-a4299f9bc1d3e60a7e628561e8d650c2a241e1c2.tar.xz
[WIP] Simplistic conversion from find syntax to fd
-rw-r--r--Makefile6
-rw-r--r--src/main.c242
-rwxr-xr-xtests/tests.sh19
3 files changed, 253 insertions, 14 deletions
diff --git a/Makefile b/Makefile
index a1144c9..0818f0c 100644
--- a/Makefile
+++ b/Makefile
@@ -195,18 +195,18 @@ CHECKS := $(STRATEGY_CHECKS) check-trie check-xtimegm
# Custom test flags for distcheck
DISTCHECK_FLAGS := -s TEST_FLAGS="--sudo --verbose=skipped"
-bfs: $(BIN)/bfs
+bfs: $(BIN)/find2fd
.PHONY: bfs
all: \
- $(BIN)/bfs \
+ $(BIN)/find2fd \
$(BIN)/tests/mksock \
$(BIN)/tests/trie \
$(BIN)/tests/xtimegm \
$(BIN)/tests/xtouch
.PHONY: all
-$(BIN)/bfs: \
+$(BIN)/find2fd: \
$(OBJ)/src/bar.o \
$(OBJ)/src/bfstd.o \
$(OBJ)/src/bftw.o \
diff --git a/src/main.c b/src/main.c
index fbddbe5..2d6272f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -54,14 +54,21 @@
#include "bfstd.h"
#include "ctx.h"
+#include "darray.h"
+#include "diag.h"
+#include "dstring.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 <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
/**
@@ -113,6 +120,32 @@ static int open_std_streams(void) {
return 0;
}
+static int find2fd_flatten(struct bfs_expr ***chain, struct bfs_expr *expr) {
+ if (!expr) {
+ return 0;
+ } else if (expr->eval_fn == eval_and) {
+ if (find2fd_flatten(chain, expr->lhs) != 0) {
+ return -1;
+ }
+ return find2fd_flatten(chain, expr->rhs);
+ } else {
+ return DARRAY_PUSH(chain, &expr);
+ }
+}
+
+static void shellesc(char **cmdline, const char *str) {
+ // https://stackoverflow.com/a/3669819/502399
+ dstrcat(cmdline, " '");
+ for (const char *c = str; *c; ++c) {
+ if (*c == '\'') {
+ dstrcat(cmdline, "'\\''");
+ } else {
+ dstrapp(cmdline, *c);
+ }
+ }
+ dstrcat(cmdline, "'");
+}
+
/**
* bfs entry point.
*/
@@ -130,11 +163,212 @@ int main(int argc, char *argv[]) {
return EXIT_FAILURE;
}
- int ret = bfs_eval(ctx);
+ bool hidden = true;
+ if (ctx->exclude != &bfs_false) {
+ if (ctx->exclude->eval_fn == eval_hidden) {
+ hidden = false;
+ } else {
+ bfs_expr_error(ctx, ctx->exclude);
+ bfs_error(ctx, "${ex}fd${rs} does not support ${red}-exclude${rs}.\n");
+ return EXIT_FAILURE;
+ }
+ }
- if (bfs_ctx_free(ctx) != 0 && ret == EXIT_SUCCESS) {
- ret = EXIT_FAILURE;
+ struct bfs_expr **exprs = NULL;
+ if (find2fd_flatten(&exprs, ctx->expr) != 0) {
+ return EXIT_FAILURE;
}
- return ret;
+ 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 (size_t i = 0; i < darray_length(exprs); ++i) {
+ struct bfs_expr *expr = exprs[i];
+ 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_has_children(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;
+ }
+
+ char *cmdline = dstralloc(0);
+
+ dstrcat(&cmdline, "fd --no-ignore");
+
+ if (hidden) {
+ dstrcat(&cmdline, " --hidden");
+ }
+
+ 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 < darray_length(ctx->paths); ++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/tests.sh b/tests/tests.sh
index 8d13aca..762687e 100755
--- a/tests/tests.sh
+++ b/tests/tests.sh
@@ -253,7 +253,7 @@ XTOUCH="$BIN/tests/xtouch"
# 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]}")")
# The temporary directory that will hold our test data
@@ -480,6 +480,7 @@ function bfs_verbose() {
}
function invoke_bfs() {
+ skip
bfs_verbose "$@"
"${BFS[@]}" "$@"
}
@@ -526,15 +527,19 @@ function diff_output() {
fi
}
-function bfs_diff() (
+function bfs_diff() {
bfs_verbose "$@"
- # Close the dup()'d stdout to make sure we have enough fd's for the process
- # substitution, even with low ulimit -n
- exec 3>&-
+ FD=$("${BFS[@]}" "$@")
+ if [ "$?" -ne 0 ]; then
+ skip
+ fi
- "${BFS[@]}" "$@" | sort >"$OUT"
+ eval "$FD" | sed 's|/$||' | sort >"$OUT"
local STATUS="${PIPESTATUS[0]}"
+ if [ "$STATUS" -ne 0 ]; then
+ skip
+ fi
diff_output || return $EX_DIFF
@@ -543,7 +548,7 @@ function bfs_diff() (
else
return $EX_BFS
fi
-)
+}
function skip() {
exit $EX_SKIP