summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2020-09-27 12:55:55 -0400
committerTavian Barnes <tavianator@tavianator.com>2020-09-27 13:23:49 -0400
commit62bbbe1a4165f63b31c68b1595ecb0e67d7af3dc (patch)
treec770ff13f37023cae132108b29077a904e6d4dfb
parent3c83bf4e1920be909f65945e56dc8b779c472a59 (diff)
downloadbfs-62bbbe1a4165f63b31c68b1595ecb0e67d7af3dc.tar.xz
Rename struct cmdline to bfs_ctx
The API remains similar, with some added accessor functions for lazy initialization of the pwcache and mtab.
-rw-r--r--Makefile1
-rw-r--r--ctx.c217
-rw-r--r--ctx.h (renamed from cmdline.h)135
-rw-r--r--diag.c48
-rw-r--r--diag.h20
-rw-r--r--eval.c146
-rw-r--r--eval.h12
-rw-r--r--exec.c24
-rw-r--r--exec.h17
-rw-r--r--main.c24
-rw-r--r--mtab.c8
-rw-r--r--mtab.h6
-rw-r--r--opt.c77
-rw-r--r--opt.h37
-rw-r--r--parse.c482
-rw-r--r--parse.h38
-rw-r--r--printf.c40
-rw-r--r--printf.h16
18 files changed, 786 insertions, 562 deletions
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/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 <tavianator@tavianator.com> *
+ * *
+ * 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 <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+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/cmdline.h b/ctx.h
index 309dfa3..b638e74 100644
--- a/cmdline.h
+++ b/ctx.h
@@ -15,14 +15,14 @@
****************************************************************************/
/**
- * Representation of the parsed command line.
+ * bfs execution context.
*/
-#ifndef BFS_CMDLINE_H
-#define BFS_CMDLINE_H
+#ifndef BFS_CTX_H
+#define BFS_CTX_H
-#include "color.h"
#include "trie.h"
+#include "color.h"
/**
* Various debugging flags.
@@ -47,35 +47,17 @@ enum debug_flags {
};
/**
- * The parsed command line.
+ * The execution context for bfs.
*/
-struct cmdline {
+struct bfs_ctx {
/** 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;
+ /** The main command line expression. */
+ struct expr *expr;
+ /** An expression for files to filter out. */
+ struct expr *exclude;
/** -mindepth option. */
int mindepth;
@@ -100,44 +82,99 @@ struct cmdline {
/** 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;
+ /** 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 open files owned by the command line. */
- struct trie open_files;
- /** The number of open files owned by the command line. */
- int nopen_files;
+ /** All the files owned by the context. */
+ struct trie files;
+ /** The number of files owned by the context. */
+ int nfiles;
};
/**
- * Parse the command line.
+ * @return
+ * A new bfs context, or NULL on failure.
*/
-struct cmdline *parse_cmdline(int argc, char *argv[]);
+struct bfs_ctx *bfs_ctx_new(void);
/**
- * Dump the parsed command line.
+ * 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.
*/
-void dump_cmdline(const struct cmdline *cmdline, enum debug_flags flag);
+const struct bfs_mtab *bfs_ctx_mtab(const struct bfs_ctx *ctx);
/**
- * Optimize the parsed command line.
+ * Open a file for the bfs context.
*
- * @return 0 if successful, -1 on error.
+ * @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.
*/
-int optimize_cmdline(struct cmdline *cmdline);
+CFILE *bfs_ctx_open(struct bfs_ctx *ctx, const char *path, bool use_color);
/**
- * Evaluate the command line.
+ * Dump the parsed command line.
+ *
+ * @param ctx
+ * The bfs context.
+ * @param flag
+ * The -D flag that triggered the dump.
*/
-int eval_cmdline(const struct cmdline *cmdline);
+void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag);
/**
- * Free the parsed command line.
+ * Free a bfs context.
*
- * @return 0 if successful, -1 on error.
+ * @param ctx
+ * The context to free.
+ * @return
+ * 0 on success, -1 if any errors occurred.
*/
-int free_cmdline(struct cmdline *cmdline);
+int bfs_ctx_free(struct bfs_ctx *ctx);
-#endif // BFS_CMDLINE_H
+#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 <assert.h>
@@ -24,77 +24,77 @@
#include <stdlib.h>
#include <string.h>
-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 <stdarg.h>
#include <stdbool.h>
@@ -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 <tavianator@tavianator.com> *
+ * Copyright (C) 2017-2020 Tavian Barnes <tavianator@tavianator.com> *
* *
* 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 <errno.h>
#include <fcntl.h>
@@ -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 <tavianator@tavianator.com> *
+ * Copyright (C) 2017-2020 Tavian Barnes <tavianator@tavianator.com> *
* *
* 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 <tavianator@tavianator.com> *
+ * Copyright (C) 2017-2020 Tavian Barnes <tavianator@tavianator.com> *
* *
* 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 <tavianator@tavianator.com> *
+ * *
+ * 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"
@@ -209,69 +211,6 @@ static void expr_set_never_returns(struct expr *expr) {
}
/**
- * 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.
*/
enum use_color {
@@ -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 <tavianator@tavianator.com> *
+ * *
+ * 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 <tavianator@tavianator.com> *
+ * Copyright (C) 2017-2020 Tavian Barnes <tavianator@tavianator.com> *
* *
* 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 <tavianator@tavianator.com> *
+ * Copyright (C) 2017-2020 Tavian Barnes <tavianator@tavianator.com> *
* *
* 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 <stdbool.h>
#include <stdio.h>
@@ -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);