From 62bbbe1a4165f63b31c68b1595ecb0e67d7af3dc Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 27 Sep 2020 12:55:55 -0400 Subject: Rename struct cmdline to bfs_ctx The API remains similar, with some added accessor functions for lazy initialization of the pwcache and mtab. --- Makefile | 1 + cmdline.h | 143 ------------------- ctx.c | 217 ++++++++++++++++++++++++++++ ctx.h | 180 +++++++++++++++++++++++ diag.c | 48 +++---- diag.h | 20 +-- eval.c | 146 ++++++++++--------- eval.h | 12 ++ exec.c | 24 ++-- exec.h | 17 +-- main.c | 24 ++-- mtab.c | 8 +- mtab.h | 6 +- opt.c | 77 +++++----- opt.h | 37 +++++ parse.c | 482 ++++++++++++++++++++++---------------------------------------- parse.h | 38 +++++ printf.c | 40 +++--- printf.h | 16 ++- 19 files changed, 880 insertions(+), 656 deletions(-) delete mode 100644 cmdline.h create mode 100644 ctx.c create mode 100644 ctx.h create mode 100644 opt.h create mode 100644 parse.h diff --git a/Makefile b/Makefile index 72abf89..ec5e6d0 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,7 @@ all: bfs tests/mksock tests/trie tests/xtimegm bfs: \ bftw.o \ color.o \ + ctx.o \ darray.o \ diag.o \ dstring.o \ diff --git a/cmdline.h b/cmdline.h deleted file mode 100644 index 309dfa3..0000000 --- a/cmdline.h +++ /dev/null @@ -1,143 +0,0 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ - -/** - * Representation of the parsed command line. - */ - -#ifndef BFS_CMDLINE_H -#define BFS_CMDLINE_H - -#include "color.h" -#include "trie.h" - -/** - * Various debugging flags. - */ -enum debug_flags { - /** Print cost estimates. */ - DEBUG_COST = 1 << 0, - /** Print executed command details. */ - DEBUG_EXEC = 1 << 1, - /** Print optimization details. */ - DEBUG_OPT = 1 << 2, - /** Print rate information. */ - DEBUG_RATES = 1 << 3, - /** Trace the filesystem traversal. */ - DEBUG_SEARCH = 1 << 4, - /** Trace all stat() calls. */ - DEBUG_STAT = 1 << 5, - /** Print the parse tree. */ - DEBUG_TREE = 1 << 6, - /** All debug flags. */ - DEBUG_ALL = (1 << 7) - 1, -}; - -/** - * The parsed command line. - */ -struct cmdline { - /** The unparsed command line arguments. */ - char **argv; - - /** The root paths. */ - const char **paths; - - /** Color data. */ - struct colors *colors; - /** Colored stdout. */ - CFILE *cout; - /** Colored stderr. */ - CFILE *cerr; - - /** User table. */ - struct bfs_users *users; - /** The error that occurred parsing the user table, if any. */ - int users_error; - /** Group table. */ - struct bfs_groups *groups; - /** The error that occurred parsing the group table, if any. */ - int groups_error; - - /** Table of mounted file systems. */ - struct bfs_mtab *mtab; - /** The error that occurred parsing the mount table, if any. */ - int mtab_error; - - /** -mindepth option. */ - int mindepth; - /** -maxdepth option. */ - int maxdepth; - - /** bftw() flags. */ - enum bftw_flags flags; - /** bftw() search strategy. */ - enum bftw_strategy strategy; - - /** Optimization level (-O). */ - int optlevel; - /** Debugging flags (-D). */ - enum debug_flags debug; - /** Whether to ignore deletions that race with bfs (-ignore_readdir_race). */ - bool ignore_races; - /** Whether to only return unique files (-unique). */ - bool unique; - /** Whether to print warnings (-warn/-nowarn). */ - bool warn; - /** Whether to only handle paths with xargs-safe characters (-X). */ - bool xargs_safe; - - /** An expression for files to filter out. */ - struct expr *exclude; - /** The main command line expression. */ - struct expr *expr; - - /** All the open files owned by the command line. */ - struct trie open_files; - /** The number of open files owned by the command line. */ - int nopen_files; -}; - -/** - * Parse the command line. - */ -struct cmdline *parse_cmdline(int argc, char *argv[]); - -/** - * Dump the parsed command line. - */ -void dump_cmdline(const struct cmdline *cmdline, enum debug_flags flag); - -/** - * Optimize the parsed command line. - * - * @return 0 if successful, -1 on error. - */ -int optimize_cmdline(struct cmdline *cmdline); - -/** - * Evaluate the command line. - */ -int eval_cmdline(const struct cmdline *cmdline); - -/** - * Free the parsed command line. - * - * @return 0 if successful, -1 on error. - */ -int free_cmdline(struct cmdline *cmdline); - -#endif // BFS_CMDLINE_H diff --git a/ctx.c b/ctx.c new file mode 100644 index 0000000..95e668c --- /dev/null +++ b/ctx.c @@ -0,0 +1,217 @@ +/**************************************************************************** + * bfs * + * Copyright (C) 2015-2020 Tavian Barnes * + * * + * Permission to use, copy, modify, and/or distribute this software for any * + * purpose with or without fee is hereby granted. * + * * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * + ****************************************************************************/ + +#include "ctx.h" +#include "darray.h" +#include "diag.h" +#include "expr.h" +#include "mtab.h" +#include "pwcache.h" +#include "trie.h" +#include +#include +#include + +struct bfs_ctx *bfs_ctx_new(void) { + struct bfs_ctx *ctx = malloc(sizeof(*ctx)); + if (!ctx) { + return NULL; + } + + ctx->argv = NULL; + ctx->paths = NULL; + ctx->expr = NULL; + ctx->exclude = NULL; + + ctx->mindepth = 0; + ctx->maxdepth = INT_MAX; + ctx->flags = BFTW_RECOVER; + ctx->strategy = BFTW_BFS; + ctx->optlevel = 3; + ctx->debug = 0; + ctx->ignore_races = false; + ctx->unique = false; + ctx->warn = false; + ctx->xargs_safe = false; + + ctx->colors = NULL; + ctx->cout = NULL; + ctx->cerr = NULL; + + ctx->users = NULL; + ctx->users_error = 0; + ctx->groups = NULL; + ctx->groups_error = 0; + + ctx->mtab = NULL; + ctx->mtab_error = 0; + + trie_init(&ctx->files); + ctx->nfiles = 0; + + return ctx; +} + +const struct bfs_users *bfs_ctx_users(const struct bfs_ctx *ctx) { + struct bfs_ctx *mut = (struct bfs_ctx *)ctx; + + if (mut->users_error) { + errno = mut->users_error; + } else if (!mut->users) { + mut->users = bfs_parse_users(); + if (!mut->users) { + mut->users_error = errno; + } + } + + return mut->users; +} + +const struct bfs_groups *bfs_ctx_groups(const struct bfs_ctx *ctx) { + struct bfs_ctx *mut = (struct bfs_ctx *)ctx; + + if (mut->groups_error) { + errno = mut->groups_error; + } else if (!mut->groups) { + mut->groups = bfs_parse_groups(); + if (!mut->groups) { + mut->groups_error = errno; + } + } + + return mut->groups; +} + +const struct bfs_mtab *bfs_ctx_mtab(const struct bfs_ctx *ctx) { + struct bfs_ctx *mut = (struct bfs_ctx *)ctx; + + if (mut->mtab_error) { + errno = mut->mtab_error; + } else if (!mut->mtab) { + mut->mtab = bfs_parse_mtab(); + if (!mut->mtab) { + mut->mtab_error = errno; + } + } + + return mut->mtab; +} + +/** + * An open file tracked by the bfs context. + */ +struct bfs_ctx_file { + /** The file itself. */ + CFILE *cfile; + /** The path to the file (for diagnostics). */ + const char *path; +}; + +CFILE *bfs_ctx_open(struct bfs_ctx *ctx, const char *path, bool use_color) { + CFILE *cfile = cfopen(path, use_color ? ctx->colors : NULL); + if (!cfile) { + goto out; + } + + struct bfs_stat sb; + if (bfs_stat(fileno(cfile->file), NULL, 0, &sb) != 0) { + goto out_close; + } + + bfs_file_id id; + bfs_stat_id(&sb, &id); + + struct trie_leaf *leaf = trie_insert_mem(&ctx->files, id, sizeof(id)); + if (!leaf) { + goto out_close; + } + + if (leaf->value) { + struct bfs_ctx_file *ctx_file = leaf->value; + cfclose(cfile); + cfile = ctx_file->cfile; + goto out; + } + + struct bfs_ctx_file *ctx_file = malloc(sizeof(*ctx_file)); + if (!ctx_file) { + trie_remove(&ctx->files, leaf); + goto out_close; + } + + ctx_file->cfile = cfile; + ctx_file->path = path; + leaf->value = ctx_file; + ++ctx->nfiles; + + goto out; + +out_close: + cfclose(cfile); + cfile = NULL; +out: + return cfile; +} + +int bfs_ctx_free(struct bfs_ctx *ctx) { + int ret = 0; + + if (ctx) { + CFILE *cout = ctx->cout; + CFILE *cerr = ctx->cerr; + + free_expr(ctx->expr); + free_expr(ctx->exclude); + + bfs_free_mtab(ctx->mtab); + + bfs_free_groups(ctx->groups); + bfs_free_users(ctx->users); + + struct trie_leaf *leaf; + while ((leaf = trie_first_leaf(&ctx->files))) { + struct bfs_ctx_file *ctx_file = leaf->value; + + if (cfclose(ctx_file->cfile) != 0) { + if (cerr) { + bfs_error(ctx, "'%s': %m.\n", ctx_file->path); + } + ret = -1; + } + + free(ctx_file); + trie_remove(&ctx->files, leaf); + } + trie_destroy(&ctx->files); + + if (cout && fflush(cout->file) != 0) { + if (cerr) { + bfs_error(ctx, "standard output: %m.\n"); + } + ret = -1; + } + + cfclose(cout); + cfclose(cerr); + + free_colors(ctx->colors); + darray_free(ctx->paths); + free(ctx->argv); + free(ctx); + } + + return ret; +} diff --git a/ctx.h b/ctx.h new file mode 100644 index 0000000..b638e74 --- /dev/null +++ b/ctx.h @@ -0,0 +1,180 @@ +/**************************************************************************** + * bfs * + * Copyright (C) 2015-2020 Tavian Barnes * + * * + * Permission to use, copy, modify, and/or distribute this software for any * + * purpose with or without fee is hereby granted. * + * * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * + ****************************************************************************/ + +/** + * bfs execution context. + */ + +#ifndef BFS_CTX_H +#define BFS_CTX_H + +#include "trie.h" +#include "color.h" + +/** + * Various debugging flags. + */ +enum debug_flags { + /** Print cost estimates. */ + DEBUG_COST = 1 << 0, + /** Print executed command details. */ + DEBUG_EXEC = 1 << 1, + /** Print optimization details. */ + DEBUG_OPT = 1 << 2, + /** Print rate information. */ + DEBUG_RATES = 1 << 3, + /** Trace the filesystem traversal. */ + DEBUG_SEARCH = 1 << 4, + /** Trace all stat() calls. */ + DEBUG_STAT = 1 << 5, + /** Print the parse tree. */ + DEBUG_TREE = 1 << 6, + /** All debug flags. */ + DEBUG_ALL = (1 << 7) - 1, +}; + +/** + * The execution context for bfs. + */ +struct bfs_ctx { + /** The unparsed command line arguments. */ + char **argv; + /** The root paths. */ + const char **paths; + /** The main command line expression. */ + struct expr *expr; + /** An expression for files to filter out. */ + struct expr *exclude; + + /** -mindepth option. */ + int mindepth; + /** -maxdepth option. */ + int maxdepth; + + /** bftw() flags. */ + enum bftw_flags flags; + /** bftw() search strategy. */ + enum bftw_strategy strategy; + + /** Optimization level (-O). */ + int optlevel; + /** Debugging flags (-D). */ + enum debug_flags debug; + /** Whether to ignore deletions that race with bfs (-ignore_readdir_race). */ + bool ignore_races; + /** Whether to only return unique files (-unique). */ + bool unique; + /** Whether to print warnings (-warn/-nowarn). */ + bool warn; + /** Whether to only handle paths with xargs-safe characters (-X). */ + bool xargs_safe; + + /** Color data. */ + struct colors *colors; + /** Colored stdout. */ + CFILE *cout; + /** Colored stderr. */ + CFILE *cerr; + + /** User table. */ + struct bfs_users *users; + /** The error that occurred parsing the user table, if any. */ + int users_error; + /** Group table. */ + struct bfs_groups *groups; + /** The error that occurred parsing the group table, if any. */ + int groups_error; + + /** Table of mounted file systems. */ + struct bfs_mtab *mtab; + /** The error that occurred parsing the mount table, if any. */ + int mtab_error; + + /** All the files owned by the context. */ + struct trie files; + /** The number of files owned by the context. */ + int nfiles; +}; + +/** + * @return + * A new bfs context, or NULL on failure. + */ +struct bfs_ctx *bfs_ctx_new(void); + +/** + * Get the users table. + * + * @param ctx + * The bfs context. + * @return + * The cached users table, or NULL on failure. + */ +const struct bfs_users *bfs_ctx_users(const struct bfs_ctx *ctx); + +/** + * Get the groups table. + * + * @param ctx + * The bfs context. + * @return + * The cached groups table, or NULL on failure. + */ +const struct bfs_groups *bfs_ctx_groups(const struct bfs_ctx *ctx); + +/** + * Get the mount table. + * + * @param ctx + * The bfs context. + * @return + * The cached mount table, or NULL on failure. + */ +const struct bfs_mtab *bfs_ctx_mtab(const struct bfs_ctx *ctx); + +/** + * Open a file for the bfs context. + * + * @param ctx + * The bfs context. + * @param use_color + * Whether to use colors if the file is a TTY. + * @return + * The opened file, or NULL on failure. + */ +CFILE *bfs_ctx_open(struct bfs_ctx *ctx, const char *path, bool use_color); + +/** + * Dump the parsed command line. + * + * @param ctx + * The bfs context. + * @param flag + * The -D flag that triggered the dump. + */ +void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag); + +/** + * Free a bfs context. + * + * @param ctx + * The context to free. + * @return + * 0 on success, -1 if any errors occurred. + */ +int bfs_ctx_free(struct bfs_ctx *ctx); + +#endif // BFS_CTX_H diff --git a/diag.c b/diag.c index fb1a295..7ed132e 100644 --- a/diag.c +++ b/diag.c @@ -15,7 +15,7 @@ ****************************************************************************/ #include "diag.h" -#include "cmdline.h" +#include "ctx.h" #include "color.h" #include "util.h" #include @@ -24,77 +24,77 @@ #include #include -void bfs_error(const struct cmdline *cmdline, const char *format, ...) { +void bfs_error(const struct bfs_ctx *ctx, const char *format, ...) { va_list args; va_start(args, format); - bfs_verror(cmdline, format, args); + bfs_verror(ctx, format, args); va_end(args); } -bool bfs_warning(const struct cmdline *cmdline, const char *format, ...) { +bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...) { va_list args; va_start(args, format); - bool ret = bfs_vwarning(cmdline, format, args); + bool ret = bfs_vwarning(ctx, format, args); va_end(args); return ret; } -bool bfs_debug(const struct cmdline *cmdline, enum debug_flags flag, const char *format, ...) { +bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...) { va_list args; va_start(args, format); - bool ret = bfs_vdebug(cmdline, flag, format, args); + bool ret = bfs_vdebug(ctx, flag, format, args); va_end(args); return ret; } -void bfs_verror(const struct cmdline *cmdline, const char *format, va_list args) { +void bfs_verror(const struct bfs_ctx *ctx, const char *format, va_list args) { int error = errno; - bfs_error_prefix(cmdline); + bfs_error_prefix(ctx); errno = error; - cvfprintf(cmdline->cerr, format, args); + cvfprintf(ctx->cerr, format, args); } -bool bfs_vwarning(const struct cmdline *cmdline, const char *format, va_list args) { +bool bfs_vwarning(const struct bfs_ctx *ctx, const char *format, va_list args) { int error = errno; - if (bfs_warning_prefix(cmdline)) { + if (bfs_warning_prefix(ctx)) { errno = error; - cvfprintf(cmdline->cerr, format, args); + cvfprintf(ctx->cerr, format, args); return true; } else { return false; } } -bool bfs_vdebug(const struct cmdline *cmdline, enum debug_flags flag, const char *format, va_list args) { +bool bfs_vdebug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, va_list args) { int error = errno; - if (bfs_debug_prefix(cmdline, flag)) { + if (bfs_debug_prefix(ctx, flag)) { errno = error; - cvfprintf(cmdline->cerr, format, args); + cvfprintf(ctx->cerr, format, args); return true; } else { return false; } } -void bfs_error_prefix(const struct cmdline *cmdline) { - cfprintf(cmdline->cerr, "${bld}%s:${rs} ${er}error:${rs} ", xbasename(cmdline->argv[0])); +void bfs_error_prefix(const struct bfs_ctx *ctx) { + cfprintf(ctx->cerr, "${bld}%s:${rs} ${er}error:${rs} ", xbasename(ctx->argv[0])); } -bool bfs_warning_prefix(const struct cmdline *cmdline) { - if (cmdline->warn) { - cfprintf(cmdline->cerr, "${bld}%s:${rs} ${wr}warning:${rs} ", xbasename(cmdline->argv[0])); +bool bfs_warning_prefix(const struct bfs_ctx *ctx) { + if (ctx->warn) { + cfprintf(ctx->cerr, "${bld}%s:${rs} ${wr}warning:${rs} ", xbasename(ctx->argv[0])); return true; } else { return false; } } -bool bfs_debug_prefix(const struct cmdline *cmdline, enum debug_flags flag) { - if (!(cmdline->debug & flag)) { +bool bfs_debug_prefix(const struct bfs_ctx *ctx, enum debug_flags flag) { + if (!(ctx->debug & flag)) { return false; } @@ -128,6 +128,6 @@ bool bfs_debug_prefix(const struct cmdline *cmdline, enum debug_flags flag) { break; } - cfprintf(cmdline->cerr, "${bld}%s:${rs} ${cyn}-D %s${rs}: ", xbasename(cmdline->argv[0]), str); + cfprintf(ctx->cerr, "${bld}%s:${rs} ${cyn}-D %s${rs}: ", xbasename(ctx->argv[0]), str); return true; } diff --git a/diag.h b/diag.h index 2e5513e..8faef07 100644 --- a/diag.h +++ b/diag.h @@ -21,7 +21,7 @@ #ifndef BFS_DIAG_H #define BFS_DIAG_H -#include "cmdline.h" +#include "ctx.h" #include "util.h" #include #include @@ -30,7 +30,7 @@ * Shorthand for printing error messages. */ BFS_FORMATTER(2, 3) -void bfs_error(const struct cmdline *cmdline, const char *format, ...); +void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); /** * Shorthand for printing warning messages. @@ -38,7 +38,7 @@ void bfs_error(const struct cmdline *cmdline, const char *format, ...); * @return Whether a warning was printed. */ BFS_FORMATTER(2, 3) -bool bfs_warning(const struct cmdline *cmdline, const char *format, ...); +bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); /** * Shorthand for printing debug messages. @@ -46,36 +46,36 @@ bool bfs_warning(const struct cmdline *cmdline, const char *format, ...); * @return Whether a debug message was printed. */ BFS_FORMATTER(3, 4) -bool bfs_debug(const struct cmdline *cmdline, enum debug_flags flag, const char *format, ...); +bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...); /** * bfs_error() variant that takes a va_list. */ -void bfs_verror(const struct cmdline *cmdline, const char *format, va_list args); +void bfs_verror(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_warning() variant that takes a va_list. */ -bool bfs_vwarning(const struct cmdline *cmdline, const char *format, va_list args); +bool bfs_vwarning(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_debug() variant that takes a va_list. */ -bool bfs_vdebug(const struct cmdline *cmdline, enum debug_flags flag, const char *format, va_list args); +bool bfs_vdebug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, va_list args); /** * Print the error message prefix. */ -void bfs_error_prefix(const struct cmdline *cmdline); +void bfs_error_prefix(const struct bfs_ctx *ctx); /** * Print the warning message prefix. */ -bool bfs_warning_prefix(const struct cmdline *cmdline); +bool bfs_warning_prefix(const struct bfs_ctx *ctx); /** * Print the debug message prefix. */ -bool bfs_debug_prefix(const struct cmdline *cmdline, enum debug_flags flag); +bool bfs_debug_prefix(const struct bfs_ctx *ctx, enum debug_flags flag); #endif // BFS_DIAG_H diff --git a/eval.c b/eval.c index 1498282..ca8c78f 100644 --- a/eval.c +++ b/eval.c @@ -20,7 +20,6 @@ #include "eval.h" #include "bftw.h" -#include "cmdline.h" #include "color.h" #include "darray.h" #include "diag.h" @@ -54,11 +53,11 @@ struct eval_state { /** Data about the current file. */ const struct BFTW *ftwbuf; - /** The parsed command line. */ - const struct cmdline *cmdline; + /** The bfs context. */ + const struct bfs_ctx *ctx; /** The bftw() callback return value. */ enum bftw_action action; - /** The eval_cmdline() return value. */ + /** The bfs_eval() return value. */ int *ret; /** Whether to quit immediately. */ bool quit; @@ -70,10 +69,10 @@ struct eval_state { BFS_FORMATTER(2, 3) static void eval_error(struct eval_state *state, const char *format, ...) { int error = errno; - const struct cmdline *cmdline = state->cmdline; - CFILE *cerr = cmdline->cerr; + const struct bfs_ctx *ctx = state->ctx; + CFILE *cerr = ctx->cerr; - bfs_error(cmdline, "%pP: ", state->ftwbuf); + bfs_error(ctx, "%pP: ", state->ftwbuf); va_list args; va_start(args, format); @@ -86,7 +85,7 @@ static void eval_error(struct eval_state *state, const char *format, ...) { * Check if an error should be ignored. */ static bool eval_should_ignore(const struct eval_state *state, int error) { - return state->cmdline->ignore_races + return state->ctx->ignore_races && is_nonexistence_error(error) && state->ftwbuf->depth > 0; } @@ -295,7 +294,13 @@ bool eval_nogroup(const struct expr *expr, struct eval_state *state) { return false; } - return bfs_getgrgid(state->cmdline->groups, statbuf->gid) == NULL; + const struct bfs_groups *groups = bfs_ctx_groups(state->ctx); + if (!groups) { + eval_report_error(state); + return false; + } + + return bfs_getgrgid(groups, statbuf->gid) == NULL; } /** @@ -307,7 +312,13 @@ bool eval_nouser(const struct expr *expr, struct eval_state *state) { return false; } - return bfs_getpwuid(state->cmdline->users, statbuf->uid) == NULL; + const struct bfs_users *users = bfs_ctx_users(state->ctx); + if (!users) { + eval_report_error(state); + return false; + } + + return bfs_getpwuid(users, statbuf->uid) == NULL; } /** @@ -341,18 +352,18 @@ bool eval_delete(const struct expr *expr, struct eval_state *state) { } /** Finish any pending -exec ... + operations. */ -static int eval_exec_finish(const struct expr *expr, const struct cmdline *cmdline) { +static int eval_exec_finish(const struct expr *expr, const struct bfs_ctx *ctx) { int ret = 0; if (expr->execbuf && bfs_exec_finish(expr->execbuf) != 0) { if (errno != 0) { - bfs_error(cmdline, "%s %s: %m.\n", expr->argv[0], expr->argv[1]); + bfs_error(ctx, "%s %s: %m.\n", expr->argv[0], expr->argv[1]); } ret = -1; } - if (expr->lhs && eval_exec_finish(expr->lhs, cmdline) != 0) { + if (expr->lhs && eval_exec_finish(expr->lhs, ctx) != 0) { ret = -1; } - if (expr->rhs && eval_exec_finish(expr->rhs, cmdline) != 0) { + if (expr->rhs && eval_exec_finish(expr->rhs, ctx) != 0) { ret = -1; } return ret; @@ -436,7 +447,13 @@ bool eval_fstype(const struct expr *expr, struct eval_state *state) { return false; } - const char *type = bfs_fstype(state->cmdline->mtab, statbuf); + const struct bfs_mtab *mtab = bfs_ctx_mtab(state->ctx); + if (!mtab) { + eval_report_error(state); + return false; + } + + const char *type = bfs_fstype(mtab, statbuf); return strcmp(type, expr->sdata) == 0; } @@ -572,8 +589,8 @@ bool eval_perm(const struct expr *expr, struct eval_state *state) { bool eval_fls(const struct expr *expr, struct eval_state *state) { CFILE *cfile = expr->cfile; FILE *file = cfile->file; - const struct bfs_users *users = state->cmdline->users; - const struct bfs_groups *groups = state->cmdline->groups; + const struct bfs_users *users = bfs_ctx_users(state->ctx); + const struct bfs_groups *groups = bfs_ctx_groups(state->ctx); const struct BFTW *ftwbuf = state->ftwbuf; const struct bfs_stat *statbuf = eval_stat(state); if (!statbuf) { @@ -930,7 +947,7 @@ static void add_elapsed(struct expr *expr, const struct timespec *start, const s */ static bool eval_expr(struct expr *expr, struct eval_state *state) { struct timespec start, end; - bool time = state->cmdline->debug & DEBUG_RATES; + bool time = state->ctx->debug & DEBUG_RATES; if (time) { if (eval_gettime(&start) != 0) { time = false; @@ -1051,8 +1068,8 @@ static bool eval_file_unique(struct eval_state *state, struct trie *seen) { /** * Log a stat() call. */ -static void debug_stat(const struct cmdline *cmdline, const struct BFTW *ftwbuf, const struct bftw_stat *cache, enum bfs_stat_flags flags) { - bfs_debug_prefix(cmdline, DEBUG_STAT); +static void debug_stat(const struct bfs_ctx *ctx, const struct BFTW *ftwbuf, const struct bftw_stat *cache, enum bfs_stat_flags flags) { + bfs_debug_prefix(ctx, DEBUG_STAT); fprintf(stderr, "bfs_stat("); if (ftwbuf->at_fd == AT_FDCWD) { @@ -1082,19 +1099,19 @@ static void debug_stat(const struct cmdline *cmdline, const struct BFTW *ftwbuf, /** * Log any stat() calls that happened. */ -static void debug_stats(const struct cmdline *cmdline, const struct BFTW *ftwbuf) { - if (!(cmdline->debug & DEBUG_STAT)) { +static void debug_stats(const struct bfs_ctx *ctx, const struct BFTW *ftwbuf) { + if (!(ctx->debug & DEBUG_STAT)) { return; } const struct bfs_stat *statbuf = ftwbuf->stat_cache.buf; if (statbuf || ftwbuf->stat_cache.error) { - debug_stat(cmdline, ftwbuf, &ftwbuf->stat_cache, BFS_STAT_FOLLOW); + debug_stat(ctx, ftwbuf, &ftwbuf->stat_cache, BFS_STAT_FOLLOW); } const struct bfs_stat *lstatbuf = ftwbuf->lstat_cache.buf; if ((lstatbuf && lstatbuf != statbuf) || ftwbuf->lstat_cache.error) { - debug_stat(cmdline, ftwbuf, &ftwbuf->lstat_cache, BFS_STAT_NOFOLLOW); + debug_stat(ctx, ftwbuf, &ftwbuf->lstat_cache, BFS_STAT_NOFOLLOW); } } @@ -1152,25 +1169,25 @@ static const char *dump_bftw_action(enum bftw_action action) { * Type passed as the argument to the bftw() callback. */ struct callback_args { - /** The parsed command line. */ - const struct cmdline *cmdline; + /** The bfs context. */ + const struct bfs_ctx *ctx; /** The set of seen files. */ struct trie *seen; - /** Eventual return value from eval_cmdline(). */ + /** Eventual return value from bfs_eval(). */ int ret; }; /** * bftw() callback. */ -static enum bftw_action cmdline_callback(const struct BFTW *ftwbuf, void *ptr) { +static enum bftw_action eval_callback(const struct BFTW *ftwbuf, void *ptr) { struct callback_args *args = ptr; - const struct cmdline *cmdline = args->cmdline; + const struct bfs_ctx *ctx = args->ctx; struct eval_state state; state.ftwbuf = ftwbuf; - state.cmdline = cmdline; + state.ctx = ctx; state.action = BFTW_CONTINUE; state.ret = &args->ret; state.quit = false; @@ -1184,46 +1201,46 @@ static enum bftw_action cmdline_callback(const struct BFTW *ftwbuf, void *ptr) { goto done; } - if (cmdline->unique && ftwbuf->visit == BFTW_PRE) { + if (ctx->unique && ftwbuf->visit == BFTW_PRE) { if (!eval_file_unique(&state, args->seen)) { goto done; } } - if (eval_expr(cmdline->exclude, &state)) { + if (eval_expr(ctx->exclude, &state)) { state.action = BFTW_PRUNE; goto done; } - if (cmdline->xargs_safe && strpbrk(ftwbuf->path, " \t\n\'\"\\")) { + if (ctx->xargs_safe && strpbrk(ftwbuf->path, " \t\n\'\"\\")) { args->ret = EXIT_FAILURE; eval_error(&state, "Path is not safe for xargs.\n"); state.action = BFTW_PRUNE; goto done; } - if (cmdline->maxdepth < 0 || ftwbuf->depth >= cmdline->maxdepth) { + if (ctx->maxdepth < 0 || ftwbuf->depth >= ctx->maxdepth) { state.action = BFTW_PRUNE; } // In -depth mode, only handle directories on the BFTW_POST visit enum bftw_visit expected_visit = BFTW_PRE; - if ((cmdline->flags & BFTW_POST_ORDER) - && (cmdline->strategy == BFTW_IDS || ftwbuf->type == BFTW_DIR) - && ftwbuf->depth < cmdline->maxdepth) { + if ((ctx->flags & BFTW_POST_ORDER) + && (ctx->strategy == BFTW_IDS || ftwbuf->type == BFTW_DIR) + && ftwbuf->depth < ctx->maxdepth) { expected_visit = BFTW_POST; } if (ftwbuf->visit == expected_visit - && ftwbuf->depth >= cmdline->mindepth - && ftwbuf->depth <= cmdline->maxdepth) { - eval_expr(cmdline->expr, &state); + && ftwbuf->depth >= ctx->mindepth + && ftwbuf->depth <= ctx->maxdepth) { + eval_expr(ctx->expr, &state); } done: - debug_stats(cmdline, ftwbuf); + debug_stats(ctx, ftwbuf); - if (bfs_debug(cmdline, DEBUG_SEARCH, "cmdline_callback({\n")) { + if (bfs_debug(ctx, DEBUG_SEARCH, "eval_callback({\n")) { fprintf(stderr, "\t.path = \"%s\",\n", ftwbuf->path); fprintf(stderr, "\t.root = \"%s\",\n", ftwbuf->root); fprintf(stderr, "\t.depth = %zu,\n", ftwbuf->depth); @@ -1239,7 +1256,7 @@ done: /** * Infer the number of open file descriptors we're allowed to have. */ -static int infer_fdlimit(const struct cmdline *cmdline) { +static int infer_fdlimit(const struct bfs_ctx *ctx) { int ret = 4096; struct rlimit rl; @@ -1250,7 +1267,7 @@ static int infer_fdlimit(const struct cmdline *cmdline) { } // 3 for std{in,out,err} - int nopen = 3 + cmdline->nopen_files; + int nopen = 3 + ctx->nfiles; // Check /proc/self/fd for the current number of open fds, if possible // (we may have inherited more than just the standard ones) @@ -1271,8 +1288,8 @@ static int infer_fdlimit(const struct cmdline *cmdline) { } ret -= nopen; - ret -= cmdline->expr->persistent_fds; - ret -= cmdline->expr->ephemeral_fds; + ret -= ctx->expr->persistent_fds; + ret -= ctx->expr->ephemeral_fds; // bftw() needs at least 2 available fds if (ret < 2) { @@ -1313,44 +1330,41 @@ static const char *dump_bftw_strategy(enum bftw_strategy strategy) { return strategies[strategy]; } -/** - * Evaluate the command line. - */ -int eval_cmdline(const struct cmdline *cmdline) { - if (!cmdline->expr) { +int bfs_eval(const struct bfs_ctx *ctx) { + if (!ctx->expr) { return EXIT_SUCCESS; } struct callback_args args = { - .cmdline = cmdline, + .ctx = ctx, .ret = EXIT_SUCCESS, }; struct trie seen; - if (cmdline->unique) { + if (ctx->unique) { trie_init(&seen); args.seen = &seen; } struct bftw_args bftw_args = { - .paths = cmdline->paths, - .npaths = darray_length(cmdline->paths), - .callback = cmdline_callback, + .paths = ctx->paths, + .npaths = darray_length(ctx->paths), + .callback = eval_callback, .ptr = &args, - .nopenfd = infer_fdlimit(cmdline), - .flags = cmdline->flags, - .strategy = cmdline->strategy, - .mtab = cmdline->mtab, + .nopenfd = infer_fdlimit(ctx), + .flags = ctx->flags, + .strategy = ctx->strategy, + .mtab = bfs_ctx_mtab(ctx), }; - if (bfs_debug(cmdline, DEBUG_SEARCH, "bftw({\n")) { + if (bfs_debug(ctx, DEBUG_SEARCH, "bftw({\n")) { fprintf(stderr, "\t.paths = {\n"); for (size_t i = 0; i < bftw_args.npaths; ++i) { fprintf(stderr, "\t\t\"%s\",\n", bftw_args.paths[i]); } fprintf(stderr, "\t},\n"); fprintf(stderr, "\t.npaths = %zu,\n", bftw_args.npaths); - fprintf(stderr, "\t.callback = cmdline_callback,\n"); + fprintf(stderr, "\t.callback = eval_callback,\n"); fprintf(stderr, "\t.ptr = &args,\n"); fprintf(stderr, "\t.nopenfd = %d,\n", bftw_args.nopenfd); fprintf(stderr, "\t.flags = "); @@ -1358,7 +1372,7 @@ int eval_cmdline(const struct cmdline *cmdline) { fprintf(stderr, ",\n\t.strategy = %s,\n", dump_bftw_strategy(bftw_args.strategy)); fprintf(stderr, "\t.mtab = "); if (bftw_args.mtab) { - fprintf(stderr, "cmdline->mtab"); + fprintf(stderr, "ctx->mtab"); } else { fprintf(stderr, "NULL"); } @@ -1370,13 +1384,13 @@ int eval_cmdline(const struct cmdline *cmdline) { perror("bftw()"); } - if (eval_exec_finish(cmdline->expr, cmdline) != 0) { + if (eval_exec_finish(ctx->expr, ctx) != 0) { args.ret = EXIT_FAILURE; } - dump_cmdline(cmdline, DEBUG_RATES); + bfs_ctx_dump(ctx, DEBUG_RATES); - if (cmdline->unique) { + if (ctx->unique) { trie_destroy(&seen); } diff --git a/eval.h b/eval.h index a95c3ac..9a1885b 100644 --- a/eval.h +++ b/eval.h @@ -22,9 +22,21 @@ #ifndef BFS_EVAL_H #define BFS_EVAL_H +#include "ctx.h" #include "expr.h" +/** + * Evaluate the command line. + * + * @param ctx + * The bfs context to evaluate. + * @return + * EXIT_SUCCESS on success, otherwise on failure. + */ +int bfs_eval(const struct bfs_ctx *ctx); + // Predicate evaluation functions + bool eval_true(const struct expr *expr, struct eval_state *state); bool eval_false(const struct expr *expr, struct eval_state *state); diff --git a/exec.c b/exec.c index e6df2dc..723f1dc 100644 --- a/exec.c +++ b/exec.c @@ -16,7 +16,7 @@ #include "exec.h" #include "bftw.h" -#include "cmdline.h" +#include "ctx.h" #include "color.h" #include "diag.h" #include "dstring.h" @@ -37,9 +37,9 @@ /** Print some debugging info. */ BFS_FORMATTER(2, 3) static void bfs_exec_debug(const struct bfs_exec *execbuf, const char *format, ...) { - const struct cmdline *cmdline = execbuf->cmdline; + const struct bfs_ctx *ctx = execbuf->ctx; - if (!bfs_debug(cmdline, DEBUG_EXEC, "${blu}")) { + if (!bfs_debug(ctx, DEBUG_EXEC, "${blu}")) { return; } @@ -51,7 +51,7 @@ static void bfs_exec_debug(const struct bfs_exec *execbuf, const char *format, . if (execbuf->flags & BFS_EXEC_CHDIR) { fputs("dir", stderr); } - cfprintf(cmdline->cerr, "${rs}: "); + cfprintf(ctx->cerr, "${rs}: "); va_list args; va_start(args, format); @@ -117,7 +117,7 @@ static size_t bfs_exec_arg_max(const struct bfs_exec *execbuf) { return arg_max; } -struct bfs_exec *parse_bfs_exec(char **argv, enum bfs_exec_flags flags, const struct cmdline *cmdline) { +struct bfs_exec *parse_bfs_exec(char **argv, enum bfs_exec_flags flags, const struct bfs_ctx *ctx) { struct bfs_exec *execbuf = malloc(sizeof(*execbuf)); if (!execbuf) { perror("malloc()"); @@ -125,7 +125,7 @@ struct bfs_exec *parse_bfs_exec(char **argv, enum bfs_exec_flags flags, const st } execbuf->flags = flags; - execbuf->cmdline = cmdline; + execbuf->ctx = ctx; execbuf->argv = NULL; execbuf->argc = 0; execbuf->argv_cap = 0; @@ -141,9 +141,9 @@ struct bfs_exec *parse_bfs_exec(char **argv, enum bfs_exec_flags flags, const st const char *arg = argv[i]; if (!arg) { if (execbuf->flags & BFS_EXEC_CONFIRM) { - bfs_error(cmdline, "%s: Expected '... ;'.\n", argv[0]); + bfs_error(ctx, "%s: Expected '... ;'.\n", argv[0]); } else { - bfs_error(cmdline, "%s: Expected '... ;' or '... {} +'.\n", argv[0]); + bfs_error(ctx, "%s: Expected '... ;' or '... {} +'.\n", argv[0]); } goto fail; } else if (strcmp(arg, ";") == 0) { @@ -160,7 +160,7 @@ struct bfs_exec *parse_bfs_exec(char **argv, enum bfs_exec_flags flags, const st execbuf->tmpl_argc = i - 1; if (execbuf->tmpl_argc == 0) { - bfs_error(cmdline, "%s: Missing command.\n", argv[0]); + bfs_error(ctx, "%s: Missing command.\n", argv[0]); goto fail; } @@ -175,7 +175,7 @@ struct bfs_exec *parse_bfs_exec(char **argv, enum bfs_exec_flags flags, const st for (i = 0; i < execbuf->tmpl_argc - 1; ++i) { char *arg = execbuf->tmpl_argv[i]; if (strstr(arg, "{}")) { - bfs_error(cmdline, "%s ... +: Only one '{}' is supported.\n", argv[0]); + bfs_error(ctx, "%s ... +: Only one '{}' is supported.\n", argv[0]); goto fail; } execbuf->argv[i] = arg; @@ -394,9 +394,9 @@ fail: if (!str) { str = "unknown"; } - bfs_warning(execbuf->cmdline, "Command '${ex}%s${rs}' terminated by signal %d (%s)\n", execbuf->argv[0], sig, str); + bfs_warning(execbuf->ctx, "Command '${ex}%s${rs}' terminated by signal %d (%s)\n", execbuf->argv[0], sig, str); } else { - bfs_warning(execbuf->cmdline, "Command '${ex}%s${rs}' terminated abnormally\n", execbuf->argv[0]); + bfs_warning(execbuf->ctx, "Command '${ex}%s${rs}' terminated abnormally\n", execbuf->argv[0]); } errno = 0; diff --git a/exec.h b/exec.h index ab3f59a..2ff0c73 100644 --- a/exec.h +++ b/exec.h @@ -1,6 +1,6 @@ /**************************************************************************** * bfs * - * Copyright (C) 2017 Tavian Barnes * + * Copyright (C) 2017-2020 Tavian Barnes * * * * Permission to use, copy, modify, and/or distribute this software for any * * purpose with or without fee is hereby granted. * @@ -24,7 +24,7 @@ #include "bftw.h" #include "color.h" -struct cmdline; +struct ctx; /** * Flags for the -exec actions. @@ -45,8 +45,8 @@ struct bfs_exec { /** Flags for this exec buffer. */ enum bfs_exec_flags flags; - /** The overall command line. */ - const struct cmdline *cmdline; + /** The bfs context. */ + const struct bfs_ctx *ctx; /** Command line template. */ char **tmpl_argv; /** Command line template size. */ @@ -82,11 +82,12 @@ struct bfs_exec { * The (bfs) command line argument to parse. * @param flags * Any flags for this exec action. - * @param cmdline - * The command line. - * @return The parsed exec action, or NULL on failure. + * @param ctx + * The bfs context. + * @return + * The parsed exec action, or NULL on failure. */ -struct bfs_exec *parse_bfs_exec(char **argv, enum bfs_exec_flags flags, const struct cmdline *cmdline); +struct bfs_exec *parse_bfs_exec(char **argv, enum bfs_exec_flags flags, const struct bfs_ctx *ctx); /** * Execute the command for a file. diff --git a/main.c b/main.c index b8063d8..70f4d8b 100644 --- a/main.c +++ b/main.c @@ -18,18 +18,18 @@ * - main(): the entry point for bfs(1), a breadth-first version of find(1) * - main.c (this file) * - * - parse_cmdline(): parses the command line into an expression tree - * - cmdline.h (declares the parsed command line structure) + * - bfs_parse_cmdline(): parses the command line into an expression tree + * - ctx.[ch] (struct bfs_ctx, the overall bfs context) * - expr.h (declares the expression tree nodes) - * - parse.c (the parser itself) - * - opt.c (the expression optimizer) + * - parse.[ch] (the parser itself) + * - opt.[ch] (the optimizer) * - * - eval_cmdline(): runs the expression on every file it sees + * - bfs_eval(): runs the expression on every file it sees * - eval.[ch] (the main evaluation functions) * - exec.[ch] (implements -exec[dir]/-ok[dir]) * - printf.[ch] (implements -[f]printf) * - * - bftw(): used by eval_cmdline() to walk the directory tree(s) + * - bftw(): used by bfs_eval() to walk the directory tree(s) * - bftw.[ch] (an extended version of nftw(3)) * * - Utilities: @@ -49,7 +49,9 @@ * - util.[ch] (everything else) */ -#include "cmdline.h" +#include "ctx.h" +#include "eval.h" +#include "parse.h" #include "util.h" #include #include @@ -100,12 +102,12 @@ int main(int argc, char *argv[]) { // Use the system locale instead of "C" setlocale(LC_ALL, ""); - struct cmdline *cmdline = parse_cmdline(argc, argv); - if (cmdline) { - ret = eval_cmdline(cmdline); + struct bfs_ctx *ctx = bfs_parse_cmdline(argc, argv); + if (ctx) { + ret = bfs_eval(ctx); } - if (free_cmdline(cmdline) != 0 && ret == EXIT_SUCCESS) { + if (bfs_ctx_free(ctx) != 0 && ret == EXIT_SUCCESS) { ret = EXIT_FAILURE; } diff --git a/mtab.c b/mtab.c index 70152cd..50b01be 100644 --- a/mtab.c +++ b/mtab.c @@ -1,6 +1,6 @@ /**************************************************************************** * bfs * - * Copyright (C) 2017-2019 Tavian Barnes * + * Copyright (C) 2017-2020 Tavian Barnes * * * * Permission to use, copy, modify, and/or distribute this software for any * * purpose with or without fee is hereby granted. * @@ -102,7 +102,7 @@ fail: return -1; } -struct bfs_mtab *parse_bfs_mtab() { +struct bfs_mtab *bfs_parse_mtab() { struct bfs_mtab *mtab = malloc(sizeof(*mtab)); if (!mtab) { return NULL; @@ -189,7 +189,7 @@ struct bfs_mtab *parse_bfs_mtab() { return mtab; fail: - free_bfs_mtab(mtab); + bfs_free_mtab(mtab); errno = error; return NULL; } @@ -230,7 +230,7 @@ bool bfs_might_be_mount(const struct bfs_mtab *mtab, const char *path) { return trie_find_str(&mtab->names, name); } -void free_bfs_mtab(struct bfs_mtab *mtab) { +void bfs_free_mtab(struct bfs_mtab *mtab) { if (mtab) { trie_destroy(&mtab->types); trie_destroy(&mtab->names); diff --git a/mtab.h b/mtab.h index 57fe11d..b2f057d 100644 --- a/mtab.h +++ b/mtab.h @@ -1,6 +1,6 @@ /**************************************************************************** * bfs * - * Copyright (C) 2017-2019 Tavian Barnes * + * Copyright (C) 2017-2020 Tavian Barnes * * * * Permission to use, copy, modify, and/or distribute this software for any * * purpose with or without fee is hereby granted. * @@ -35,7 +35,7 @@ struct bfs_mtab; * @return * The parsed mount table, or NULL on error. */ -struct bfs_mtab *parse_bfs_mtab(void); +struct bfs_mtab *bfs_parse_mtab(void); /** * Determine the file system type that a file is on. @@ -65,6 +65,6 @@ bool bfs_might_be_mount(const struct bfs_mtab *mtab, const char *path); /** * Free a mount table. */ -void free_bfs_mtab(struct bfs_mtab *mtab); +void bfs_free_mtab(struct bfs_mtab *mtab); #endif // BFS_MTAB_H diff --git a/opt.c b/opt.c index 458106f..d342406 100644 --- a/opt.c +++ b/opt.c @@ -39,8 +39,9 @@ * effects are reachable at all, and skipping the traversal if not. */ -#include "cmdline.h" +#include "opt.h" #include "color.h" +#include "ctx.h" #include "diag.h" #include "eval.h" #include "expr.h" @@ -300,8 +301,8 @@ static void set_facts_impossible(struct opt_facts *facts) { * Optimizer state. */ struct opt_state { - /** The command line we're optimizing. */ - const struct cmdline *cmdline; + /** The context we're optimizing. */ + const struct bfs_ctx *ctx; /** Data flow facts before this expression is evaluated. */ struct opt_facts facts; @@ -316,10 +317,10 @@ struct opt_state { /** Log an optimization. */ BFS_FORMATTER(3, 4) static bool debug_opt(const struct opt_state *state, int level, const char *format, ...) { - if (bfs_debug(state->cmdline, DEBUG_OPT, "${cyn}-O%d${rs}: ", level)) { + if (bfs_debug(state->ctx, DEBUG_OPT, "${cyn}-O%d${rs}: ", level)) { va_list args; va_start(args, format); - cvfprintf(state->cmdline->cerr, format, args); + cvfprintf(state->ctx->cerr, format, args); va_end(args); return true; } else { @@ -391,7 +392,7 @@ static struct expr *de_morgan(const struct opt_state *state, struct expr *expr, } if (debug) { - cfprintf(state->cmdline->cerr, "<==> %pe\n", parent); + cfprintf(state->ctx->cerr, "<==> %pe\n", parent); } if (expr->lhs->eval == eval_not) { @@ -437,7 +438,7 @@ static struct expr *optimize_not_expr(const struct opt_state *state, struct expr struct expr *rhs = expr->rhs; - int optlevel = state->cmdline->optlevel; + int optlevel = state->ctx->optlevel; if (optlevel >= 1) { if (rhs == &expr_true) { debug_opt(state, 1, "constant propagation: %pe <==> %pe\n", expr, &expr_false); @@ -493,8 +494,8 @@ static struct expr *optimize_and_expr(const struct opt_state *state, struct expr struct expr *lhs = expr->lhs; struct expr *rhs = expr->rhs; - const struct cmdline *cmdline = state->cmdline; - int optlevel = cmdline->optlevel; + const struct bfs_ctx *ctx = state->ctx; + int optlevel = ctx->optlevel; if (optlevel >= 1) { if (lhs == &expr_true) { debug_opt(state, 1, "conjunction elimination: %pe <==> %pe\n", expr, rhs); @@ -510,7 +511,7 @@ static struct expr *optimize_and_expr(const struct opt_state *state, struct expr struct expr *ret = extract_child_expr(expr, &expr->lhs); ret = negate_expr(ret, &fake_not_arg); if (debug && ret) { - cfprintf(cmdline->cerr, "%pe\n", ret); + cfprintf(ctx->cerr, "%pe\n", ret); } return ret; } else if (optlevel >= 2 && lhs->pure && rhs == &expr_false) { @@ -562,8 +563,8 @@ static struct expr *optimize_or_expr(const struct opt_state *state, struct expr struct expr *lhs = expr->lhs; struct expr *rhs = expr->rhs; - const struct cmdline *cmdline = state->cmdline; - int optlevel = cmdline->optlevel; + const struct bfs_ctx *ctx = state->ctx; + int optlevel = ctx->optlevel; if (optlevel >= 1) { if (lhs->always_true) { debug_opt(state, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); @@ -579,7 +580,7 @@ static struct expr *optimize_or_expr(const struct opt_state *state, struct expr struct expr *ret = extract_child_expr(expr, &expr->lhs); ret = negate_expr(ret, &fake_not_arg); if (debug && ret) { - cfprintf(cmdline->cerr, "%pe\n", ret); + cfprintf(ctx->cerr, "%pe\n", ret); } return ret; } else if (optlevel >= 2 && lhs->pure && rhs == &expr_true) { @@ -626,7 +627,7 @@ fail: /** Optimize an expression in an ignored-result context. */ static struct expr *ignore_result(const struct opt_state *state, struct expr *expr) { - int optlevel = state->cmdline->optlevel; + int optlevel = state->ctx->optlevel; if (optlevel >= 1) { while (true) { @@ -660,7 +661,7 @@ static struct expr *optimize_comma_expr(const struct opt_state *state, struct ex struct expr *lhs = expr->lhs; struct expr *rhs = expr->rhs; - int optlevel = state->cmdline->optlevel; + int optlevel = state->ctx->optlevel; if (optlevel >= 1) { lhs = expr->lhs = ignore_result(state, lhs); @@ -758,7 +759,7 @@ static void infer_icmp_facts(struct opt_state *state, const struct expr *expr, e static void infer_gid_facts(struct opt_state *state, const struct expr *expr) { infer_icmp_facts(state, expr, GID_RANGE); - struct bfs_groups *groups = state->cmdline->groups; + const struct bfs_groups *groups = bfs_ctx_groups(state->ctx); struct range *range = &state->facts_when_true.ranges[GID_RANGE]; if (groups && range->min == range->max) { gid_t gid = range->min; @@ -771,7 +772,7 @@ static void infer_gid_facts(struct opt_state *state, const struct expr *expr) { static void infer_uid_facts(struct opt_state *state, const struct expr *expr) { infer_icmp_facts(state, expr, UID_RANGE); - struct bfs_users *users = state->cmdline->users; + const struct bfs_users *users = bfs_ctx_users(state->ctx); struct range *range = &state->facts_when_true.ranges[UID_RANGE]; if (users && range->min == range->max) { uid_t uid = range->min; @@ -884,7 +885,7 @@ static struct expr *optimize_expr_recursive(struct opt_state *state, struct expr set_facts_impossible(&state->facts_when_true); } - if (state->cmdline->optlevel < 2 || expr == &expr_true || expr == &expr_false) { + if (state->ctx->optlevel < 2 || expr == &expr_true || expr == &expr_false) { goto done; } @@ -920,7 +921,7 @@ static bool reorder_expr(const struct opt_state *state, struct expr *expr, doubl expr->lhs = expr->rhs; expr->rhs = lhs; if (debug) { - cfprintf(state->cmdline->cerr, "%pe (~${ylw}%g${rs} --> ~${ylw}%g${rs})\n", expr, expr->cost, swapped_cost); + cfprintf(state->ctx->cerr, "%pe (~${ylw}%g${rs} --> ~${ylw}%g${rs})\n", expr, expr->cost, swapped_cost); } expr->cost = swapped_cost; return true; @@ -971,7 +972,7 @@ static struct expr *optimize_expr(struct opt_state *state, struct expr *expr) { return NULL; } - if (state->cmdline->optlevel >= 3 && reorder_expr_recursive(state, expr)) { + if (state->ctx->optlevel >= 3 && reorder_expr_recursive(state, expr)) { // Re-do optimizations to account for the new ordering *state->facts_when_impure = saved_impure; expr = optimize_expr_recursive(state, expr); @@ -983,20 +984,20 @@ static struct expr *optimize_expr(struct opt_state *state, struct expr *expr) { return expr; } -int optimize_cmdline(struct cmdline *cmdline) { - dump_cmdline(cmdline, DEBUG_OPT); +int bfs_optimize(struct bfs_ctx *ctx) { + bfs_ctx_dump(ctx, DEBUG_OPT); struct opt_facts facts_when_impure; set_facts_impossible(&facts_when_impure); struct opt_state state = { - .cmdline = cmdline, + .ctx = ctx, .facts_when_impure = &facts_when_impure, }; facts_init(&state.facts); - cmdline->exclude = optimize_expr(&state, cmdline->exclude); - if (!cmdline->exclude) { + ctx->exclude = optimize_expr(&state, ctx->exclude); + if (!ctx->exclude) { return -1; } @@ -1004,40 +1005,40 @@ int optimize_cmdline(struct cmdline *cmdline) { state.facts = state.facts_when_false; struct range *depth = &state.facts.ranges[DEPTH_RANGE]; - constrain_min(depth, cmdline->mindepth); - constrain_max(depth, cmdline->maxdepth); + constrain_min(depth, ctx->mindepth); + constrain_max(depth, ctx->maxdepth); - cmdline->expr = optimize_expr(&state, cmdline->expr); - if (!cmdline->expr) { + ctx->expr = optimize_expr(&state, ctx->expr); + if (!ctx->expr) { return -1; } - cmdline->expr = ignore_result(&state, cmdline->expr); + ctx->expr = ignore_result(&state, ctx->expr); if (facts_are_impossible(&facts_when_impure)) { - bfs_warning(cmdline, "This command won't do anything.\n"); + bfs_warning(ctx, "This command won't do anything.\n"); } const struct range *depth_when_impure = &facts_when_impure.ranges[DEPTH_RANGE]; long long mindepth = depth_when_impure->min; long long maxdepth = depth_when_impure->max; - int optlevel = cmdline->optlevel; + int optlevel = ctx->optlevel; - if (optlevel >= 2 && mindepth > cmdline->mindepth) { + if (optlevel >= 2 && mindepth > ctx->mindepth) { if (mindepth > INT_MAX) { mindepth = INT_MAX; } - cmdline->mindepth = mindepth; - debug_opt(&state, 2, "data flow: mindepth --> %d\n", cmdline->mindepth); + ctx->mindepth = mindepth; + debug_opt(&state, 2, "data flow: mindepth --> %d\n", ctx->mindepth); } - if (optlevel >= 4 && maxdepth < cmdline->maxdepth) { + if (optlevel >= 4 && maxdepth < ctx->maxdepth) { if (maxdepth < INT_MIN) { maxdepth = INT_MIN; } - cmdline->maxdepth = maxdepth; - debug_opt(&state, 4, "data flow: maxdepth --> %d\n", cmdline->maxdepth); + ctx->maxdepth = maxdepth; + debug_opt(&state, 4, "data flow: maxdepth --> %d\n", ctx->maxdepth); } return 0; diff --git a/opt.h b/opt.h new file mode 100644 index 0000000..a30cd17 --- /dev/null +++ b/opt.h @@ -0,0 +1,37 @@ +/**************************************************************************** + * bfs * + * Copyright (C) 2020 Tavian Barnes * + * * + * Permission to use, copy, modify, and/or distribute this software for any * + * purpose with or without fee is hereby granted. * + * * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * + ****************************************************************************/ + +/** + * Optimization. + */ + +#ifndef BFS_OPT_H +#define BFS_OPT_H + +#include "ctx.h" + +/** + * Apply optimizations to the command line. + * + * @param ctx + * The bfs context to optimize. + * @return + * 0 if successful, -1 on error. + */ +int bfs_optimize(struct bfs_ctx *ctx); + +#endif // BFS_OPT_H + diff --git a/parse.c b/parse.c index a3af1d1..337ba00 100644 --- a/parse.c +++ b/parse.c @@ -21,8 +21,9 @@ * flags like always-true options, and skipping over paths wherever they appear. */ +#include "parse.h" #include "bfs.h" -#include "cmdline.h" +#include "ctx.h" #include "darray.h" #include "diag.h" #include "dstring.h" @@ -31,6 +32,7 @@ #include "expr.h" #include "fsade.h" #include "mtab.h" +#include "opt.h" #include "printf.h" #include "pwcache.h" #include "spawn.h" @@ -208,69 +210,6 @@ static void expr_set_never_returns(struct expr *expr) { expr->always_true = expr->always_false = true; } -/** - * An open file for the command line. - */ -struct open_file { - /** The file itself. */ - CFILE *cfile; - /** The path to the file (for diagnostics). */ - const char *path; -}; - -/** - * Free the parsed command line. - */ -int free_cmdline(struct cmdline *cmdline) { - int ret = 0; - - if (cmdline) { - CFILE *cout = cmdline->cout; - CFILE *cerr = cmdline->cerr; - - free_expr(cmdline->expr); - free_expr(cmdline->exclude); - - free_bfs_mtab(cmdline->mtab); - - bfs_free_groups(cmdline->groups); - bfs_free_users(cmdline->users); - - struct trie_leaf *leaf; - while ((leaf = trie_first_leaf(&cmdline->open_files))) { - struct open_file *ofile = leaf->value; - - if (cfclose(ofile->cfile) != 0) { - if (cerr) { - bfs_error(cmdline, "'%s': %m.\n", ofile->path); - } - ret = -1; - } - - free(ofile); - trie_remove(&cmdline->open_files, leaf); - } - trie_destroy(&cmdline->open_files); - - if (cout && fflush(cout->file) != 0) { - if (cerr) { - bfs_error(cmdline, "standard output: %m.\n"); - } - ret = -1; - } - - cfclose(cout); - cfclose(cerr); - - free_colors(cmdline->colors); - darray_free(cmdline->paths); - free(cmdline->argv); - free(cmdline); - } - - return ret; -} - /** * Color use flags. */ @@ -285,7 +224,7 @@ enum use_color { */ struct parser_state { /** The command line being constructed. */ - struct cmdline *cmdline; + struct bfs_ctx *ctx; /** The command line arguments being parsed. */ char **argv; /** The name of this program. */ @@ -351,7 +290,7 @@ BFS_FORMATTER(2, 3) static void parse_error(const struct parser_state *state, const char *format, ...) { va_list args; va_start(args, format); - bfs_verror(state->cmdline, format, args); + bfs_verror(state->ctx, format, args); va_end(args); } @@ -362,7 +301,7 @@ BFS_FORMATTER(2, 3) static bool parse_warning(const struct parser_state *state, const char *format, ...) { va_list args; va_start(args, format); - bool ret = bfs_vwarning(state->cmdline, format, args); + bool ret = bfs_vwarning(state->ctx, format, args); va_end(args); return ret; } @@ -373,75 +312,31 @@ static bool parse_warning(const struct parser_state *state, const char *format, static void init_print_expr(struct parser_state *state, struct expr *expr) { expr_set_always_true(expr); expr->cost = PRINT_COST; - expr->cfile = state->cmdline->cout; + expr->cfile = state->ctx->cout; } /** * Open a file for an expression. */ static int expr_open(struct parser_state *state, struct expr *expr, const char *path) { - int ret = -1; + struct bfs_ctx *ctx = state->ctx; - struct cmdline *cmdline = state->cmdline; - - CFILE *cfile = cfopen(path, state->use_color ? cmdline->colors : NULL); - if (!cfile) { - parse_error(state, "${blu}%s${rs} ${bld}%s${rs}: %m.\n", expr->argv[0], path); - goto out; - } - - struct bfs_stat sb; - if (bfs_stat(fileno(cfile->file), NULL, 0, &sb) != 0) { + expr->cfile = bfs_ctx_open(ctx, path, state->use_color); + if (!expr->cfile) { parse_error(state, "${blu}%s${rs} ${bld}%s${rs}: %m.\n", expr->argv[0], path); - goto out_close; - } - - bfs_file_id id; - bfs_stat_id(&sb, &id); - - struct trie_leaf *leaf = trie_insert_mem(&cmdline->open_files, id, sizeof(id)); - if (!leaf) { - perror("trie_insert_mem()"); - goto out_close; - } - - if (leaf->value) { - struct open_file *ofile = leaf->value; - expr->cfile = ofile->cfile; - ret = 0; - goto out_close; - } - - struct open_file *ofile = malloc(sizeof(*ofile)); - if (!ofile) { - perror("malloc()"); - trie_remove(&cmdline->open_files, leaf); - goto out_close; + return -1; } - ofile->cfile = cfile; - ofile->path = path; - leaf->value = ofile; - ++cmdline->nopen_files; - - expr->cfile = cfile; - - ret = 0; - goto out; - -out_close: - cfclose(cfile); -out: - return ret; + return 0; } /** * Invoke bfs_stat() on an argument. */ static int stat_arg(const struct parser_state *state, struct expr *expr, struct bfs_stat *sb) { - const struct cmdline *cmdline = state->cmdline; + const struct bfs_ctx *ctx = state->ctx; - bool follow = cmdline->flags & (BFTW_FOLLOW_ROOTS | BFTW_FOLLOW_ALL); + bool follow = ctx->flags & (BFTW_FOLLOW_ROOTS | BFTW_FOLLOW_ALL); enum bfs_stat_flags flags = follow ? BFS_STAT_TRYFOLLOW : BFS_STAT_NOFOLLOW; int ret = bfs_stat(AT_FDCWD, expr->sdata, flags, sb); @@ -481,12 +376,12 @@ static char **parser_advance(struct parser_state *state, enum token_type type, s * Parse a root path. */ static int parse_root(struct parser_state *state, const char *path) { - struct cmdline *cmdline = state->cmdline; - return DARRAY_PUSH(&cmdline->paths, &path); + struct bfs_ctx *ctx = state->ctx; + return DARRAY_PUSH(&ctx->paths, &path); } /** - * While parsing an expression, skip any paths and add them to the cmdline. + * While parsing an expression, skip any paths and add them to ctx->paths. */ static int skip_paths(struct parser_state *state) { while (true) { @@ -863,13 +758,13 @@ static bool parse_debug_flag(const char *flag, size_t len, const char *expected) * Parse -D FLAG. */ static struct expr *parse_debug(struct parser_state *state, int arg1, int arg2) { - struct cmdline *cmdline = state->cmdline; + struct bfs_ctx *ctx = state->ctx; const char *arg = state->argv[0]; const char *flags = state->argv[1]; if (!flags) { parse_error(state, "${cyn}%s${rs} needs a flag.\n\n", arg); - debug_help(cmdline->cerr); + debug_help(ctx->cerr); return NULL; } @@ -884,7 +779,7 @@ static struct expr *parse_debug(struct parser_state *state, int arg1, int arg2) } if (parse_debug_flag(flag, len, "help")) { - debug_help(cmdline->cout); + debug_help(ctx->cout); state->just_info = true; return NULL; } @@ -894,22 +789,22 @@ static struct expr *parse_debug(struct parser_state *state, int arg1, int arg2) if (!expected) { if (parse_warning(state, "Unrecognized debug flag ${bld}")) { fwrite(flag, 1, len, stderr); - cfprintf(cmdline->cerr, "${rs}.\n\n"); + cfprintf(ctx->cerr, "${rs}.\n\n"); unrecognized = true; } break; } if (parse_debug_flag(flag, len, expected)) { - cmdline->debug |= debug_flags[i].flag; + ctx->debug |= debug_flags[i].flag; break; } } } if (unrecognized) { - debug_help(cmdline->cerr); - cfprintf(cmdline->cerr, "\n"); + debug_help(ctx->cerr); + cfprintf(ctx->cerr, "\n"); } return parse_unary_flag(state); @@ -919,7 +814,7 @@ static struct expr *parse_debug(struct parser_state *state, int arg1, int arg2) * Parse -On. */ static struct expr *parse_optlevel(struct parser_state *state, int arg1, int arg2) { - int *optlevel = &state->cmdline->optlevel; + int *optlevel = &state->ctx->optlevel; if (strcmp(state->argv[0], "-Ofast") == 0) { *optlevel = 4; @@ -938,9 +833,9 @@ static struct expr *parse_optlevel(struct parser_state *state, int arg1, int arg * Parse -[PHL], -(no)?follow. */ static struct expr *parse_follow(struct parser_state *state, int flags, int option) { - struct cmdline *cmdline = state->cmdline; - cmdline->flags &= ~(BFTW_FOLLOW_ROOTS | BFTW_FOLLOW_ALL); - cmdline->flags |= flags; + struct bfs_ctx *ctx = state->ctx; + ctx->flags &= ~(BFTW_FOLLOW_ROOTS | BFTW_FOLLOW_ALL); + ctx->flags |= flags; if (option) { return parse_nullary_positional_option(state); } else { @@ -952,7 +847,7 @@ static struct expr *parse_follow(struct parser_state *state, int flags, int opti * Parse -X. */ static struct expr *parse_xargs_safe(struct parser_state *state, int arg1, int arg2) { - state->cmdline->xargs_safe = true; + state->ctx->xargs_safe = true; return parse_nullary_flag(state); } @@ -1061,16 +956,16 @@ static struct expr *parse_capable(struct parser_state *state, int flag, int arg2 * Parse -(no)?color. */ static struct expr *parse_color(struct parser_state *state, int color, int arg2) { - struct cmdline *cmdline = state->cmdline; - struct colors *colors = cmdline->colors; + struct bfs_ctx *ctx = state->ctx; + struct colors *colors = ctx->colors; if (color) { state->use_color = COLOR_ALWAYS; - cmdline->cout->colors = colors; - cmdline->cerr->colors = colors; + ctx->cout->colors = colors; + ctx->cerr->colors = colors; } else { state->use_color = COLOR_NEVER; - cmdline->cout->colors = NULL; - cmdline->cerr->colors = NULL; + ctx->cout->colors = NULL; + ctx->cerr->colors = NULL; } return parse_nullary_option(state); } @@ -1116,7 +1011,7 @@ static struct expr *parse_daystart(struct parser_state *state, int arg1, int arg * Parse -delete. */ static struct expr *parse_delete(struct parser_state *state, int arg1, int arg2) { - state->cmdline->flags |= BFTW_POST_ORDER; + state->ctx->flags |= BFTW_POST_ORDER; state->depth_arg = state->argv[0]; return parse_nullary_action(state, eval_delete); } @@ -1125,7 +1020,7 @@ static struct expr *parse_delete(struct parser_state *state, int arg1, int arg2) * Parse -d. */ static struct expr *parse_depth(struct parser_state *state, int arg1, int arg2) { - state->cmdline->flags |= BFTW_POST_ORDER; + state->ctx->flags |= BFTW_POST_ORDER; state->depth_arg = state->argv[0]; return parse_nullary_flag(state); } @@ -1146,7 +1041,7 @@ static struct expr *parse_depth_n(struct parser_state *state, int arg1, int arg2 * Parse -{min,max}depth N. */ static struct expr *parse_depth_limit(struct parser_state *state, int is_min, int arg2) { - struct cmdline *cmdline = state->cmdline; + struct bfs_ctx *ctx = state->ctx; const char *arg = state->argv[0]; const char *value = state->argv[1]; if (!value) { @@ -1154,7 +1049,7 @@ static struct expr *parse_depth_limit(struct parser_state *state, int is_min, in return NULL; } - int *depth = is_min ? &cmdline->mindepth