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 : &cmdline->maxdepth; + int *depth = is_min ? &ctx->mindepth : &ctx->maxdepth; if (!parse_int(state, value, depth, IF_INT | IF_UNSIGNED)) { return NULL; } @@ -1174,7 +1069,7 @@ static struct expr *parse_empty(struct parser_state *state, int arg1, int arg2) expr->cost = 2000.0; expr->probability = 0.01; - if (state->cmdline->optlevel < 4) { + if (state->ctx->optlevel < 4) { // Since -empty attempts to open and read directories, it may // have side effects such as reporting permission errors, and // thus shouldn't be re-ordered without aggressive optimizations @@ -1190,7 +1085,7 @@ static struct expr *parse_empty(struct parser_state *state, int arg1, int arg2) * Parse -exec(dir)?/-ok(dir)?. */ static struct expr *parse_exec(struct parser_state *state, int flags, int arg2) { - struct bfs_exec *execbuf = parse_bfs_exec(state->argv, flags, state->cmdline); + struct bfs_exec *execbuf = parse_bfs_exec(state->argv, flags, state->ctx); if (!execbuf) { return NULL; } @@ -1266,14 +1161,23 @@ static struct expr *parse_f(struct parser_state *state, int arg1, int arg2) { */ static struct expr *parse_fls(struct parser_state *state, int arg1, int arg2) { struct expr *expr = parse_unary_action(state, eval_fls); - if (expr) { - expr_set_always_true(expr); - expr->cost = PRINT_COST; - if (expr_open(state, expr, expr->sdata) != 0) { - goto fail; - } - expr->reftime = state->now; + if (!expr) { + goto fail; } + + if (expr_open(state, expr, expr->sdata) != 0) { + goto fail; + } + + expr_set_always_true(expr); + expr->cost = PRINT_COST; + expr->reftime = state->now; + + // We'll need these for user/group names, so initialize them now to + // avoid EMFILE later + bfs_ctx_users(state->ctx); + bfs_ctx_groups(state->ctx); + return expr; fail: @@ -1350,7 +1254,7 @@ static struct expr *parse_fprintf(struct parser_state *state, int arg1, int arg2 goto fail; } - expr->printf = parse_bfs_printf(format, state->cmdline); + expr->printf = parse_bfs_printf(format, state->ctx); if (!expr->printf) { goto fail; } @@ -1366,9 +1270,8 @@ fail: * Parse -fstype TYPE. */ static struct expr *parse_fstype(struct parser_state *state, int arg1, int arg2) { - struct cmdline *cmdline = state->cmdline; - if (!cmdline->mtab) { - parse_error(state, "Couldn't parse the mount table: %s.\n", strerror(cmdline->mtab_error)); + if (!bfs_ctx_mtab(state->ctx)) { + parse_error(state, "Couldn't parse the mount table: %m.\n"); return NULL; } @@ -1383,9 +1286,9 @@ static struct expr *parse_fstype(struct parser_state *state, int arg1, int arg2) * Parse -gid/-group. */ static struct expr *parse_group(struct parser_state *state, int arg1, int arg2) { - struct cmdline *cmdline = state->cmdline; - if (!cmdline->groups) { - parse_error(state, "Couldn't parse the group table: %s.\n", strerror(cmdline->groups_error)); + const struct bfs_groups *groups = bfs_ctx_groups(state->ctx); + if (!groups) { + parse_error(state, "Couldn't parse the group table: %m.\n"); return NULL; } @@ -1396,7 +1299,7 @@ static struct expr *parse_group(struct parser_state *state, int arg1, int arg2) return NULL; } - const struct group *grp = bfs_getgrnam(cmdline->groups, expr->sdata); + const struct group *grp = bfs_getgrnam(groups, expr->sdata); if (grp) { expr->idata = grp->gr_gid; expr->cmp_flag = CMP_EXACT; @@ -1422,7 +1325,7 @@ fail: * Parse -unique. */ static struct expr *parse_unique(struct parser_state *state, int arg1, int arg2) { - state->cmdline->unique = true; + state->ctx->unique = true; return parse_nullary_option(state); } @@ -1441,9 +1344,9 @@ static struct expr *parse_used(struct parser_state *state, int arg1, int arg2) { * Parse -uid/-user. */ static struct expr *parse_user(struct parser_state *state, int arg1, int arg2) { - struct cmdline *cmdline = state->cmdline; - if (!cmdline->users) { - parse_error(state, "Couldn't parse the user table: %s.\n", strerror(cmdline->users_error)); + const struct bfs_users *users = bfs_ctx_users(state->ctx); + if (!users) { + parse_error(state, "Couldn't parse the user table: %m.\n"); return NULL; } @@ -1454,7 +1357,7 @@ static struct expr *parse_user(struct parser_state *state, int arg1, int arg2) { return NULL; } - const struct passwd *pwd = bfs_getpwnam(cmdline->users, expr->sdata); + const struct passwd *pwd = bfs_getpwnam(users, expr->sdata); if (pwd) { expr->idata = pwd->pw_uid; expr->cmp_flag = CMP_EXACT; @@ -1491,7 +1394,7 @@ static struct expr *parse_hidden(struct parser_state *state, int arg1, int arg2) * Parse -(no)?ignore_readdir_race. */ static struct expr *parse_ignore_races(struct parser_state *state, int ignore, int arg2) { - state->cmdline->ignore_races = ignore; + state->ctx->ignore_races = ignore; return parse_nullary_option(state); } @@ -1524,10 +1427,18 @@ static struct expr *parse_links(struct parser_state *state, int arg1, int arg2) */ static struct expr *parse_ls(struct parser_state *state, int arg1, int arg2) { struct expr *expr = parse_nullary_action(state, eval_fls); - if (expr) { - init_print_expr(state, expr); - expr->reftime = state->now; + if (!expr) { + return NULL; } + + init_print_expr(state, expr); + expr->reftime = state->now; + + // We'll need these for user/group names, so initialize them now to + // avoid EMFILE later + bfs_ctx_users(state->ctx); + bfs_ctx_groups(state->ctx); + return expr; } @@ -1540,7 +1451,7 @@ static struct expr *parse_mount(struct parser_state *state, int arg1, int arg2) "${blu}-xdev${rs}, due to http://austingroupbugs.net/view.php?id=1133.\n\n", state->argv[0]); - state->cmdline->flags |= BFTW_PRUNE_MOUNTS; + state->ctx->flags |= BFTW_PRUNE_MOUNTS; state->mount_arg = state->argv[0]; return parse_nullary_option(state); } @@ -1725,9 +1636,8 @@ fail: * Parse -nogroup. */ static struct expr *parse_nogroup(struct parser_state *state, int arg1, int arg2) { - struct cmdline *cmdline = state->cmdline; - if (!cmdline->groups) { - parse_error(state, "Couldn't parse the group table: %s.\n", strerror(cmdline->groups_error)); + if (!bfs_ctx_groups(state->ctx)) { + parse_error(state, "Couldn't parse the group table: %m.\n"); return NULL; } @@ -1751,9 +1661,9 @@ static struct expr *parse_nohidden(struct parser_state *state, int arg1, int arg hidden->probability = 0.01; hidden->pure = true; - struct cmdline *cmdline = state->cmdline; - cmdline->exclude = new_binary_expr(eval_or, cmdline->exclude, hidden, &fake_or_arg); - if (!cmdline->exclude) { + struct bfs_ctx *ctx = state->ctx; + ctx->exclude = new_binary_expr(eval_or, ctx->exclude, hidden, &fake_or_arg); + if (!ctx->exclude) { return NULL; } @@ -1773,9 +1683,8 @@ static struct expr *parse_noleaf(struct parser_state *state, int arg1, int arg2) * Parse -nouser. */ static struct expr *parse_nouser(struct parser_state *state, int arg1, int arg2) { - struct cmdline *cmdline = state->cmdline; - if (!cmdline->users) { - parse_error(state, "Couldn't parse the user table: %s.\n", strerror(cmdline->users_error)); + if (!bfs_ctx_users(state->ctx)) { + parse_error(state, "Couldn't parse the user table: %m.\n"); return NULL; } @@ -2083,7 +1992,7 @@ static struct expr *parse_printf(struct parser_state *state, int arg1, int arg2) init_print_expr(state, expr); - expr->printf = parse_bfs_printf(expr->sdata, state->cmdline); + expr->printf = parse_bfs_printf(expr->sdata, state->ctx); if (!expr->printf) { free_expr(expr); return NULL; @@ -2176,8 +2085,8 @@ static struct expr *parse_regex_extended(struct parser_state *state, int arg1, i * Parse -regextype TYPE. */ static struct expr *parse_regextype(struct parser_state *state, int arg1, int arg2) { - struct cmdline *cmdline = state->cmdline; - CFILE *cfile = cmdline->cerr; + struct bfs_ctx *ctx = state->ctx; + CFILE *cfile = ctx->cerr; const char *arg = state->argv[0]; const char *type = state->argv[1]; @@ -2192,7 +2101,7 @@ static struct expr *parse_regextype(struct parser_state *state, int arg1, int ar state->regex_flags = REG_EXTENDED; } else if (strcmp(type, "help") == 0) { state->just_info = true; - cfile = cmdline->cout; + cfile = ctx->cout; goto list_types; } else { parse_error(state, "${blu}%s${rs} ${bld}%s${rs}: Unsupported regex type.\n\n", arg, type); @@ -2212,7 +2121,7 @@ list_types: * Parse -s. */ static struct expr *parse_s(struct parser_state *state, int arg1, int arg2) { - state->cmdline->flags |= BFTW_SORT; + state->ctx->flags |= BFTW_SORT; return parse_nullary_flag(state); } @@ -2244,8 +2153,8 @@ static struct expr *parse_samefile(struct parser_state *state, int arg1, int arg * Parse -S STRATEGY. */ static struct expr *parse_search_strategy(struct parser_state *state, int arg1, int arg2) { - struct cmdline *cmdline = state->cmdline; - CFILE *cfile = cmdline->cerr; + struct bfs_ctx *ctx = state->ctx; + CFILE *cfile = ctx->cerr; const char *flag = state->argv[0]; const char *arg = state->argv[1]; @@ -2256,16 +2165,16 @@ static struct expr *parse_search_strategy(struct parser_state *state, int arg1, if (strcmp(arg, "bfs") == 0) { - cmdline->strategy = BFTW_BFS; + ctx->strategy = BFTW_BFS; } else if (strcmp(arg, "dfs") == 0) { - cmdline->strategy = BFTW_DFS; + ctx->strategy = BFTW_DFS; } else if (strcmp(arg, "ids") == 0) { - cmdline->strategy = BFTW_IDS; + ctx->strategy = BFTW_IDS; } else if (strcmp(arg, "eds") == 0) { - cmdline->strategy = BFTW_EDS; + ctx->strategy = BFTW_EDS; } else if (strcmp(arg, "help") == 0) { state->just_info = true; - cfile = cmdline->cout; + cfile = ctx->cout; goto list_strategies; } else { parse_error(state, "${cyn}%s${rs} ${bld}%s${rs}: Unrecognized search strategy.\n\n", flag, arg); @@ -2465,7 +2374,7 @@ static struct expr *parse_type(struct parser_state *state, int x, int arg2) { expr->idata = types; expr->probability = probability; - if (x && state->cmdline->optlevel < 4) { + if (x && state->ctx->optlevel < 4) { // Since -xtype dereferences symbolic links, it may have side // effects such as reporting permission errors, and thus // shouldn't be re-ordered without aggressive optimizations @@ -2483,7 +2392,7 @@ fail: * Parse -(no)?warn. */ static struct expr *parse_warn(struct parser_state *state, int warn, int arg2) { - state->cmdline->warn = warn; + state->ctx->warn = warn; return parse_nullary_positional_option(state); } @@ -2525,7 +2434,7 @@ static struct expr *parse_xattrname(struct parser_state *state, int arg1, int ar * Parse -xdev. */ static struct expr *parse_xdev(struct parser_state *state, int arg1, int arg2) { - state->cmdline->flags |= BFTW_PRUNE_MOUNTS; + state->ctx->flags |= BFTW_PRUNE_MOUNTS; state->xdev_arg = state->argv[0]; return parse_nullary_option(state); } @@ -2639,7 +2548,7 @@ fail: * "Parse" -help. */ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { - CFILE *cout = state->cmdline->cout; + CFILE *cout = state->ctx->cout; pid_t pager = -1; if (state->stdout_tty) { @@ -2899,7 +2808,7 @@ static struct expr *parse_help(struct parser_state *state, int arg1, int arg2) { * "Parse" -version. */ static struct expr *parse_version(struct parser_state *state, int arg1, int arg2) { - cfprintf(state->cmdline->cout, "${ex}bfs${rs} ${bld}%s${rs}\n\n", BFS_VERSION); + cfprintf(state->ctx->cout, "${ex}bfs${rs} ${bld}%s${rs}\n\n", BFS_VERSION); printf("%s\n", BFS_HOMEPAGE); @@ -3099,7 +3008,7 @@ static struct expr *parse_literal(struct parser_state *state) { match = table_lookup_fuzzy(arg); - CFILE *cerr = state->cmdline->cerr; + CFILE *cerr = state->ctx->cerr; parse_error(state, "Unknown argument ${er}%s${rs}; did you mean ", arg); switch (match->type) { case T_FLAG: @@ -3192,9 +3101,9 @@ static struct expr *parse_factor(struct parser_state *state) { state->excluding = false; - struct cmdline *cmdline = state->cmdline; - cmdline->exclude = new_binary_expr(eval_or, cmdline->exclude, factor, &fake_or_arg); - if (!cmdline->exclude) { + struct bfs_ctx *ctx = state->ctx; + ctx->exclude = new_binary_expr(eval_or, ctx->exclude, factor, &fake_or_arg); + if (!ctx->exclude) { return NULL; } @@ -3370,7 +3279,7 @@ static struct expr *parse_whole_expr(struct parser_state *state) { parse_warning(state, "${blu}%s${rs} is redundant in the presence of ${blu}%s${rs}.\n\n", state->xdev_arg, state->mount_arg); } - if (state->cmdline->warn && state->depth_arg && state->prune_arg) { + if (state->ctx->warn && state->depth_arg && state->prune_arg) { parse_warning(state, "${blu}%s${rs} does not work in the presence of ${blu}%s${rs}.\n", state->prune_arg, state->depth_arg); if (state->interactive) { @@ -3390,40 +3299,37 @@ fail: return NULL; } -/** - * Dump the parsed form of the command line, for debugging. - */ -void dump_cmdline(const struct cmdline *cmdline, enum debug_flags flag) { - if (!bfs_debug_prefix(cmdline, flag)) { +void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { + if (!bfs_debug_prefix(ctx, flag)) { return; } - CFILE *cerr = cmdline->cerr; + CFILE *cerr = ctx->cerr; - cfprintf(cerr, "${ex}%s${rs} ", cmdline->argv[0]); + cfprintf(cerr, "${ex}%s${rs} ", ctx->argv[0]); - if (cmdline->flags & BFTW_FOLLOW_ALL) { + if (ctx->flags & BFTW_FOLLOW_ALL) { cfprintf(cerr, "${cyn}-L${rs} "); - } else if (cmdline->flags & BFTW_FOLLOW_ROOTS) { + } else if (ctx->flags & BFTW_FOLLOW_ROOTS) { cfprintf(cerr, "${cyn}-H${rs} "); } else { cfprintf(cerr, "${cyn}-P${rs} "); } - if (cmdline->xargs_safe) { + if (ctx->xargs_safe) { cfprintf(cerr, "${cyn}-X${rs} "); } - if (cmdline->flags & BFTW_SORT) { + if (ctx->flags & BFTW_SORT) { cfprintf(cerr, "${cyn}-s${rs} "); } - if (cmdline->optlevel != 3) { - cfprintf(cerr, "${cyn}-O${bld}%d${rs} ", cmdline->optlevel); + if (ctx->optlevel != 3) { + cfprintf(cerr, "${cyn}-O${bld}%d${rs} ", ctx->optlevel); } const char *strategy = NULL; - switch (cmdline->strategy) { + switch (ctx->strategy) { case BFTW_BFS: strategy = "bfs"; break; @@ -3440,7 +3346,7 @@ void dump_cmdline(const struct cmdline *cmdline, enum debug_flags flag) { assert(strategy); cfprintf(cerr, "${cyn}-S${rs} ${bld}%s${rs} ", strategy); - enum debug_flags debug = cmdline->debug; + enum debug_flags debug = ctx->debug; if (debug) { cfprintf(cerr, "${cyn}-D${rs} "); for (int i = 0; debug; ++i) { @@ -3457,8 +3363,8 @@ void dump_cmdline(const struct cmdline *cmdline, enum debug_flags flag) { cfprintf(cerr, " "); } - for (size_t i = 0; i < darray_length(cmdline->paths); ++i) { - const char *path = cmdline->paths[i]; + for (size_t i = 0; i < darray_length(ctx->paths); ++i) { + const char *path = ctx->paths[i]; char c = path[0]; if (c == '-' || c == '(' || c == ')' || c == '!' || c == ',') { cfprintf(cerr, "${cyn}-f${rs} "); @@ -3466,43 +3372,43 @@ void dump_cmdline(const struct cmdline *cmdline, enum debug_flags flag) { cfprintf(cerr, "${mag}%s${rs} ", path); } - if (cmdline->cout->colors) { + if (ctx->cout->colors) { cfprintf(cerr, "${blu}-color${rs} "); } else { cfprintf(cerr, "${blu}-nocolor${rs} "); } - if (cmdline->flags & BFTW_POST_ORDER) { + if (ctx->flags & BFTW_POST_ORDER) { cfprintf(cerr, "${blu}-depth${rs} "); } - if (cmdline->ignore_races) { + if (ctx->ignore_races) { cfprintf(cerr, "${blu}-ignore_readdir_race${rs} "); } - if (cmdline->mindepth != 0) { - cfprintf(cerr, "${blu}-mindepth${rs} ${bld}%d${rs} ", cmdline->mindepth); + if (ctx->mindepth != 0) { + cfprintf(cerr, "${blu}-mindepth${rs} ${bld}%d${rs} ", ctx->mindepth); } - if (cmdline->maxdepth != INT_MAX) { - cfprintf(cerr, "${blu}-maxdepth${rs} ${bld}%d${rs} ", cmdline->maxdepth); + if (ctx->maxdepth != INT_MAX) { + cfprintf(cerr, "${blu}-maxdepth${rs} ${bld}%d${rs} ", ctx->maxdepth); } - if (cmdline->flags & BFTW_SKIP_MOUNTS) { + if (ctx->flags & BFTW_SKIP_MOUNTS) { cfprintf(cerr, "${blu}-mount${rs} "); } - if (cmdline->unique) { + if (ctx->unique) { cfprintf(cerr, "${blu}-unique${rs} "); } - if ((cmdline->flags & (BFTW_SKIP_MOUNTS | BFTW_PRUNE_MOUNTS)) == BFTW_PRUNE_MOUNTS) { + if ((ctx->flags & (BFTW_SKIP_MOUNTS | BFTW_PRUNE_MOUNTS)) == BFTW_PRUNE_MOUNTS) { cfprintf(cerr, "${blu}-xdev${rs} "); } if (flag == DEBUG_RATES) { - if (cmdline->exclude != &expr_false) { - cfprintf(cerr, "(${red}-exclude${rs} %pE) ", cmdline->exclude); + if (ctx->exclude != &expr_false) { + cfprintf(cerr, "(${red}-exclude${rs} %pE) ", ctx->exclude); } - cfprintf(cerr, "%pE", cmdline->expr); + cfprintf(cerr, "%pE", ctx->expr); } else { - if (cmdline->exclude != &expr_false) { - cfprintf(cerr, "(${red}-exclude${rs} %pe) ", cmdline->exclude); + if (ctx->exclude != &expr_false) { + cfprintf(cerr, "(${red}-exclude${rs} %pe) ", ctx->exclude); } - cfprintf(cerr, "%pe", cmdline->expr); + cfprintf(cerr, "%pe", ctx->expr); } fputs("\n", stderr); @@ -3511,10 +3417,10 @@ void dump_cmdline(const struct cmdline *cmdline, enum debug_flags flag) { /** * Dump the estimated costs. */ -static void dump_costs(const struct cmdline *cmdline) { - const struct expr *expr = cmdline->expr; - bfs_debug(cmdline, DEBUG_COST, " Cost: ~${ylw}%g${rs}\n", expr->cost); - bfs_debug(cmdline, DEBUG_COST, "Probability: ~${ylw}%g%%${rs}\n", 100.0*expr->probability); +static void dump_costs(const struct bfs_ctx *ctx) { + const struct expr *expr = ctx->expr; + bfs_debug(ctx, DEBUG_COST, " Cost: ~${ylw}%g${rs}\n", expr->cost); + bfs_debug(ctx, DEBUG_COST, "Probability: ~${ylw}%g%%${rs}\n", 100.0*expr->probability); } /** @@ -3540,56 +3446,26 @@ static int parse_gettime(struct timespec *ts) { #endif } -/** - * Parse the command line. - */ -struct cmdline *parse_cmdline(int argc, char *argv[]) { - struct cmdline *cmdline = malloc(sizeof(struct cmdline)); - if (!cmdline) { - perror("malloc()"); +struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { + struct bfs_ctx *ctx = bfs_ctx_new(); + if (!ctx) { + perror("bfs_new_ctx()"); goto fail; } - cmdline->argv = NULL; - cmdline->paths = NULL; - cmdline->colors = NULL; - cmdline->cout = NULL; - cmdline->cerr = NULL; - cmdline->users = NULL; - cmdline->users_error = 0; - cmdline->groups = NULL; - cmdline->groups_error = 0; - cmdline->mtab = NULL; - cmdline->mtab_error = 0; - cmdline->mindepth = 0; - cmdline->maxdepth = INT_MAX; - cmdline->flags = BFTW_RECOVER; - cmdline->strategy = BFTW_BFS; - cmdline->optlevel = 3; - cmdline->debug = 0; - cmdline->ignore_races = false; - cmdline->unique = false; - cmdline->warn = false; - cmdline->xargs_safe = false; - cmdline->exclude = &expr_false; - cmdline->expr = &expr_true; - cmdline->nopen_files = 0; - - trie_init(&cmdline->open_files); - static char* default_argv[] = {"bfs", NULL}; if (argc < 1) { argc = 1; argv = default_argv; } - cmdline->argv = malloc((argc + 1)*sizeof(*cmdline->argv)); - if (!cmdline->argv) { + ctx->argv = malloc((argc + 1)*sizeof(*ctx->argv)); + if (!ctx->argv) { perror("malloc()"); goto fail; } for (int i = 0; i <= argc; ++i) { - cmdline->argv[i] = argv[i]; + ctx->argv[i] = argv[i]; } enum use_color use_color = COLOR_AUTO; @@ -3598,41 +3474,26 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) { use_color = COLOR_NEVER; } - cmdline->colors = parse_colors(getenv("LS_COLORS")); - cmdline->cout = cfdup(stdout, use_color ? cmdline->colors : NULL); - cmdline->cerr = cfdup(stderr, use_color ? cmdline->colors : NULL); - if (!cmdline->cout || !cmdline->cerr) { + ctx->colors = parse_colors(getenv("LS_COLORS")); + ctx->cout = cfdup(stdout, use_color ? ctx->colors : NULL); + ctx->cerr = cfdup(stderr, use_color ? ctx->colors : NULL); + if (!ctx->cout || !ctx->cerr) { perror("cfdup()"); goto fail; } - cmdline->users = bfs_parse_users(); - if (!cmdline->users) { - cmdline->users_error = errno; - } - - cmdline->groups = bfs_parse_groups(); - if (!cmdline->groups) { - cmdline->groups_error = errno; - } - - cmdline->mtab = parse_bfs_mtab(); - if (!cmdline->mtab) { - cmdline->mtab_error = errno; - } - bool stdin_tty = isatty(STDIN_FILENO); bool stdout_tty = isatty(STDOUT_FILENO); bool stderr_tty = isatty(STDERR_FILENO); if (!getenv("POSIXLY_CORRECT")) { - cmdline->warn = stdin_tty; + ctx->warn = stdin_tty; } struct parser_state state = { - .cmdline = cmdline, - .argv = cmdline->argv + 1, - .command = cmdline->argv[0], + .ctx = ctx, + .argv = ctx->argv + 1, + .command = ctx->argv[0], .regex_flags = 0, .stdout_tty = stdout_tty, .interactive = stdin_tty && stderr_tty, @@ -3650,15 +3511,16 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) { if (strcmp(xbasename(state.command), "find") == 0) { // Operate depth-first when invoked as "find" - cmdline->strategy = BFTW_DFS; + ctx->strategy = BFTW_DFS; } if (parse_gettime(&state.now) != 0) { goto fail; } - cmdline->expr = parse_whole_expr(&state); - if (!cmdline->expr) { + ctx->exclude = &expr_false; + ctx->expr = parse_whole_expr(&state); + if (!ctx->expr) { if (state.just_info) { goto done; } else { @@ -3666,28 +3528,28 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) { } } - if (optimize_cmdline(cmdline) != 0) { + if (bfs_optimize(ctx) != 0) { goto fail; } - if (darray_length(cmdline->paths) == 0) { + if (darray_length(ctx->paths) == 0) { if (parse_root(&state, ".") != 0) { goto fail; } } - if ((cmdline->flags & BFTW_FOLLOW_ALL) && !cmdline->unique) { + if ((ctx->flags & BFTW_FOLLOW_ALL) && !ctx->unique) { // We need bftw() to detect cycles unless -unique does it for us - cmdline->flags |= BFTW_DETECT_CYCLES; + ctx->flags |= BFTW_DETECT_CYCLES; } - dump_cmdline(cmdline, DEBUG_TREE); - dump_costs(cmdline); + bfs_ctx_dump(ctx, DEBUG_TREE); + dump_costs(ctx); done: - return cmdline; + return ctx; fail: - free_cmdline(cmdline); + bfs_ctx_free(ctx); return NULL; } diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..337b5b9 --- /dev/null +++ b/parse.h @@ -0,0 +1,38 @@ +/**************************************************************************** + * 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. * + ****************************************************************************/ + +/** + * bfs command line parsing. + */ + +#ifndef BFS_PARSE_H +#define BFS_PARSE_H + +#include "ctx.h" + +/** + * Parse the command line. + * + * @param argc + * The number of arguments. + * @param argv + * The arguments to parse. + * @return + * A new bfs context, or NULL on failure. + */ +struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]); + +#endif // BFS_PARSE_H diff --git a/printf.c b/printf.c index a8cb8f0..124be22 100644 --- a/printf.c +++ b/printf.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. * @@ -15,8 +15,8 @@ ****************************************************************************/ #include "printf.h" -#include "cmdline.h" #include "color.h" +#include "ctx.h" #include "diag.h" #include "dstring.h" #include "expr.h" @@ -550,7 +550,7 @@ static struct bfs_printf **append_literal(struct bfs_printf **tail, struct bfs_p } } -struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) { +struct bfs_printf *parse_bfs_printf(const char *format, struct bfs_ctx *ctx) { struct bfs_printf *head = NULL; struct bfs_printf **tail = &head; @@ -595,11 +595,11 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) goto done; case '\0': - bfs_error(cmdline, "'%s': Incomplete escape sequence '\\'.\n", format); + bfs_error(ctx, "'%s': Incomplete escape sequence '\\'.\n", format); goto error; default: - bfs_error(cmdline, "'%s': Unrecognized escape sequence '\\%c'.\n", format, c); + bfs_error(ctx, "'%s': Unrecognized escape sequence '\\%c'.\n", format, c); goto error; } } else if (c == '%') { @@ -633,7 +633,7 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) case ' ': case '-': if (strchr(directive->str, c)) { - bfs_error(cmdline, "'%s': Duplicate flag '%c'.\n", format, c); + bfs_error(ctx, "'%s': Duplicate flag '%c'.\n", format, c); goto directive_error; } if (dstrapp(&directive->str, c) != 0) { @@ -689,19 +689,19 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) directive->fn = bfs_printf_f; break; case 'F': - if (!cmdline->mtab) { - bfs_error(cmdline, "Couldn't parse the mount table: %s.\n", strerror(cmdline->mtab_error)); + directive->ptr = bfs_ctx_mtab(ctx); + if (!directive->ptr) { + bfs_error(ctx, "Couldn't parse the mount table: %m.\n"); goto directive_error; } - directive->ptr = cmdline->mtab; directive->fn = bfs_printf_F; break; case 'g': - if (!cmdline->groups) { - bfs_error(cmdline, "Couldn't parse the group table: %s.\n", strerror(cmdline->groups_error)); + directive->ptr = bfs_ctx_groups(ctx); + if (!directive->ptr) { + bfs_error(ctx, "Couldn't parse the group table: %m.\n"); goto directive_error; } - directive->ptr = cmdline->groups; directive->fn = bfs_printf_g; break; case 'G': @@ -750,11 +750,11 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) directive->stat_field = BFS_STAT_MTIME; break; case 'u': - if (!cmdline->users) { - bfs_error(cmdline, "Couldn't parse the user table: %s.\n", strerror(cmdline->users_error)); + directive->ptr = bfs_ctx_users(ctx); + if (!directive->ptr) { + bfs_error(ctx, "Couldn't parse the user table: %m.\n"); goto directive_error; } - directive->ptr = cmdline->users; directive->fn = bfs_printf_u; break; case 'U': @@ -789,27 +789,27 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) directive->fn = bfs_printf_strftime; c = *++i; if (!c) { - bfs_error(cmdline, "'%s': Incomplete time specifier '%s%c'.\n", format, directive->str, i[-1]); + bfs_error(ctx, "'%s': Incomplete time specifier '%s%c'.\n", format, directive->str, i[-1]); goto directive_error; } else if (strchr("%+@aAbBcCdDeFgGhHIjklmMnprRsStTuUVwWxXyYzZ", c)) { directive->c = c; } else { - bfs_error(cmdline, "'%s': Unrecognized time specifier '%%%c%c'.\n", format, i[-1], c); + bfs_error(ctx, "'%s': Unrecognized time specifier '%%%c%c'.\n", format, i[-1], c); goto directive_error; } break; case '\0': - bfs_error(cmdline, "'%s': Incomplete format specifier '%s'.\n", format, directive->str); + bfs_error(ctx, "'%s': Incomplete format specifier '%s'.\n", format, directive->str); goto directive_error; default: - bfs_error(cmdline, "'%s': Unrecognized format specifier '%%%c'.\n", format, c); + bfs_error(ctx, "'%s': Unrecognized format specifier '%%%c'.\n", format, c); goto directive_error; } if (must_be_numeric && strcmp(specifier, "s") == 0) { - bfs_error(cmdline, "'%s': Invalid flags '%s' for string format '%%%c'.\n", format, directive->str + 1, c); + bfs_error(ctx, "'%s': Invalid flags '%s' for string format '%%%c'.\n", format, directive->str + 1, c); goto directive_error; } diff --git a/printf.h b/printf.h index 3bbbcd2..9be3b0b 100644 --- a/printf.h +++ b/printf.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. * @@ -22,7 +22,7 @@ #define BFS_PRINTF_H #include "bftw.h" -#include "cmdline.h" +#include "ctx.h" #include #include @@ -36,11 +36,12 @@ struct bfs_printf; * * @param format * The format string to parse. - * @param cmdline - * The command line. - * @return The parsed printf command, or NULL on failure. + * @param ctx + * The bfs context. + * @return + * The parsed printf command, or NULL on failure. */ -struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline); +struct bfs_printf *parse_bfs_printf(const char *format, struct bfs_ctx *ctx); /** * Evaluate a parsed format string. @@ -52,7 +53,8 @@ struct bfs_printf *parse_bfs_printf(const char *format, struct cmdline *cmdline) * @param ftwbuf * The bftw() data for the current file. If needs_stat is true, statbuf * must be non-NULL. - * @return 0 on success, -1 on failure. + * @return + * 0 on success, -1 on failure. */ int bfs_printf(FILE *file, const struct bfs_printf *command, const struct BFTW *ftwbuf); -- cgit v1.2.3