summaryrefslogtreecommitdiffstats
path: root/bfs.c
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2015-11-29 12:00:05 -0500
committerTavian Barnes <tavianator@tavianator.com>2015-11-29 12:00:05 -0500
commitda51ac27f1849ad94753277650435ae482a0423f (patch)
treed194bda63df14c5ec6bd32601d8a7db189f6c39e /bfs.c
parentf81be72020a886b5eca21bc1d8a5b0d62b9954d9 (diff)
downloadbfs-da51ac27f1849ad94753277650435ae482a0423f.tar.xz
Split out parsing code.
Diffstat (limited to 'bfs.c')
-rw-r--r--bfs.c1008
1 files changed, 0 insertions, 1008 deletions
diff --git a/bfs.c b/bfs.c
deleted file mode 100644
index d9d1f7a..0000000
--- a/bfs.c
+++ /dev/null
@@ -1,1008 +0,0 @@
-/*********************************************************************
- * bfs *
- * Copyright (C) 2015 Tavian Barnes <tavianator@tavianator.com> *
- * *
- * This program is free software. It comes without any warranty, to *
- * the extent permitted by applicable law. You can redistribute it *
- * and/or modify it under the terms of the Do What The Fuck You Want *
- * To Public License, Version 2, as published by Sam Hocevar. See *
- * the COPYING file or http://www.wtfpl.net/ for more details. *
- *********************************************************************/
-
-#include "bftw.h"
-#include "color.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <fnmatch.h>
-#include <limits.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-/**
- * A command line expression.
- */
-typedef struct expression expression;
-
-/**
- * The parsed command line.
- */
-typedef struct cmdline cmdline;
-
-/**
- * Ephemeral state for evaluating an expression.
- */
-typedef struct {
- /** Data about the current file. */
- struct BFTW *ftwbuf;
- /** The parsed command line. */
- const cmdline *cl;
- /** The bftw() callback return value. */
- bftw_action action;
- /** A stat() buffer, if necessary. */
- struct stat statbuf;
-} eval_state;
-
-/**
- * Perform a stat() call if necessary.
- */
-static void fill_statbuf(eval_state *state) {
- struct BFTW *ftwbuf = state->ftwbuf;
- if (ftwbuf->statbuf) {
- return;
- }
-
- if (fstatat(ftwbuf->at_fd, ftwbuf->at_path, &state->statbuf, AT_SYMLINK_NOFOLLOW) == 0) {
- ftwbuf->statbuf = &state->statbuf;
- } else {
- perror("fstatat()");
- }
-}
-
-/**
- * Expression evaluation function.
- *
- * @param expr
- * The current expression.
- * @param state
- * The current evaluation state.
- * @return
- * The result of the test.
- */
-typedef bool eval_fn(const expression *expr, eval_state *state);
-
-struct expression {
- /** The left hand side of the expression. */
- expression *lhs;
- /** The right hand side of the expression. */
- expression *rhs;
- /** The function that evaluates this expression. */
- eval_fn *eval;
- /** Optional integer data for this expression. */
- int idata;
- /** Optional string data for this expression. */
- const char *sdata;
-};
-
-/**
- * Create a new expression.
- */
-static expression *new_expression(eval_fn *eval) {
- expression *expr = malloc(sizeof(expression));
- if (!expr) {
- perror("malloc()");
- return NULL;
- }
-
- expr->lhs = NULL;
- expr->rhs = NULL;
- expr->eval = eval;
- expr->idata = 0;
- expr->sdata = NULL;
- return expr;
-}
-
-/**
- * -true test.
- */
-static bool eval_true(const expression *expr, eval_state *state) {
- return true;
-}
-
-/**
- * Singleton true expression instance.
- */
-static expression expr_true = {
- .lhs = NULL,
- .rhs = NULL,
- .eval = eval_true,
- .idata = 0,
- .sdata = NULL,
-};
-
-/**
- * -false test.
- */
-static bool eval_false(const expression *expr, eval_state *state) {
- return false;
-}
-
-/**
- * Singleton false expression instance.
- */
-static expression expr_false = {
- .lhs = NULL,
- .rhs = NULL,
- .eval = eval_false,
- .idata = 0,
- .sdata = NULL,
-};
-
-/**
- * Free an expression.
- */
-static void free_expression(expression *expr) {
- if (expr && expr != &expr_true && expr != &expr_false) {
- free_expression(expr->lhs);
- free_expression(expr->rhs);
- free(expr);
- }
-}
-
-/**
- * Create a new unary expression.
- */
-static expression *new_unary_expression(expression *rhs, eval_fn *eval) {
- expression *expr = new_expression(eval);
- if (!expr) {
- free_expression(rhs);
- return NULL;
- }
-
- expr->rhs = rhs;
- expr->eval = eval;
- return expr;
-}
-
-/**
- * Create a new binary expression.
- */
-static expression *new_binary_expression(expression *lhs, expression *rhs, eval_fn *eval) {
- expression *expr = new_expression(eval);
- if (!expr) {
- free_expression(rhs);
- free_expression(lhs);
- return NULL;
- }
-
- expr->lhs = lhs;
- expr->rhs = rhs;
- expr->eval = eval;
- return expr;
-}
-
-struct cmdline {
- /** The array of paths to start from. */
- const char **roots;
- /** The number of root paths. */
- size_t nroots;
-
- /** Color data. */
- color_table *colors;
- /** -color option. */
- bool color;
-
- /** -mindepth option. */
- int mindepth;
- /** -maxdepth option. */
- int maxdepth;
-
- /** bftw() flags. */
- int flags;
-
- /** The command line expression. */
- expression *expr;
-};
-
-/**
- * Free the parsed command line.
- */
-static void free_cmdline(cmdline *cl) {
- if (cl) {
- free_expression(cl->expr);
- free_colors(cl->colors);
- free(cl->roots);
- free(cl);
- }
-}
-
-/**
- * Add a root path to the cmdline.
- */
-static bool cmdline_add_root(cmdline *cl, const char *root) {
- size_t i = cl->nroots++;
- const char **roots = realloc(cl->roots, cl->nroots*sizeof(const char *));
- if (!roots) {
- perror("realloc()");
- return false;
- }
-
- roots[i] = root;
- cl->roots = roots;
- return true;
-}
-
-/**
- * Ephemeral state for parsing the command line.
- */
-typedef struct {
- /** The command line being parsed. */
- cmdline *cl;
- /** The command line arguments. */
- char **argv;
- /** Current argument index. */
- int i;
-
- /** Whether a -print action is implied. */
- bool implicit_print;
- /** Whether warnings are enabled (see -warn, -nowarn). */
- bool warn;
- /** Whether any non-option arguments have been encountered. */
- bool non_option_seen;
-} parser_state;
-
-/**
- * Parse the expression specified on the command line.
- */
-static expression *parse_expression(parser_state *state);
-
-/**
- * While parsing an expression, skip any paths and add them to the cmdline.
- */
-static const char *skip_paths(parser_state *state) {
- while (true) {
- const char *arg = state->argv[state->i];
- if (!arg
- || arg[0] == '-'
- || strcmp(arg, "(") == 0
- || strcmp(arg, ")") == 0
- || strcmp(arg, "!") == 0
- || strcmp(arg, ",") == 0) {
- return arg;
- }
-
- if (!cmdline_add_root(state->cl, arg)) {
- return NULL;
- }
-
- ++state->i;
- }
-}
-
-/**
- * -executable, -readable, -writable action.
- */
-static bool eval_access(const expression *expr, eval_state *state) {
- struct BFTW *ftwbuf = state->ftwbuf;
- return faccessat(ftwbuf->at_fd, ftwbuf->at_path, expr->idata, AT_SYMLINK_NOFOLLOW) == 0;
-}
-
-/**
- * -delete action.
- */
-static bool eval_delete(const expression *expr, eval_state *state) {
- struct BFTW *ftwbuf = state->ftwbuf;
-
- int flag = 0;
- if (ftwbuf->typeflag == BFTW_DIR) {
- flag |= AT_REMOVEDIR;
- }
-
- if (unlinkat(ftwbuf->at_fd, ftwbuf->at_path, flag) != 0) {
- print_error(state->cl->colors, ftwbuf->path, errno);
- state->action = BFTW_STOP;
- }
-
- return true;
-}
-
-/**
- * -prune action.
- */
-static bool eval_prune(const expression *expr, eval_state *state) {
- state->action = BFTW_SKIP_SUBTREE;
- return true;
-}
-
-/**
- * -hidden test.
- */
-static bool eval_hidden(const expression *expr, eval_state *state) {
- struct BFTW *ftwbuf = state->ftwbuf;
- return ftwbuf->nameoff > 0 && ftwbuf->path[ftwbuf->nameoff] == '.';
-}
-
-/**
- * -nohidden action.
- */
-static bool eval_nohidden(const expression *expr, eval_state *state) {
- if (eval_hidden(expr, state)) {
- eval_prune(expr, state);
- return false;
- } else {
- return true;
- }
-}
-
-/**
- * -name test.
- */
-static bool eval_name(const expression *expr, eval_state *state) {
- struct BFTW *ftwbuf = state->ftwbuf;
- return fnmatch(expr->sdata, ftwbuf->path + ftwbuf->nameoff, 0) == 0;
-}
-
-/**
- * -path test.
- */
-static bool eval_path(const expression *expr, eval_state *state) {
- struct BFTW *ftwbuf = state->ftwbuf;
- return fnmatch(expr->sdata, ftwbuf->path, 0) == 0;
-}
-
-/**
- * -print action.
- */
-static bool eval_print(const expression *expr, eval_state *state) {
- color_table *colors = state->cl->colors;
- if (colors) {
- fill_statbuf(state);
- }
- pretty_print(colors, state->ftwbuf);
- return true;
-}
-
-/**
- * -print0 action.
- */
-static bool eval_print0(const expression *expr, eval_state *state) {
- const char *path = state->ftwbuf->path;
- fwrite(path, 1, strlen(path) + 1, stdout);
- return true;
-}
-
-/**
- * -quit action.
- */
-static bool eval_quit(const expression *expr, eval_state *state) {
- state->action = BFTW_STOP;
- return true;
-}
-
-/**
- * -type test.
- */
-static bool eval_type(const expression *expr, eval_state *state) {
- return state->ftwbuf->typeflag == expr->idata;
-}
-
-/**
- * Create a new option expression.
- */
-static expression *new_option(parser_state *state, const char *option) {
- if (state->warn && state->non_option_seen) {
- fprintf(stderr,
- "The '%s' option applies to the entire command line.\n"
- "For clarity, place it before any non-option arguments.\n\n",
- option);
- }
-
- return &expr_true;
-}
-
-/**
- * Create a new positional option expression.
- */
-static expression *new_positional_option(parser_state *state) {
- return &expr_true;
-}
-
-/**
- * Create a new test expression.
- */
-static expression *new_test(parser_state *state, eval_fn *eval) {
- state->non_option_seen = true;
- return new_expression(eval);
-}
-
-/**
- * Create a new test expression with integer data.
- */
-static expression *new_test_idata(parser_state *state, eval_fn *eval, int idata) {
- expression *test = new_test(state, eval);
- if (test) {
- test->idata = idata;
- }
- return test;
-}
-
-/**
- * Create a new test expression with string data.
- */
-static expression *new_test_sdata(parser_state *state, eval_fn *eval, const char *sdata) {
- expression *test = new_test(state, eval);
- if (test) {
- test->sdata = sdata;
- }
- return test;
-}
-
-/**
- * Create a new action expression.
- */
-static expression *new_action(parser_state *state, eval_fn *eval) {
- if (eval != eval_nohidden && eval != eval_prune) {
- state->implicit_print = false;
- }
-
- state->non_option_seen = true;
-
- return new_expression(eval);
-}
-
-/**
- * Parse an integer.
- */
-static bool parse_int(const char *str, int *value) {
- char *endptr;
- long result = strtol(str, &endptr, 10);
-
- if (*str == '\0' || *endptr != '\0') {
- return false;
- }
-
- if (result < INT_MIN || result > INT_MAX) {
- return false;
- }
-
- *value = result;
- return true;
-}
-
-/**
- * Parse a test that takes a string argument.
- */
-static expression *parse_test_sdata(parser_state *state, const char *test, eval_fn *eval) {
- const char *arg = state->argv[state->i];
- if (!arg) {
- fprintf(stderr, "%s needs a value.\n", test);
- return NULL;
- }
-
- ++state->i;
-
- return new_test_sdata(state, eval, arg);
-}
-
-/**
- * Parse -{min,max}depth N.
- */
-static expression *parse_depth(parser_state *state, const char *option, int *depth) {
- const char *arg = state->argv[state->i];
- if (!arg) {
- fprintf(stderr, "%s needs a value.\n", option);
- return NULL;
- }
-
- ++state->i;
-
- if (!parse_int(arg, depth)) {
- fprintf(stderr, "%s is not a valid integer.\n", arg);
- return NULL;
- }
-
- return new_option(state, option);
-}
-
-/**
- * Parse -type [bcdpfls].
- */
-static expression *parse_type(parser_state *state) {
- const char *arg = state->argv[state->i];
- if (!arg) {
- fputs("-type needs a value.\n", stderr);
- return NULL;
- }
-
- int typeflag = BFTW_UNKNOWN;
-
- switch (arg[0]) {
- case 'b':
- typeflag = BFTW_BLK;
- break;
- case 'c':
- typeflag = BFTW_CHR;
- break;
- case 'd':
- typeflag = BFTW_DIR;
- break;
- case 'p':
- typeflag = BFTW_FIFO;
- break;
- case 'f':
- typeflag = BFTW_REG;
- break;
- case 'l':
- typeflag = BFTW_LNK;
- break;
- case 's':
- typeflag = BFTW_SOCK;
- break;
- }
-
- if (typeflag == BFTW_UNKNOWN || arg[1] != '\0') {
- fprintf(stderr, "Unknown type flag '%s'.\n", arg);
- return NULL;
- }
-
- ++state->i;
-
- return new_test_idata(state, eval_type, typeflag);
-}
-
-/**
- * LITERAL : OPTION
- * | TEST
- * | ACTION
- */
-static expression *parse_literal(parser_state *state) {
- // Paths are already skipped at this point
- const char *arg = state->argv[state->i++];
-
- if (strcmp(arg, "-color") == 0) {
- state->cl->color = true;
- return new_option(state, arg);
- } else if (strcmp(arg, "-nocolor") == 0) {
- state->cl->color = false;
- return new_option(state, arg);
- } else if (strcmp(arg, "-delete") == 0) {
- state->cl->flags |= BFTW_DEPTH;
- return new_action(state, eval_delete);
- } else if (strcmp(arg, "-d") == 0 || strcmp(arg, "-depth") == 0) {
- state->cl->flags |= BFTW_DEPTH;
- return new_option(state, arg);
- } else if (strcmp(arg, "-executable") == 0) {
- return new_test_idata(state, eval_access, X_OK);
- } else if (strcmp(arg, "-false") == 0) {
- return &expr_false;
- } else if (strcmp(arg, "-hidden") == 0) {
- return new_test(state, eval_hidden);
- } else if (strcmp(arg, "-nohidden") == 0) {
- return new_action(state, eval_nohidden);
- } else if (strcmp(arg, "-mindepth") == 0) {
- return parse_depth(state, arg, &state->cl->mindepth);
- } else if (strcmp(arg, "-maxdepth") == 0) {
- return parse_depth(state, arg, &state->cl->maxdepth);
- } else if (strcmp(arg, "-name") == 0) {
- return parse_test_sdata(state, arg, eval_name);
- } else if (strcmp(arg, "-path") == 0 || strcmp(arg, "-wholename") == 0) {
- return parse_test_sdata(state, arg, eval_path);
- } else if (strcmp(arg, "-print") == 0) {
- return new_action(state, eval_print);
- } else if (strcmp(arg, "-print0") == 0) {
- return new_action(state, eval_print0);
- } else if (strcmp(arg, "-prune") == 0) {
- return new_action(state, eval_prune);
- } else if (strcmp(arg, "-quit") == 0) {
- return new_action(state, eval_quit);
- } else if (strcmp(arg, "-readable") == 0) {
- return new_test_idata(state, eval_access, R_OK);
- } else if (strcmp(arg, "-true") == 0) {
- return &expr_true;
- } else if (strcmp(arg, "-type") == 0) {
- return parse_type(state);
- } else if (strcmp(arg, "-warn") == 0) {
- state->warn = true;
- return new_positional_option(state);
- } else if (strcmp(arg, "-nowarn") == 0) {
- state->warn = false;
- return new_positional_option(state);
- } else if (strcmp(arg, "-writable") == 0) {
- return new_test_idata(state, eval_access, W_OK);
- } else {
- fprintf(stderr, "Unknown argument '%s'.\n", arg);
- return NULL;
- }
-}
-
-/**
- * Evaluate a negation.
- */
-static bool eval_not(const expression *expr, eval_state *state) {
- return !expr->rhs->eval(expr, state);
-}
-
-/**
- * Create a "not" expression.
- */
-static expression *new_not_expression(expression *rhs) {
- if (rhs == &expr_true) {
- return &expr_false;
- } else if (rhs == &expr_false) {
- return &expr_true;
- } else {
- return new_unary_expression(rhs, eval_not);
- }
-}
-
-/**
- * FACTOR : "(" EXPR ")"
- * | "!" FACTOR | "-not" FACTOR
- * | LITERAL
- */
-static expression *parse_factor(parser_state *state) {
- const char *arg = skip_paths(state);
- if (!arg) {
- fputs("Expression terminated prematurely.\n", stderr);
- return NULL;
- }
-
- if (strcmp(arg, "(") == 0) {
- ++state->i;
- expression *expr = parse_expression(state);
- if (!expr) {
- return NULL;
- }
-
- arg = skip_paths(state);
- if (!arg || strcmp(arg, ")") != 0) {
- fputs("Expected a ')'.\n", stderr);
- free_expression(expr);
- return NULL;
- }
- ++state->i;
-
- return expr;
- } else if (strcmp(arg, "!") == 0 || strcmp(arg, "-not") == 0) {
- ++state->i;
-
- expression *factor = parse_factor(state);
- if (!factor) {
- return NULL;
- }
-
- return new_not_expression(factor);
- } else {
- return parse_literal(state);
- }
-}
-
-/**
- * Evaluate a conjunction.
- */
-static bool eval_and(const expression *expr, eval_state *state) {
- return expr->lhs->eval(expr->lhs, state) && expr->rhs->eval(expr->rhs, state);
-}
-
-/**
- * Create an "and" expression.
- */
-static expression *new_and_expression(expression *lhs, expression *rhs) {
- if (lhs == &expr_true) {
- return rhs;
- } else if (lhs == &expr_false) {
- free_expression(rhs);
- return lhs;
- } else if (rhs == &expr_true) {
- return lhs;
- } else {
- return new_binary_expression(lhs, rhs, eval_and);
- }
-}
-
-/**
- * TERM : FACTOR
- * | TERM FACTOR
- * | TERM "-a" FACTOR
- * | TERM "-and" FACTOR
- */
-static expression *parse_term(parser_state *state) {
- expression *term = parse_factor(state);
-
- while (term) {
- const char *arg = skip_paths(state);
- if (!arg) {
- break;
- }
-
- if (strcmp(arg, "-o") == 0 || strcmp(arg, "-or") == 0
- || strcmp(arg, ",") == 0
- || strcmp(arg, ")") == 0) {
- break;
- }
-
- if (strcmp(arg, "-a") == 0 || strcmp(arg, "-and") == 0) {
- ++state->i;
- }
-
- expression *lhs = term;
- expression *rhs = parse_factor(state);
- if (!rhs) {
- free_expression(lhs);
- return NULL;
- }
-
- term = new_and_expression(lhs, rhs);
- }
-
- return term;
-}
-
-/**
- * Evaluate a disjunction.
- */
-static bool eval_or(const expression *expr, eval_state *state) {
- return expr->lhs->eval(expr->lhs, state) || expr->rhs->eval(expr->rhs, state);
-}
-
-/**
- * Create an "or" expression.
- */
-static expression *new_or_expression(expression *lhs, expression *rhs) {
- if (lhs == &expr_true) {
- free_expression(rhs);
- return lhs;
- } else if (lhs == &expr_false) {
- return rhs;
- } else if (rhs == &expr_false) {
- return lhs;
- } else {
- return new_binary_expression(lhs, rhs, eval_or);
- }
-}
-
-/**
- * CLAUSE : TERM
- * | CLAUSE "-o" TERM
- * | CLAUSE "-or" TERM
- */
-static expression *parse_clause(parser_state *state) {
- expression *clause = parse_term(state);
-
- while (clause) {
- const char *arg = skip_paths(state);
- if (!arg) {
- break;
- }
-
- if (strcmp(arg, "-o") != 0 && strcmp(arg, "-or") != 0) {
- break;
- }
-
- ++state->i;
-
- expression *lhs = clause;
- expression *rhs = parse_term(state);
- if (!rhs) {
- free_expression(lhs);
- return NULL;
- }
-
- clause = new_or_expression(lhs, rhs);
- }
-
- return clause;
-}
-
-/**
- * Evaluate the comma operator.
- */
-static bool eval_comma(const expression *expr, eval_state *state) {
- expr->lhs->eval(expr->lhs, state);
- return expr->rhs->eval(expr->rhs, state);
-}
-
-/**
- * Create a "comma" expression.
- */
-static expression *new_comma_expression(expression *lhs, expression *rhs) {
- if (lhs == &expr_true || lhs == &expr_false) {
- return rhs;
- } else {
- return new_binary_expression(lhs, rhs, eval_comma);
- }
-}
-
-/**
- * EXPR : CLAUSE
- * | EXPR "," CLAUSE
- */
-static expression *parse_expression(parser_state *state) {
- expression *expr = parse_clause(state);
-
- while (expr) {
- const char *arg = skip_paths(state);
- if (!arg) {
- break;
- }
-
- if (strcmp(arg, ",") != 0) {
- break;
- }
-
- ++state->i;
-
- expression *lhs = expr;
- expression *rhs = parse_clause(state);
- if (!rhs) {
- free_expression(lhs);
- return NULL;
- }
-
- expr = new_comma_expression(lhs, rhs);
- }
-
- return expr;
-}
-
-/**
- * Parse the command line.
- */
-static cmdline *parse_cmdline(int argc, char *argv[]) {
- cmdline *cl = malloc(sizeof(cmdline));
- if (!cl) {
- goto fail;
- }
-
- cl->roots = NULL;
- cl->nroots = 0;
- cl->colors = NULL;
- cl->color = isatty(STDOUT_FILENO);
- cl->mindepth = 0;
- cl->maxdepth = INT_MAX;
- cl->flags = BFTW_RECOVER;
- cl->expr = &expr_true;
-
- parser_state state = {
- .cl = cl,
- .argv = argv,
- .i = 1,
- .implicit_print = true,
- .warn = true,
- .non_option_seen = false,
- };
-
- if (skip_paths(&state)) {
- cl->expr = parse_expression(&state);
- if (!cl->expr) {
- goto fail;
- }
- }
-
- if (state.i < argc) {
- fprintf(stderr, "Unexpected argument '%s'.\n", argv[state.i]);
- goto fail;
- }
-
- if (state.implicit_print) {
- expression *print = new_expression(eval_print);
- if (!print) {
- goto fail;
- }
-
- cl->expr = new_and_expression(cl->expr, print);
- if (!cl->expr) {
- goto fail;
- }
- }
-
- if (cl->nroots == 0) {
- if (!cmdline_add_root(cl, ".")) {
- goto fail;
- }
- }
-
- if (cl->color) {
- cl->colors = parse_colors(getenv("LS_COLORS"));
- }
-
- return cl;
-
-fail:
- free_cmdline(cl);
- return NULL;
-}
-
-/**
- * Infer the number of open file descriptors we're allowed to have.
- */
-static int infer_nopenfd() {
- int ret = 4096;
-
- struct rlimit rl;
- if (getrlimit(RLIMIT_NOFILE, &rl) == 0) {
- if (rl.rlim_cur != RLIM_INFINITY) {
- ret = rl.rlim_cur;
- }
- }
-
- // Account for std{in,out,err}
- if (ret > 3) {
- ret -= 3;
- }
-
- return ret;
-}
-
-/**
- * bftw() callback.
- */
-static bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) {
- const cmdline *cl = ptr;
-
- if (ftwbuf->typeflag == BFTW_ERROR) {
- print_error(cl->colors, ftwbuf->path, ftwbuf->error);
- return BFTW_SKIP_SUBTREE;
- }
-
- eval_state state = {
- .ftwbuf = ftwbuf,
- .cl = cl,
- .action = BFTW_CONTINUE,
- };
-
- if (ftwbuf->depth >= cl->maxdepth) {
- state.action = BFTW_SKIP_SUBTREE;
- }
-
- // In -depth mode, only handle directories on the BFTW_POST visit
- bftw_visit expected_visit = BFTW_PRE;
- if ((cl->flags & BFTW_DEPTH)
- && ftwbuf->typeflag == BFTW_DIR
- && ftwbuf->depth < cl->maxdepth) {
- expected_visit = BFTW_POST;
- }
-
- if (ftwbuf->visit == expected_visit
- && ftwbuf->depth >= cl->mindepth
- && ftwbuf->depth <= cl->maxdepth) {
- cl->expr->eval(cl->expr, &state);
- }
-
- return state.action;
-}
-
-/**
- * Evaluate the command line.
- */
-static int eval_cmdline(cmdline *cl) {
- int ret = 0;
- int nopenfd = infer_nopenfd();
-
- for (size_t i = 0; i < cl->nroots; ++i) {
- if (bftw(cl->roots[i], cmdline_callback, nopenfd, cl->flags, cl) != 0) {
- ret = -1;
- perror("bftw()");
- }
- }
-
- return ret;
-}
-
-int main(int argc, char *argv[]) {
- int ret = EXIT_FAILURE;
-
- cmdline *cl = parse_cmdline(argc, argv);
- if (cl) {
- if (eval_cmdline(cl) == 0) {
- ret = EXIT_SUCCESS;
- }
- }
-
- free_cmdline(cl);
- return ret;
-}