diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2015-11-29 12:00:05 -0500 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2015-11-29 12:00:05 -0500 |
commit | da51ac27f1849ad94753277650435ae482a0423f (patch) | |
tree | d194bda63df14c5ec6bd32601d8a7db189f6c39e | |
parent | f81be72020a886b5eca21bc1d8a5b0d62b9954d9 (diff) | |
download | bfs-da51ac27f1849ad94753277650435ae482a0423f.tar.xz |
Split out parsing code.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | bfs.h | 118 | ||||
-rw-r--r-- | eval.c | 263 | ||||
-rw-r--r-- | main.c | 27 | ||||
-rw-r--r-- | parse.c (renamed from bfs.c) | 352 |
5 files changed, 412 insertions, 350 deletions
@@ -24,7 +24,7 @@ ALL_LDFLAGS = $(ALL_CFLAGS) $(LDFLAGS) all: bfs -bfs: bfs.o bftw.o color.o +bfs: bftw.o color.o eval.o main.o parse.o $(CC) $(ALL_LDFLAGS) $^ -o $@ %.o: %.c @@ -0,0 +1,118 @@ +/********************************************************************* + * 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. * + *********************************************************************/ + +#ifndef BFS_H +#define BFS_H + +#include "color.h" +#include <stdbool.h> +#include <stddef.h> + +/** + * The parsed command line. + */ +typedef struct cmdline cmdline; + +/** + * A command line expression. + */ +typedef struct expression expression; + +/** + * Ephemeral state for evaluating an expression. + */ +typedef struct eval_state eval_state; + +/** + * 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 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; +}; + +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; +}; + +/** + * Parse the command line. + */ +cmdline *parse_cmdline(int argc, char *argv[]); + +/** + * Evaluate the command line. + */ +int eval_cmdline(cmdline *cl); + +/** + * Free the parsed command line. + */ +void free_cmdline(cmdline *cl); + +// Predicate evaluation functions +bool eval_access(const expression *expr, eval_state *state); +bool eval_delete(const expression *expr, eval_state *state); +bool eval_false(const expression *expr, eval_state *state); +bool eval_hidden(const expression *expr, eval_state *state); +bool eval_name(const expression *expr, eval_state *state); +bool eval_nohidden(const expression *expr, eval_state *state); +bool eval_path(const expression *expr, eval_state *state); +bool eval_print(const expression *expr, eval_state *state); +bool eval_print0(const expression *expr, eval_state *state); +bool eval_prune(const expression *expr, eval_state *state); +bool eval_quit(const expression *expr, eval_state *state); +bool eval_true(const expression *expr, eval_state *state); +bool eval_type(const expression *expr, eval_state *state); + +// Operator evaluation functions +bool eval_not(const expression *expr, eval_state *state); +bool eval_and(const expression *expr, eval_state *state); +bool eval_or(const expression *expr, eval_state *state); +bool eval_comma(const expression *expr, eval_state *state); + +#endif // BFS_H @@ -0,0 +1,263 @@ +#include "bfs.h" +#include "bftw.h" +#include <errno.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <stdio.h> +#include <string.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <unistd.h> + +struct eval_state { + /** 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; +}; + +/** + * 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()"); + } +} + +/** + * -true test. + */ +bool eval_true(const expression *expr, eval_state *state) { + return true; +} + +/** + * -false test. + */ +bool eval_false(const expression *expr, eval_state *state) { + return false; +} + +/** + * -executable, -readable, -writable action. + */ +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. + */ +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. + */ +bool eval_prune(const expression *expr, eval_state *state) { + state->action = BFTW_SKIP_SUBTREE; + return true; +} + +/** + * -hidden test. + */ +bool eval_hidden(const expression *expr, eval_state *state) { + struct BFTW *ftwbuf = state->ftwbuf; + return ftwbuf->nameoff > 0 && ftwbuf->path[ftwbuf->nameoff] == '.'; +} + +/** + * -nohidden action. + */ +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. + */ +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. + */ +bool eval_path(const expression *expr, eval_state *state) { + struct BFTW *ftwbuf = state->ftwbuf; + return fnmatch(expr->sdata, ftwbuf->path, 0) == 0; +} + +/** + * -print action. + */ +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. + */ +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. + */ +bool eval_quit(const expression *expr, eval_state *state) { + state->action = BFTW_STOP; + return true; +} + +/** + * -type test. + */ +bool eval_type(const expression *expr, eval_state *state) { + return state->ftwbuf->typeflag == expr->idata; +} + +/** + * Evaluate a negation. + */ +bool eval_not(const expression *expr, eval_state *state) { + return !expr->rhs->eval(expr, state); +} + +/** + * Evaluate a conjunction. + */ +bool eval_and(const expression *expr, eval_state *state) { + return expr->lhs->eval(expr->lhs, state) && expr->rhs->eval(expr->rhs, state); +} + +/** + * Evaluate a disjunction. + */ +bool eval_or(const expression *expr, eval_state *state) { + return expr->lhs->eval(expr->lhs, state) || expr->rhs->eval(expr->rhs, state); +} + +/** + * Evaluate the comma operator. + */ +bool eval_comma(const expression *expr, eval_state *state) { + expr->lhs->eval(expr->lhs, state); + return expr->rhs->eval(expr->rhs, state); +} + +/** + * 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. + */ +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; +} @@ -0,0 +1,27 @@ +/********************************************************************* + * 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 "bfs.h" +#include <stdlib.h> + +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; +} @@ -1,94 +1,11 @@ -/********************************************************************* - * 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 "bfs.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) { @@ -107,13 +24,6 @@ static expression *new_expression(eval_fn *eval) { } /** - * -true test. - */ -static bool eval_true(const expression *expr, eval_state *state) { - return true; -} - -/** * Singleton true expression instance. */ static expression expr_true = { @@ -125,13 +35,6 @@ static expression expr_true = { }; /** - * -false test. - */ -static bool eval_false(const expression *expr, eval_state *state) { - return false; -} - -/** * Singleton false expression instance. */ static expression expr_false = { @@ -185,33 +88,10 @@ static expression *new_binary_expression(expression *lhs, expression *rhs, 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) { +void free_cmdline(cmdline *cl) { if (cl) { free_expression(cl->expr); free_colors(cl->colors); @@ -284,113 +164,6 @@ static const char *skip_paths(parser_state *state) { } /** - * -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) { @@ -620,13 +393,6 @@ static expression *parse_literal(parser_state *state) { } /** - * 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) { @@ -682,13 +448,6 @@ static expression *parse_factor(parser_state *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) { @@ -743,13 +502,6 @@ static expression *parse_term(parser_state *state) { } /** - * 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) { @@ -799,14 +551,6 @@ static expression *parse_clause(parser_state *state) { } /** - * 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) { @@ -852,7 +596,7 @@ static expression *parse_expression(parser_state *state) { /** * Parse the command line. */ -static cmdline *parse_cmdline(int argc, char *argv[]) { +cmdline *parse_cmdline(int argc, char *argv[]) { cmdline *cl = malloc(sizeof(cmdline)); if (!cl) { goto fail; @@ -916,93 +660,3 @@ 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; -} |