diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.c | 225 |
1 files changed, 218 insertions, 7 deletions
@@ -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 = ∅ + } 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; } |