From 6961c9a4c2fe8612db222bfd1693e38f7a43a2cd Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 29 Nov 2022 16:07:17 -0500 Subject: expr: Remove the synthetic flag Only diagnostics cares about this, and we can just check if the pointers are equal. --- src/opt.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index fa2e66c..f7f80d7 100644 --- a/src/opt.c +++ b/src/opt.c @@ -366,10 +366,6 @@ static struct bfs_expr *negate_expr(struct bfs_expr *rhs, char **argv) { return NULL; } - if (argv == &fake_not_arg) { - expr->synthetic = true; - } - expr->lhs = NULL; expr->rhs = rhs; return expr; @@ -404,7 +400,6 @@ static struct bfs_expr *de_morgan(const struct opt_state *state, struct bfs_expr expr->eval_fn = eval_and; expr->argv = &fake_and_arg; } - expr->synthetic = true; expr->lhs = negate_expr(expr->lhs, argv); expr->rhs = negate_expr(expr->rhs, argv); -- cgit v1.2.3 From 1d68b43f1554ee82b3ac5772534a5ecbd57855a7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 30 Nov 2022 10:51:45 -0500 Subject: expr: Remove the singleton bfs_{true,false} expressions --- src/color.c | 4 + src/expr.h | 5 - src/opt.c | 68 ++++++++------ src/parse.c | 306 +++++++++++++++++++++++++++++++++--------------------------- 4 files changed, 209 insertions(+), 174 deletions(-) (limited to 'src/opt.c') diff --git a/src/color.c b/src/color.c index 98fe9f2..94fcac8 100644 --- a/src/color.c +++ b/src/color.c @@ -877,6 +877,10 @@ static int cbuff(CFILE *cfile, const char *format, ...); /** Dump a parsed expression tree, for debugging. */ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { + if (!expr) { + return dstrcat(&cfile->buffer, "(null)"); + } + if (dstrcat(&cfile->buffer, "(") != 0) { return -1; } diff --git a/src/expr.h b/src/expr.h index 2de8958..e541d8e 100644 --- a/src/expr.h +++ b/src/expr.h @@ -202,11 +202,6 @@ struct bfs_expr { }; }; -/** Singleton true expression instance. */ -extern struct bfs_expr bfs_true; -/** Singleton false expression instance. */ -extern struct bfs_expr bfs_false; - /** * Create a new expression. */ diff --git a/src/opt.c b/src/opt.c index f7f80d7..441c611 100644 --- a/src/opt.c +++ b/src/opt.c @@ -344,6 +344,13 @@ static void opt_warning(const struct opt_state *state, const struct bfs_expr *ex } } +/** Create a constant expression. */ +static struct bfs_expr *opt_const(bool value) { + static bfs_eval_fn *fns[] = {eval_false, eval_true}; + static char *fake_args[] = {"-false", "-true"}; + return bfs_expr_new(fns[value], 1, &fake_args[value]); +} + /** Extract a child expression, freeing the outer expression. */ static struct bfs_expr *extract_child_expr(struct bfs_expr *expr, struct bfs_expr **child) { struct bfs_expr *ret = *child; @@ -457,14 +464,11 @@ static struct bfs_expr *optimize_not_expr(const struct opt_state *state, struct int optlevel = state->ctx->optlevel; if (optlevel >= 1) { - if (rhs == &bfs_true) { - opt_debug(state, 1, "constant propagation: %pe <==> %pe\n", expr, &bfs_false); + if (rhs->eval_fn == eval_true || rhs->eval_fn == eval_false) { + struct bfs_expr *ret = opt_const(rhs->eval_fn == eval_false); + opt_debug(state, 1, "constant propagation: %pe <==> %pe\n", expr, ret); bfs_expr_free(expr); - return &bfs_false; - } else if (rhs == &bfs_false) { - opt_debug(state, 1, "constant propagation: %pe <==> %pe\n", expr, &bfs_true); - bfs_expr_free(expr); - return &bfs_true; + return ret; } else if (rhs->eval_fn == eval_not) { opt_debug(state, 1, "double negation: %pe <==> %pe\n", expr, rhs->rhs); return extract_child_expr(expr, &rhs->rhs); @@ -514,17 +518,17 @@ static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct const struct bfs_ctx *ctx = state->ctx; int optlevel = ctx->optlevel; if (optlevel >= 1) { - if (lhs == &bfs_true) { + if (lhs->eval_fn == eval_true) { opt_debug(state, 1, "conjunction elimination: %pe <==> %pe\n", expr, rhs); return extract_child_expr(expr, &expr->rhs); - } else if (rhs == &bfs_true) { + } else if (rhs->eval_fn == eval_true) { opt_debug(state, 1, "conjunction elimination: %pe <==> %pe\n", expr, lhs); return extract_child_expr(expr, &expr->lhs); } else if (lhs->always_false) { opt_debug(state, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); opt_warning(state, expr->rhs, "This expression is unreachable.\n\n"); return extract_child_expr(expr, &expr->lhs); - } else if (lhs->always_true && rhs == &bfs_false) { + } else if (lhs->always_true && rhs->eval_fn == eval_false) { bool debug = opt_debug(state, 1, "strength reduction: %pe <==> ", expr); struct bfs_expr *ret = extract_child_expr(expr, &expr->lhs); ret = negate_expr(ret, &fake_not_arg); @@ -532,7 +536,7 @@ static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct cfprintf(ctx->cerr, "%pe\n", ret); } return ret; - } else if (optlevel >= 2 && lhs->pure && rhs == &bfs_false) { + } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_false) { opt_debug(state, 2, "purity: %pe <==> %pe\n", expr, rhs); opt_warning(state, expr->lhs, "The result of this expression is ignored.\n\n"); return extract_child_expr(expr, &expr->rhs); @@ -589,13 +593,13 @@ static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct b opt_debug(state, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); opt_warning(state, expr->rhs, "This expression is unreachable.\n\n"); return extract_child_expr(expr, &expr->lhs); - } else if (lhs == &bfs_false) { + } else if (lhs->eval_fn == eval_false) { opt_debug(state, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, rhs); return extract_child_expr(expr, &expr->rhs); - } else if (rhs == &bfs_false) { + } else if (rhs->eval_fn == eval_false) { opt_debug(state, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, lhs); return extract_child_expr(expr, &expr->lhs); - } else if (lhs->always_false && rhs == &bfs_true) { + } else if (lhs->always_false && rhs->eval_fn == eval_true) { bool debug = opt_debug(state, 1, "strength reduction: %pe <==> ", expr); struct bfs_expr *ret = extract_child_expr(expr, &expr->lhs); ret = negate_expr(ret, &fake_not_arg); @@ -603,7 +607,7 @@ static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct b cfprintf(ctx->cerr, "%pe\n", ret); } return ret; - } else if (optlevel >= 2 && lhs->pure && rhs == &bfs_true) { + } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_true) { opt_debug(state, 2, "purity: %pe <==> %pe\n", expr, rhs); opt_warning(state, expr->lhs, "The result of this expression is ignored.\n\n"); return extract_child_expr(expr, &expr->rhs); @@ -667,11 +671,12 @@ static struct bfs_expr *ignore_result(const struct opt_state *state, struct bfs_ } } - if (optlevel >= 2 && expr->pure && expr != &bfs_false) { - opt_debug(state, 2, "ignored result: %pe --> %pe\n", expr, &bfs_false); + if (optlevel >= 2 && expr->pure && expr->eval_fn != eval_false) { + struct bfs_expr *ret = opt_const(false); + opt_debug(state, 2, "ignored result: %pe --> %pe\n", expr, ret); opt_warning(state, expr, "The result of this expression is ignored.\n\n"); bfs_expr_free(expr); - expr = &bfs_false; + return ret; } } @@ -693,8 +698,8 @@ static struct bfs_expr *optimize_comma_expr(const struct opt_state *state, struc opt_debug(state, 1, "reachability: %pe <==> %pe\n", expr, lhs); opt_warning(state, expr->rhs, "This expression is unreachable.\n\n"); return extract_child_expr(expr, &expr->lhs); - } else if ((lhs->always_true && rhs == &bfs_true) - || (lhs->always_false && rhs == &bfs_false)) { + } else if ((lhs->always_true && rhs->eval_fn == eval_true) + || (lhs->always_false && rhs->eval_fn == eval_false)) { opt_debug(state, 1, "redundancy elimination: %pe <==> %pe\n", expr, lhs); return extract_child_expr(expr, &expr->lhs); } else if (optlevel >= 2 && lhs->pure) { @@ -835,11 +840,11 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct state->facts_when_false = state->facts; if (optlevel >= 2 && facts_are_impossible(&state->facts)) { - opt_debug(state, 2, "reachability: %pe --> %pe\n", expr, &bfs_false); + struct bfs_expr *ret = opt_const(false); + opt_debug(state, 2, "reachability: %pe --> %pe\n", expr, ret); opt_warning(state, expr, "This expression is unreachable.\n\n"); bfs_expr_free(expr); - expr = &bfs_false; - goto done; + return ret; } if (!bfs_expr_has_children(expr) && !expr->pure) { @@ -893,7 +898,7 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct } if (!expr) { - goto done; + return NULL; } if (bfs_expr_has_children(expr)) { @@ -918,33 +923,34 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct set_facts_impossible(&state->facts_when_true); } - if (optlevel < 2 || expr == &bfs_true || expr == &bfs_false) { - goto done; + if (optlevel < 2 || expr->eval_fn == eval_true || expr->eval_fn == eval_false) { + return expr; } if (facts_are_impossible(&state->facts_when_true)) { if (expr->pure) { - opt_debug(state, 2, "data flow: %pe --> %pe\n", expr, &bfs_false); + struct bfs_expr *ret = opt_const(false); opt_warning(state, expr, "This expression is always false.\n\n"); + opt_debug(state, 2, "data flow: %pe --> %pe\n", expr, ret); bfs_expr_free(expr); - expr = &bfs_false; + return ret; } else { expr->always_false = true; expr->probability = 0.0; } } else if (facts_are_impossible(&state->facts_when_false)) { if (expr->pure) { - opt_debug(state, 2, "data flow: %pe --> %pe\n", expr, &bfs_true); + struct bfs_expr *ret = opt_const(true); + opt_debug(state, 2, "data flow: %pe --> %pe\n", expr, ret); opt_warning(state, expr, "This expression is always true.\n\n"); bfs_expr_free(expr); - expr = &bfs_true; + return ret; } else { expr->always_true = true; expr->probability = 1.0; } } -done: return expr; } diff --git a/src/parse.c b/src/parse.c index 2136bb5..16132f3 100644 --- a/src/parse.c +++ b/src/parse.c @@ -73,45 +73,6 @@ static char *fake_true_arg = "-true"; #define STAT_COST 1000.0 #define PRINT_COST 20000.0 -struct bfs_expr bfs_true = { - .eval_fn = eval_true, - .argc = 1, - .argv = &fake_true_arg, - .pure = true, - .always_true = true, - .cost = FAST_COST, - .probability = 1.0, -}; - -struct bfs_expr bfs_false = { - .eval_fn = eval_false, - .argc = 1, - .argv = &fake_false_arg, - .pure = true, - .always_false = true, - .cost = FAST_COST, - .probability = 0.0, -}; - -void bfs_expr_free(struct bfs_expr *expr) { - if (!expr || expr == &bfs_true || expr == &bfs_false) { - return; - } - - if (bfs_expr_has_children(expr)) { - bfs_expr_free(expr->rhs); - bfs_expr_free(expr->lhs); - } else if (expr->eval_fn == eval_exec) { - bfs_exec_free(expr->exec); - } else if (expr->eval_fn == eval_fprintf) { - bfs_printf_free(expr->printf); - } else if (expr->eval_fn == eval_regex) { - bfs_regfree(expr->regex); - } - - free(expr); -} - struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { struct bfs_expr *expr = malloc(sizeof(*expr)); if (!expr) { @@ -124,16 +85,29 @@ struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { expr->argv = argv; expr->persistent_fds = 0; expr->ephemeral_fds = 0; - expr->pure = false; - expr->always_true = false; - expr->always_false = false; expr->cost = FAST_COST; - expr->probability = 0.5; expr->evaluations = 0; expr->successes = 0; expr->elapsed.tv_sec = 0; expr->elapsed.tv_nsec = 0; + if (eval_fn == eval_true) { + expr->pure = true; + expr->always_true = true; + expr->always_false = false; + expr->probability = 1.0; + } else if (eval_fn == eval_false) { + expr->pure = true; + expr->always_true = false; + expr->always_false = true; + expr->probability = 0.0; + } else { + expr->pure = false; + expr->always_true = false; + expr->always_false = false; + expr->probability = 0.5; + } + // Prevent bfs_expr_free() from freeing uninitialized pointers on error paths if (bfs_expr_has_children(expr)) { expr->lhs = NULL; @@ -149,6 +123,37 @@ struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { return expr; } +bool bfs_expr_has_children(const struct bfs_expr *expr) { + return expr->eval_fn == eval_and + || expr->eval_fn == eval_or + || expr->eval_fn == eval_not + || expr->eval_fn == eval_comma; +} + +bool bfs_expr_never_returns(const struct bfs_expr *expr) { + // Expressions that never return are vacuously both always true and always false + return expr->always_true && expr->always_false; +} + +void bfs_expr_free(struct bfs_expr *expr) { + if (!expr) { + return; + } + + if (bfs_expr_has_children(expr)) { + bfs_expr_free(expr->rhs); + bfs_expr_free(expr->lhs); + } else if (expr->eval_fn == eval_exec) { + bfs_exec_free(expr->exec); + } else if (expr->eval_fn == eval_fprintf) { + bfs_printf_free(expr->printf); + } else if (expr->eval_fn == eval_regex) { + bfs_regfree(expr->regex); + } + + free(expr); +} + /** * Create a new unary expression. */ @@ -193,18 +198,6 @@ static struct bfs_expr *new_binary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *l return expr; } -bool bfs_expr_has_children(const struct bfs_expr *expr) { - return expr->eval_fn == eval_and - || expr->eval_fn == eval_or - || expr->eval_fn == eval_not - || expr->eval_fn == eval_comma; -} - -bool bfs_expr_never_returns(const struct bfs_expr *expr) { - // Expressions that never return are vacuously both always true and always false - return expr->always_true && expr->always_false; -} - /** * Set an expression to always return true. */ @@ -762,8 +755,8 @@ static bool looks_like_icmp(const char *str) { * Parse a single flag. */ static struct bfs_expr *parse_flag(struct parser_state *state, size_t argc) { - parser_advance(state, T_FLAG, argc); - return &bfs_true; + char **argv = parser_advance(state, T_FLAG, argc); + return bfs_expr_new(eval_true, argc, argv); } /** @@ -773,12 +766,26 @@ static struct bfs_expr *parse_nullary_flag(struct parser_state *state) { return parse_flag(state, 1); } +/** + * Parse a flag that takes a value. + */ +static struct bfs_expr *parse_unary_flag(struct parser_state *state) { + const char *arg = state->argv[0]; + const char *value = state->argv[1]; + if (!value) { + parse_error(state, "${cyn}%s${rs} needs a value.\n", arg); + return NULL; + } + + return parse_flag(state, 2); +} + /** * Parse a single option. */ static struct bfs_expr *parse_option(struct parser_state *state, size_t argc) { - parser_advance(state, T_OPTION, argc); - return &bfs_true; + char **argv = parser_advance(state, T_OPTION, argc); + return bfs_expr_new(eval_true, argc, argv); } /** @@ -792,6 +799,13 @@ static struct bfs_expr *parse_nullary_option(struct parser_state *state) { * Parse an option that takes a value. */ static struct bfs_expr *parse_unary_option(struct parser_state *state) { + const char *arg = state->argv[0]; + const char *value = state->argv[1]; + if (!value) { + parse_error(state, "${blu}%s${rs} needs a value.\n", arg); + return NULL; + } + return parse_option(state, 2); } @@ -929,19 +943,16 @@ static bool parse_debug_flag(const char *flag, size_t len, const char *expected) static struct bfs_expr *parse_debug(struct parser_state *state, int arg1, int arg2) { 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); + struct bfs_expr *expr = parse_unary_flag(state); + if (!expr) { + cfprintf(ctx->cerr, "\n"); debug_help(ctx->cerr); return NULL; } - parser_advance(state, T_FLAG, 1); - bool unrecognized = false; - for (const char *flag = flags, *next; flag; flag = next) { + for (const char *flag = expr->argv[1], *next; flag; flag = next) { size_t len = strcspn(flag, ","); if (flag[len]) { next = flag + len + 1; @@ -952,6 +963,7 @@ static struct bfs_expr *parse_debug(struct parser_state *state, int arg1, int ar if (parse_debug_flag(flag, len, "help")) { debug_help(ctx->cout); state->just_info = true; + bfs_expr_free(expr); return NULL; } else if (parse_debug_flag(flag, len, "all")) { ctx->debug = DEBUG_ALL; @@ -969,7 +981,7 @@ static struct bfs_expr *parse_debug(struct parser_state *state, int arg1, int ar if (DEBUG_ALL & i) { ctx->debug |= i; } else { - if (parse_warning(state, "Unrecognized debug flag ${bld}")) { + if (parse_expr_warning(state, expr, "Unrecognized debug flag ${bld}")) { fwrite(flag, 1, len, stderr); cfprintf(ctx->cerr, "${rs}.\n\n"); unrecognized = true; @@ -982,27 +994,32 @@ static struct bfs_expr *parse_debug(struct parser_state *state, int arg1, int ar cfprintf(ctx->cerr, "\n"); } - parser_advance(state, T_FLAG, 1); - return &bfs_true; + return expr; } /** * Parse -On. */ static struct bfs_expr *parse_optlevel(struct parser_state *state, int arg1, int arg2) { + struct bfs_expr *expr = parse_nullary_flag(state); + if (!expr) { + return NULL; + } + int *optlevel = &state->ctx->optlevel; - if (strcmp(state->argv[0], "-Ofast") == 0) { + if (strcmp(expr->argv[0], "-Ofast") == 0) { *optlevel = 4; - } else if (!parse_int(state, state->argv, state->argv[0] + 2, optlevel, IF_INT | IF_UNSIGNED)) { + } else if (!parse_int(state, expr->argv, expr->argv[0] + 2, optlevel, IF_INT | IF_UNSIGNED)) { + bfs_expr_free(expr); return NULL; } if (*optlevel > 4) { - parse_warning(state, "${cyn}-O${bld}%s${rs} is the same as ${cyn}-O${bld}4${rs}.\n\n", state->argv[0] + 2); + parse_expr_warning(state, expr, "${cyn}-O${bld}%s${rs} is the same as ${cyn}-O${bld}4${rs}.\n\n", state->argv[0] + 2); } - return parse_nullary_flag(state); + return expr; } /** @@ -1203,12 +1220,18 @@ static struct bfs_expr *parse_capable(struct parser_state *state, int flag, int * Parse -(no)?color. */ static struct bfs_expr *parse_color(struct parser_state *state, int color, int arg2) { + struct bfs_expr *expr = parse_nullary_option(state); + if (!expr) { + return NULL; + } + struct bfs_ctx *ctx = state->ctx; struct colors *colors = ctx->colors; if (color) { if (!colors) { - parse_error(state, "%s.\n", strerror(ctx->colors_error)); + parse_expr_error(state, expr, "%s.\n", strerror(ctx->colors_error)); + bfs_expr_free(expr); return NULL; } @@ -1221,15 +1244,14 @@ static struct bfs_expr *parse_color(struct parser_state *state, int color, int a ctx->cerr->colors = NULL; } - return parse_nullary_option(state); + return expr; } /** * Parse -{false,true}. */ static struct bfs_expr *parse_const(struct parser_state *state, int value, int arg2) { - parser_advance(state, T_TEST, 1); - return value ? &bfs_true : &bfs_false; + return parse_nullary_test(state, value ? eval_true : eval_false); } /** @@ -1295,20 +1317,20 @@ static struct bfs_expr *parse_depth_n(struct parser_state *state, int arg1, int * Parse -{min,max}depth N. */ static struct bfs_expr *parse_depth_limit(struct parser_state *state, int is_min, int arg2) { - struct bfs_ctx *ctx = state->ctx; - const char *arg = state->argv[0]; - const char *value = state->argv[1]; - if (!value) { - parse_error(state, "${blu}%s${rs} needs a value.\n", arg); + struct bfs_expr *expr = parse_unary_option(state); + if (!expr) { return NULL; } + struct bfs_ctx *ctx = state->ctx; int *depth = is_min ? &ctx->mindepth : &ctx->maxdepth; - if (!parse_int(state, &state->argv[1], value, depth, IF_INT | IF_UNSIGNED)) { + char **arg = &expr->argv[1]; + if (!parse_int(state, arg, *arg, depth, IF_INT | IF_UNSIGNED)) { + bfs_expr_free(expr); return NULL; } - return parse_unary_option(state); + return expr; } /** @@ -1398,33 +1420,30 @@ static struct bfs_expr *parse_exit(struct parser_state *state, int arg1, int arg * Parse -f PATH. */ static struct bfs_expr *parse_f(struct parser_state *state, int arg1, int arg2) { - const char *path = state->argv[1]; - if (!path) { - parse_error(state, "${cyn}-f${rs} requires a path.\n"); + struct bfs_expr *expr = parse_unary_flag(state); + if (!expr) { return NULL; } - if (parse_root(state, path) != 0) { + if (parse_root(state, expr->argv[1]) != 0) { + bfs_expr_free(expr); return NULL; } - parser_advance(state, T_FLAG, 1); - parser_advance(state, T_PATH, 1); - return &bfs_true; + return expr; } /** * Parse -files0-from PATH. */ static struct bfs_expr *parse_files0_from(struct parser_state *state, int arg1, int arg2) { - const char *arg = state->argv[0]; - const char *from = state->argv[1]; - if (!from) { - parse_error(state, "${blu}%s${rs} requires a path.\n", arg); + struct bfs_expr *expr = parse_unary_option(state); + if (!expr) { return NULL; } - state->files0_arg = parser_advance(state, T_OPTION, 1); + state->files0_arg = expr->argv; + const char *from = expr->argv[1]; FILE *file; if (strcmp(from, "-") == 0) { @@ -1433,27 +1452,24 @@ static struct bfs_expr *parse_files0_from(struct parser_state *state, int arg1, file = xfopen(from, O_RDONLY | O_CLOEXEC); } if (!file) { - parse_error(state, "%m.\n"); - return NULL; + parse_expr_error(state, expr, "%m.\n"); + goto fail; } - struct bfs_expr *expr = &bfs_true; - while (true) { char *path = xgetdelim(file, '\0'); if (!path) { if (errno) { - parse_error(state, "%m.\n"); - expr = NULL; + goto fail; + } else { + break; } - break; } int ret = parse_root(state, path); free(path); if (ret != 0) { - expr = NULL; - break; + goto fail; } } @@ -1464,8 +1480,14 @@ static struct bfs_expr *parse_files0_from(struct parser_state *state, int arg1, } state->implicit_root = false; - parser_advance(state, T_OPTION, 1); return expr; + +fail: + if (file && file != stdin) { + fclose(file); + } + bfs_expr_free(expr); + return NULL; } /** @@ -1771,12 +1793,17 @@ static struct bfs_expr *parse_ls(struct parser_state *state, int arg1, int arg2) * Parse -mount. */ static struct bfs_expr *parse_mount(struct parser_state *state, int arg1, int arg2) { - parse_warning(state, "In the future, ${blu}%s${rs} will skip mount points entirely, unlike\n", state->argv[0]); + struct bfs_expr *expr = parse_nullary_option(state); + if (!expr) { + return NULL; + } + + parse_expr_warning(state, expr, "In the future, ${blu}%s${rs} will skip mount points entirely, unlike\n", expr->argv[0]); bfs_warning(state->ctx, "${blu}-xdev${rs}, due to http://austingroupbugs.net/view.php?id=1133.\n\n"); state->ctx->flags |= BFTW_PRUNE_MOUNTS; state->mount_arg = state->argv; - return parse_nullary_option(state); + return expr; } /** @@ -1814,8 +1841,8 @@ static struct bfs_expr *parse_fnmatch(const struct parser_state *state, struct b } if (i % 2 != 0) { parse_expr_warning(state, expr, "Unescaped trailing backslash.\n\n"); - bfs_expr_free(expr); - return &bfs_false; + expr->eval_fn = eval_false; + return expr; } expr->cost = 400.0; @@ -2009,8 +2036,7 @@ static struct bfs_expr *parse_nohidden(struct parser_state *state, int arg1, int return NULL; } - parser_advance(state, T_OPTION, 1); - return &bfs_true; + return parse_nullary_option(state); } /** @@ -2430,16 +2456,14 @@ static struct bfs_expr *parse_regextype(struct parser_state *state, int arg1, in struct bfs_ctx *ctx = state->ctx; CFILE *cfile = ctx->cerr; - const char *arg = state->argv[0]; - const char *type = state->argv[1]; - if (!type) { - parse_error(state, "${blu}%s${rs} needs a value.\n\n", arg); + struct bfs_expr *expr = parse_unary_option(state); + if (!expr) { + cfprintf(cfile, "\n"); goto list_types; } - parser_advance(state, T_OPTION, 1); - // See https://www.gnu.org/software/gnulib/manual/html_node/Predefined-Syntaxes.html + const char *type = expr->argv[1]; if (strcmp(type, "posix-basic") == 0 || strcmp(type, "ed") == 0 || strcmp(type, "sed") == 0) { @@ -2457,12 +2481,11 @@ static struct bfs_expr *parse_regextype(struct parser_state *state, int arg1, in cfile = ctx->cout; goto list_types; } else { - parse_error(state, "Unsupported regex type.\n\n"); + parse_expr_error(state, expr, "Unsupported regex type.\n\n"); goto list_types; } - parser_advance(state, T_OPTION, 1); - return &bfs_true; + return expr; list_types: cfprintf(cfile, "Supported types are:\n\n"); @@ -2474,6 +2497,8 @@ list_types: cfprintf(cfile, " ${bld}grep${rs}: Like ${grn}grep${rs}\n"); #endif cfprintf(cfile, " ${bld}sed${rs}: Like ${grn}sed${rs} (same as ${bld}posix-basic${rs})\n"); + + bfs_expr_free(expr); return NULL; } @@ -2516,15 +2541,13 @@ static struct bfs_expr *parse_search_strategy(struct parser_state *state, int ar struct bfs_ctx *ctx = state->ctx; CFILE *cfile = ctx->cerr; - const char *flag = state->argv[0]; - const char *arg = state->argv[1]; - if (!arg) { - parse_error(state, "${cyn}%s${rs} needs an argument.\n\n", flag); + struct bfs_expr *expr = parse_unary_flag(state); + if (!expr) { + cfprintf(cfile, "\n"); goto list_strategies; } - parser_advance(state, T_FLAG, 1); - + const char *arg = expr->argv[1]; if (strcmp(arg, "bfs") == 0) { ctx->strategy = BFTW_BFS; } else if (strcmp(arg, "dfs") == 0) { @@ -2538,12 +2561,11 @@ static struct bfs_expr *parse_search_strategy(struct parser_state *state, int ar cfile = ctx->cout; goto list_strategies; } else { - parse_error(state, "Unrecognized search strategy.\n\n"); + parse_expr_error(state, expr, "Unrecognized search strategy.\n\n"); goto list_strategies; } - parser_advance(state, T_FLAG, 1); - return &bfs_true; + return expr; list_strategies: cfprintf(cfile, "Supported search strategies:\n\n"); @@ -2551,6 +2573,8 @@ list_strategies: cfprintf(cfile, " ${bld}dfs${rs}: depth-first search\n"); cfprintf(cfile, " ${bld}ids${rs}: iterative deepening search\n"); cfprintf(cfile, " ${bld}eds${rs}: exponential deepening search\n"); + + bfs_expr_free(expr); return NULL; } @@ -3456,7 +3480,7 @@ static struct bfs_expr *parse_factor(struct parser_state *state) { return NULL; } - parser_advance(state, T_OPERATOR, 1); + char **argv = parser_advance(state, T_OPERATOR, 1); state->excluding = true; struct bfs_expr *factor = parse_factor(state); @@ -3470,7 +3494,7 @@ static struct bfs_expr *parse_factor(struct parser_state *state) { return NULL; } - return &bfs_true; + return bfs_expr_new(eval_true, state->argv - argv, argv); } else if (strcmp(arg, "!") == 0 || strcmp(arg, "-not") == 0) { char **argv = parser_advance(state, T_OPERATOR, 1); @@ -3612,12 +3636,14 @@ static struct bfs_expr *parse_whole_expr(struct parser_state *state) { return NULL; } - struct bfs_expr *expr = &bfs_true; + struct bfs_expr *expr; if (state->argv[0]) { expr = parse_expr(state); - if (!expr) { - return NULL; - } + } else { + expr = bfs_expr_new(eval_true, 1, &fake_true_arg); + } + if (!expr) { + return NULL; } if (state->argv[0]) { @@ -3804,7 +3830,7 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { fputs("\n", stderr); - if (ctx->exclude != &bfs_false) { + if (ctx->exclude->eval_fn != eval_false) { bfs_debug(ctx, flag, "(${red}-exclude${rs}\n"); dump_expr_multiline(ctx, flag, ctx->exclude, 1, 1); } @@ -3910,7 +3936,11 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { ctx->strategy = BFTW_DFS; } - ctx->exclude = &bfs_false; + ctx->exclude = bfs_expr_new(eval_false, 1, &fake_false_arg); + if (!ctx->exclude) { + goto fail; + } + ctx->expr = parse_whole_expr(&state); if (!ctx->expr) { if (state.just_info) { -- cgit v1.2.3 From d1e532ed839c1b2be093c88006fcf4cd3d11805d Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 5 Dec 2022 14:18:53 -0500 Subject: expr: Rename bfs_expr_has_children() to _is_parent() --- src/color.c | 2 +- src/diag.c | 2 +- src/eval.c | 4 ++-- src/expr.h | 2 +- src/opt.c | 6 +++--- src/parse.c | 12 ++++++------ 6 files changed, 14 insertions(+), 14 deletions(-) (limited to 'src/opt.c') diff --git a/src/color.c b/src/color.c index 94fcac8..8d32c6c 100644 --- a/src/color.c +++ b/src/color.c @@ -888,7 +888,7 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { const struct bfs_expr *lhs = NULL; const struct bfs_expr *rhs = NULL; - if (bfs_expr_has_children(expr)) { + if (bfs_expr_is_parent(expr)) { lhs = expr->lhs; rhs = expr->rhs; diff --git a/src/diag.c b/src/diag.c index c5e139e..a0c11f2 100644 --- a/src/diag.c +++ b/src/diag.c @@ -125,7 +125,7 @@ static bool highlight_expr_recursive(const struct bfs_ctx *ctx, const struct bfs } } - if (bfs_expr_has_children(expr)) { + if (bfs_expr_is_parent(expr)) { ret |= highlight_expr_recursive(ctx, expr->lhs, args); ret |= highlight_expr_recursive(ctx, expr->rhs, args); } diff --git a/src/eval.c b/src/eval.c index 4c9d807..e3257ce 100644 --- a/src/eval.c +++ b/src/eval.c @@ -387,7 +387,7 @@ static int eval_exec_finish(const struct bfs_expr *expr, const struct bfs_ctx *c } ret = -1; } - } else if (bfs_expr_has_children(expr)) { + } else if (bfs_expr_is_parent(expr)) { if (expr->lhs && eval_exec_finish(expr->lhs, ctx) != 0) { ret = -1; } @@ -1557,7 +1557,7 @@ static bool eval_must_buffer(const struct bfs_expr *expr) { return true; } - if (bfs_expr_has_children(expr)) { + if (bfs_expr_is_parent(expr)) { if (expr->lhs && eval_must_buffer(expr->lhs)) { return true; } diff --git a/src/expr.h b/src/expr.h index e541d8e..a52007a 100644 --- a/src/expr.h +++ b/src/expr.h @@ -210,7 +210,7 @@ struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval, size_t argc, char **argv); /** * @return Whether the expression has child expressions. */ -bool bfs_expr_has_children(const struct bfs_expr *expr); +bool bfs_expr_is_parent(const struct bfs_expr *expr); /** * @return Whether expr is known to always quit. diff --git a/src/opt.c b/src/opt.c index 441c611..56d4102 100644 --- a/src/opt.c +++ b/src/opt.c @@ -847,7 +847,7 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct return ret; } - if (!bfs_expr_has_children(expr) && !expr->pure) { + if (!bfs_expr_is_parent(expr) && !expr->pure) { facts_union(state->facts_when_impure, state->facts_when_impure, &state->facts); } @@ -901,7 +901,7 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct return NULL; } - if (bfs_expr_has_children(expr)) { + if (bfs_expr_is_parent(expr)) { struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; if (rhs) { @@ -980,7 +980,7 @@ static bool reorder_expr(const struct opt_state *state, struct bfs_expr *expr, f * Whether any subexpression was reordered. */ static bool reorder_expr_recursive(const struct opt_state *state, struct bfs_expr *expr) { - if (!bfs_expr_has_children(expr)) { + if (!bfs_expr_is_parent(expr)) { return false; } diff --git a/src/parse.c b/src/parse.c index a858a4c..320e165 100644 --- a/src/parse.c +++ b/src/parse.c @@ -109,7 +109,7 @@ struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { } // Prevent bfs_expr_free() from freeing uninitialized pointers on error paths - if (bfs_expr_has_children(expr)) { + if (bfs_expr_is_parent(expr)) { expr->lhs = NULL; expr->rhs = NULL; } else if (eval_fn == eval_exec) { @@ -123,7 +123,7 @@ struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { return expr; } -bool bfs_expr_has_children(const struct bfs_expr *expr) { +bool bfs_expr_is_parent(const struct bfs_expr *expr) { return expr->eval_fn == eval_and || expr->eval_fn == eval_or || expr->eval_fn == eval_not @@ -140,7 +140,7 @@ void bfs_expr_free(struct bfs_expr *expr) { return; } - if (bfs_expr_has_children(expr)) { + if (bfs_expr_is_parent(expr)) { bfs_expr_free(expr->rhs); bfs_expr_free(expr->lhs); } else if (expr->eval_fn == eval_exec) { @@ -166,7 +166,7 @@ static struct bfs_expr *new_unary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *rh expr->lhs = NULL; expr->rhs = rhs; - assert(bfs_expr_has_children(expr)); + assert(bfs_expr_is_parent(expr)); expr->persistent_fds = rhs->persistent_fds; expr->ephemeral_fds = rhs->ephemeral_fds; @@ -186,7 +186,7 @@ static struct bfs_expr *new_binary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *l expr->lhs = lhs; expr->rhs = rhs; - assert(bfs_expr_has_children(expr)); + assert(bfs_expr_is_parent(expr)); expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; if (lhs->ephemeral_fds > rhs->ephemeral_fds) { @@ -3722,7 +3722,7 @@ static void dump_expr_multiline(const struct bfs_ctx *ctx, enum debug_flags flag cfprintf(ctx->cerr, " "); } - if (bfs_expr_has_children(expr)) { + if (bfs_expr_is_parent(expr)) { cfprintf(ctx->cerr, "(${red}%s${rs}\n", expr->argv[0]); if (expr->lhs) { dump_expr_multiline(ctx, flag, expr->lhs, indent + 1, 0); -- cgit v1.2.3 From ededa3ed1cf0d02fcdb9a42d44d46a84fec1c58e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 8 Dec 2022 16:43:29 -0500 Subject: opt: Pass a va_list to bfs_vwarning(), not bfs_warning() Luckily none of the callers actually pass any arguments. --- src/opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index 56d4102..3250331 100644 --- a/src/opt.c +++ b/src/opt.c @@ -339,7 +339,7 @@ static void opt_warning(const struct opt_state *state, const struct bfs_expr *ex if (bfs_expr_warning(state->ctx, expr)) { va_list args; va_start(args, format); - bfs_warning(state->ctx, format, args); + bfs_vwarning(state->ctx, format, args); va_end(args); } } -- cgit v1.2.3 From fcbd5d315ec71e1890a2d8a0d7e4107bfbb7bca6 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 24 Jan 2023 16:41:34 -0500 Subject: opt: Use a table to look up optimizer functions --- src/opt.c | 115 ++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 44 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index 3250331..6ec3414 100644 --- a/src/opt.c +++ b/src/opt.c @@ -746,19 +746,6 @@ static void infer_pred_facts(struct opt_state *state, enum pred_type pred) { constrain_pred(&state->facts_when_false.preds[pred], false); } -/** Infer data flow facts about an -{execut,read,writ}able expression. */ -static void infer_access_facts(struct opt_state *state, const struct bfs_expr *expr) { - if (expr->num & R_OK) { - infer_pred_facts(state, READABLE_PRED); - } - if (expr->num & W_OK) { - infer_pred_facts(state, WRITABLE_PRED); - } - if (expr->num & X_OK) { - infer_pred_facts(state, EXECUTABLE_PRED); - } -} - /** Infer data flow facts about an icmp-style ([+-]N) expression. */ static void infer_icmp_facts(struct opt_state *state, const struct bfs_expr *expr, enum range_type type) { struct range *range_when_true = &state->facts_when_true.ranges[type]; @@ -786,8 +773,22 @@ static void infer_icmp_facts(struct opt_state *state, const struct bfs_expr *exp } } -/** Infer data flow facts about a -gid expression. */ -static void infer_gid_facts(struct opt_state *state, const struct bfs_expr *expr) { +/** Optimize -{execut,read,writ}able. */ +static struct bfs_expr *optimize_access(struct opt_state *state, struct bfs_expr *expr) { + if (expr->num & R_OK) { + infer_pred_facts(state, READABLE_PRED); + } + if (expr->num & W_OK) { + infer_pred_facts(state, WRITABLE_PRED); + } + if (expr->num & X_OK) { + infer_pred_facts(state, EXECUTABLE_PRED); + } + return expr; +} + +/** Optimize -gid. */ +static struct bfs_expr *optimize_gid(struct opt_state *state, struct bfs_expr *expr) { infer_icmp_facts(state, expr, GID_RANGE); struct range *range = &state->facts_when_true.ranges[GID_RANGE]; @@ -798,10 +799,12 @@ static void infer_gid_facts(struct opt_state *state, const struct bfs_expr *expr constrain_pred(&state->facts_when_true.preds[NOGROUP_PRED], nogroup); } } + + return expr; } -/** Infer data flow facts about a -uid expression. */ -static void infer_uid_facts(struct opt_state *state, const struct bfs_expr *expr) { +/** Optimize -uid. */ +static struct bfs_expr *optimize_uid(struct opt_state *state, struct bfs_expr *expr) { infer_icmp_facts(state, expr, UID_RANGE); struct range *range = &state->facts_when_true.ranges[UID_RANGE]; @@ -812,25 +815,68 @@ static void infer_uid_facts(struct opt_state *state, const struct bfs_expr *expr constrain_pred(&state->facts_when_true.preds[NOUSER_PRED], nouser); } } + + return expr; } -/** Infer data flow facts about a -samefile expression. */ -static void infer_samefile_facts(struct opt_state *state, const struct bfs_expr *expr) { +/** Optimize -samefile. */ +static struct bfs_expr *optimize_samefile(struct opt_state *state, struct bfs_expr *expr) { struct range *range_when_true = &state->facts_when_true.ranges[INUM_RANGE]; constrain_min(range_when_true, expr->ino); constrain_max(range_when_true, expr->ino); + return expr; } -/** Infer data flow facts about a -type expression. */ -static void infer_type_facts(struct opt_state *state, const struct bfs_expr *expr) { +/** Optimize -type. */ +static struct bfs_expr *optimize_type(struct opt_state *state, struct bfs_expr *expr) { state->facts_when_true.types &= expr->num; state->facts_when_false.types &= ~expr->num; + return expr; } -/** Infer data flow facts about an -xtype expression. */ -static void infer_xtype_facts(struct opt_state *state, const struct bfs_expr *expr) { +/** Optimize -xtype. */ +static struct bfs_expr *optimize_xtype(struct opt_state *state, struct bfs_expr *expr) { state->facts_when_true.xtypes &= expr->num; state->facts_when_false.xtypes &= ~expr->num; + return expr; +} + +/** Signature for custom optimizer functions. */ +typedef struct bfs_expr *bfs_opt_fn(struct opt_state *state, struct bfs_expr *expr); + +/** Table of custom optimizer functions. */ +static const struct { + /** The evaluation function this optimizer applies to. */ + bfs_eval_fn *eval_fn; + /** The corresponding optimizer function. */ + bfs_opt_fn *opt_fn; +} opt_fns[] = { + // Primaries + {eval_access, optimize_access}, + {eval_gid, optimize_gid}, + {eval_samefile, optimize_samefile}, + {eval_type, optimize_type}, + {eval_uid, optimize_uid}, + {eval_xtype, optimize_xtype}, + + // Operators + {eval_and, optimize_and_expr_recursive}, + {eval_comma, optimize_comma_expr_recursive}, + {eval_not, optimize_not_expr_recursive}, + {eval_or, optimize_or_expr_recursive}, +}; + +/** + * Look up the appropriate optimizer for an expression and call it. + */ +static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs_expr *expr) { + for (size_t i = 0; i < BFS_COUNTOF(opt_fns); ++i) { + if (opt_fns[i].eval_fn == expr->eval_fn) { + return opt_fns[i].opt_fn(state, expr); + } + } + + return expr; } static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct bfs_expr *expr) { @@ -851,9 +897,7 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct facts_union(state->facts_when_impure, state->facts_when_impure, &state->facts); } - if (expr->eval_fn == eval_access) { - infer_access_facts(state, expr); - } else if (expr->eval_fn == eval_acl) { + if (expr->eval_fn == eval_acl) { infer_pred_facts(state, ACL_PRED); } else if (expr->eval_fn == eval_capable) { infer_pred_facts(state, CAPABLE_PRED); @@ -861,8 +905,6 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct infer_icmp_facts(state, expr, DEPTH_RANGE); } else if (expr->eval_fn == eval_empty) { infer_pred_facts(state, EMPTY_PRED); - } else if (expr->eval_fn == eval_gid) { - infer_gid_facts(state, expr); } else if (expr->eval_fn == eval_hidden) { infer_pred_facts(state, HIDDEN_PRED); } else if (expr->eval_fn == eval_inum) { @@ -873,30 +915,15 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct infer_pred_facts(state, NOGROUP_PRED); } else if (expr->eval_fn == eval_nouser) { infer_pred_facts(state, NOUSER_PRED); - } else if (expr->eval_fn == eval_samefile) { - infer_samefile_facts(state, expr); } else if (expr->eval_fn == eval_size) { infer_icmp_facts(state, expr, SIZE_RANGE); } else if (expr->eval_fn == eval_sparse) { infer_pred_facts(state, SPARSE_PRED); - } else if (expr->eval_fn == eval_type) { - infer_type_facts(state, expr); - } else if (expr->eval_fn == eval_uid) { - infer_uid_facts(state, expr); } else if (expr->eval_fn == eval_xattr) { infer_pred_facts(state, XATTR_PRED); - } else if (expr->eval_fn == eval_xtype) { - infer_xtype_facts(state, expr); - } else if (expr->eval_fn == eval_not) { - expr = optimize_not_expr_recursive(state, expr); - } else if (expr->eval_fn == eval_and) { - expr = optimize_and_expr_recursive(state, expr); - } else if (expr->eval_fn == eval_or) { - expr = optimize_or_expr_recursive(state, expr); - } else if (expr->eval_fn == eval_comma) { - expr = optimize_comma_expr_recursive(state, expr); } + expr = optimize_expr_lookup(state, expr); if (!expr) { return NULL; } -- cgit v1.2.3 From 0c0dbcbcdf04e55c2805eb71aecd968ea2ffe1a0 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 24 Jan 2023 16:47:28 -0500 Subject: opt: Use a table for simple predicates --- src/opt.c | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index 6ec3414..a33ffea 100644 --- a/src/opt.c +++ b/src/opt.c @@ -841,6 +841,25 @@ static struct bfs_expr *optimize_xtype(struct opt_state *state, struct bfs_expr return expr; } +/** + * Table of simple predicates. + */ +static const struct { + /** The evaluation function this optimizer applies to. */ + bfs_eval_fn *eval_fn; + /** The corresponding predicate. */ + enum pred_type pred; +} opt_preds[] = { + {eval_acl, ACL_PRED}, + {eval_capable, CAPABLE_PRED}, + {eval_empty, EMPTY_PRED}, + {eval_hidden, HIDDEN_PRED}, + {eval_nogroup, NOGROUP_PRED}, + {eval_nouser, NOUSER_PRED}, + {eval_sparse, SPARSE_PRED}, + {eval_xattr, XATTR_PRED}, +}; + /** Signature for custom optimizer functions. */ typedef struct bfs_expr *bfs_opt_fn(struct opt_state *state, struct bfs_expr *expr); @@ -870,6 +889,13 @@ static const struct { * Look up the appropriate optimizer for an expression and call it. */ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs_expr *expr) { + for (size_t i = 0; i < BFS_COUNTOF(opt_preds); ++i) { + if (opt_preds[i].eval_fn == expr->eval_fn) { + infer_pred_facts(state, opt_preds[i].pred); + break; + } + } + for (size_t i = 0; i < BFS_COUNTOF(opt_fns); ++i) { if (opt_fns[i].eval_fn == expr->eval_fn) { return opt_fns[i].opt_fn(state, expr); @@ -897,30 +923,14 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct facts_union(state->facts_when_impure, state->facts_when_impure, &state->facts); } - if (expr->eval_fn == eval_acl) { - infer_pred_facts(state, ACL_PRED); - } else if (expr->eval_fn == eval_capable) { - infer_pred_facts(state, CAPABLE_PRED); - } else if (expr->eval_fn == eval_depth) { + if (expr->eval_fn == eval_depth) { infer_icmp_facts(state, expr, DEPTH_RANGE); - } else if (expr->eval_fn == eval_empty) { - infer_pred_facts(state, EMPTY_PRED); - } else if (expr->eval_fn == eval_hidden) { - infer_pred_facts(state, HIDDEN_PRED); } else if (expr->eval_fn == eval_inum) { infer_icmp_facts(state, expr, INUM_RANGE); } else if (expr->eval_fn == eval_links) { infer_icmp_facts(state, expr, LINKS_RANGE); - } else if (expr->eval_fn == eval_nogroup) { - infer_pred_facts(state, NOGROUP_PRED); - } else if (expr->eval_fn == eval_nouser) { - infer_pred_facts(state, NOUSER_PRED); } else if (expr->eval_fn == eval_size) { infer_icmp_facts(state, expr, SIZE_RANGE); - } else if (expr->eval_fn == eval_sparse) { - infer_pred_facts(state, SPARSE_PRED); - } else if (expr->eval_fn == eval_xattr) { - infer_pred_facts(state, XATTR_PRED); } expr = optimize_expr_lookup(state, expr); -- cgit v1.2.3 From b922add3a5597020a0ad5357b5fc390b3fae709f Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 24 Jan 2023 16:51:33 -0500 Subject: opt: Use a table for simple range comparisons --- src/opt.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index a33ffea..50fcd20 100644 --- a/src/opt.c +++ b/src/opt.c @@ -789,8 +789,6 @@ static struct bfs_expr *optimize_access(struct opt_state *state, struct bfs_expr /** Optimize -gid. */ static struct bfs_expr *optimize_gid(struct opt_state *state, struct bfs_expr *expr) { - infer_icmp_facts(state, expr, GID_RANGE); - struct range *range = &state->facts_when_true.ranges[GID_RANGE]; if (range->min == range->max) { gid_t gid = range->min; @@ -805,8 +803,6 @@ static struct bfs_expr *optimize_gid(struct opt_state *state, struct bfs_expr *e /** Optimize -uid. */ static struct bfs_expr *optimize_uid(struct opt_state *state, struct bfs_expr *expr) { - infer_icmp_facts(state, expr, UID_RANGE); - struct range *range = &state->facts_when_true.ranges[UID_RANGE]; if (range->min == range->max) { uid_t uid = range->min; @@ -860,6 +856,23 @@ static const struct { {eval_xattr, XATTR_PRED}, }; +/** + * Table of simple range comparisons. + */ +static const struct { + /** The evaluation function this optimizer applies to. */ + bfs_eval_fn *eval_fn; + /** The corresponding range. */ + enum range_type range; +} opt_ranges[] = { + {eval_depth, DEPTH_RANGE}, + {eval_gid, GID_RANGE}, + {eval_inum, INUM_RANGE}, + {eval_links, LINKS_RANGE}, + {eval_size, SIZE_RANGE}, + {eval_uid, UID_RANGE}, +}; + /** Signature for custom optimizer functions. */ typedef struct bfs_expr *bfs_opt_fn(struct opt_state *state, struct bfs_expr *expr); @@ -896,6 +909,13 @@ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs } } + for (size_t i = 0; i < BFS_COUNTOF(opt_ranges); ++i) { + if (opt_ranges[i].eval_fn == expr->eval_fn) { + infer_icmp_facts(state, expr, opt_ranges[i].range); + break; + } + } + for (size_t i = 0; i < BFS_COUNTOF(opt_fns); ++i) { if (opt_fns[i].eval_fn == expr->eval_fn) { return opt_fns[i].opt_fn(state, expr); @@ -923,16 +943,6 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct facts_union(state->facts_when_impure, state->facts_when_impure, &state->facts); } - if (expr->eval_fn == eval_depth) { - infer_icmp_facts(state, expr, DEPTH_RANGE); - } else if (expr->eval_fn == eval_inum) { - infer_icmp_facts(state, expr, INUM_RANGE); - } else if (expr->eval_fn == eval_links) { - infer_icmp_facts(state, expr, LINKS_RANGE); - } else if (expr->eval_fn == eval_size) { - infer_icmp_facts(state, expr, SIZE_RANGE); - } - expr = optimize_expr_lookup(state, expr); if (!expr) { return NULL; -- cgit v1.2.3 From 693b5f60dc9787d9237920cc0c87fe0e010194ee Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 24 Jan 2023 16:59:49 -0500 Subject: opt: Move purity out of the parser --- src/opt.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/parse.c | 27 ++------------------------- 2 files changed, 64 insertions(+), 25 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index 50fcd20..934a4b4 100644 --- a/src/opt.c +++ b/src/opt.c @@ -787,6 +787,18 @@ static struct bfs_expr *optimize_access(struct opt_state *state, struct bfs_expr return expr; } +/** Optimize -empty. */ +static struct bfs_expr *optimize_empty(struct opt_state *state, struct bfs_expr *expr) { + 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 + expr->pure = true; + } + + return expr; +} + /** Optimize -gid. */ static struct bfs_expr *optimize_gid(struct opt_state *state, struct bfs_expr *expr) { struct range *range = &state->facts_when_true.ranges[GID_RANGE]; @@ -832,11 +844,53 @@ static struct bfs_expr *optimize_type(struct opt_state *state, struct bfs_expr * /** Optimize -xtype. */ static struct bfs_expr *optimize_xtype(struct opt_state *state, struct bfs_expr *expr) { + if (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 + expr->pure = true; + } + state->facts_when_true.xtypes &= expr->num; state->facts_when_false.xtypes &= ~expr->num; return expr; } +/** + * Table of pure expressions. + */ +static bfs_eval_fn *const opt_pure[] = { + eval_access, + eval_acl, + eval_capable, + eval_depth, + eval_false, + eval_flags, + eval_fstype, + eval_gid, + eval_hidden, + eval_inum, + eval_links, + eval_lname, + eval_name, + eval_newer, + eval_nogroup, + eval_nouser, + eval_path, + eval_perm, + eval_regex, + eval_samefile, + eval_size, + eval_sparse, + eval_time, + eval_true, + eval_type, + eval_uid, + eval_used, + eval_xattr, + eval_xattrname, +}; + /** * Table of simple predicates. */ @@ -885,6 +939,7 @@ static const struct { } opt_fns[] = { // Primaries {eval_access, optimize_access}, + {eval_empty, optimize_empty}, {eval_gid, optimize_gid}, {eval_samefile, optimize_samefile}, {eval_type, optimize_type}, @@ -902,6 +957,13 @@ static const struct { * Look up the appropriate optimizer for an expression and call it. */ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs_expr *expr) { + for (size_t i = 0; i < BFS_COUNTOF(opt_pure); ++i) { + if (opt_pure[i] == expr->eval_fn) { + expr->pure = true; + break; + } + } + for (size_t i = 0; i < BFS_COUNTOF(opt_preds); ++i) { if (opt_preds[i].eval_fn == expr->eval_fn) { infer_pred_facts(state, opt_preds[i].pred); diff --git a/src/parse.c b/src/parse.c index 51a9d0a..0b4224c 100644 --- a/src/parse.c +++ b/src/parse.c @@ -85,6 +85,7 @@ struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { expr->argv = argv; expr->persistent_fds = 0; expr->ephemeral_fds = 0; + expr->pure = false; expr->cost = FAST_COST; expr->evaluations = 0; expr->successes = 0; @@ -92,17 +93,14 @@ struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { expr->elapsed.tv_nsec = 0; if (eval_fn == eval_true) { - expr->pure = true; expr->always_true = true; expr->always_false = false; expr->probability = 1.0; } else if (eval_fn == eval_false) { - expr->pure = true; expr->always_true = false; expr->always_false = true; expr->probability = 0.0; } else { - expr->pure = false; expr->always_true = false; expr->always_false = false; expr->probability = 0.5; @@ -814,11 +812,7 @@ static struct bfs_expr *parse_unary_option(struct parser_state *state) { */ static struct bfs_expr *parse_test(struct parser_state *state, bfs_eval_fn *eval_fn, size_t argc) { char **argv = parser_advance(state, T_TEST, argc); - struct bfs_expr *expr = bfs_expr_new(eval_fn, argc, argv); - if (expr) { - expr->pure = true; - } - return expr; + return bfs_expr_new(eval_fn, argc, argv); } /** @@ -1344,16 +1338,7 @@ static struct bfs_expr *parse_empty(struct parser_state *state, int arg1, int ar expr->cost = 2000.0; expr->probability = 0.01; - - 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 - expr->pure = false; - } - expr->ephemeral_fds = 1; - return expr; } @@ -2030,7 +2015,6 @@ static struct bfs_expr *parse_nohidden(struct parser_state *state, int arg1, int } hidden->probability = 0.01; - hidden->pure = true; if (parse_exclude(state, hidden) != 0) { return NULL; @@ -2766,13 +2750,6 @@ static struct bfs_expr *parse_type(struct parser_state *state, int x, int arg2) expr->num = types; expr->probability = probability; - 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 - expr->pure = false; - } - return expr; fail: -- cgit v1.2.3 From 60b51a3bda3f3fc220a2fa56de5f86c4bac35f2e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 24 Jan 2023 17:08:58 -0500 Subject: opt: Move always_{true,false} out of the parser --- src/opt.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/parse.c | 56 ++++++-------------------------------------------------- 2 files changed, 61 insertions(+), 50 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index 934a4b4..c80d584 100644 --- a/src/opt.c +++ b/src/opt.c @@ -45,6 +45,7 @@ #include "ctx.h" #include "diag.h" #include "eval.h" +#include "exec.h" #include "expr.h" #include "pwcache.h" #include @@ -799,6 +800,15 @@ static struct bfs_expr *optimize_empty(struct opt_state *state, struct bfs_expr return expr; } +/** Optimize -{exec,ok}{,dir}. */ +static struct bfs_expr *optimize_exec(struct opt_state *state, struct bfs_expr *expr) { + if (expr->exec->flags & BFS_EXEC_MULTI) { + expr->always_true = true; + } + + return expr; +} + /** Optimize -gid. */ static struct bfs_expr *optimize_gid(struct opt_state *state, struct bfs_expr *expr) { struct range *range = &state->facts_when_true.ranges[GID_RANGE]; @@ -891,6 +901,34 @@ static bfs_eval_fn *const opt_pure[] = { eval_xattrname, }; +/** + * Table of always-true expressions. + */ +static bfs_eval_fn *const opt_always_true[] = { + eval_fls, + eval_fprint, + eval_fprint0, + eval_fprintf, + eval_fprintx, + eval_prune, + eval_true, + + // Non-returning + eval_exit, + eval_quit, +}; + +/** + * Table of always-false expressions. + */ +static bfs_eval_fn *const opt_always_false[] = { + eval_false, + + // Non-returning + eval_exit, + eval_quit, +}; + /** * Table of simple predicates. */ @@ -940,6 +978,7 @@ static const struct { // Primaries {eval_access, optimize_access}, {eval_empty, optimize_empty}, + {eval_exec, optimize_exec}, {eval_gid, optimize_gid}, {eval_samefile, optimize_samefile}, {eval_type, optimize_type}, @@ -964,6 +1003,20 @@ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs } } + for (size_t i = 0; i < BFS_COUNTOF(opt_always_true); ++i) { + if (opt_always_true[i] == expr->eval_fn) { + expr->always_true = true; + break; + } + } + + for (size_t i = 0; i < BFS_COUNTOF(opt_always_false); ++i) { + if (opt_always_false[i] == expr->eval_fn) { + expr->always_false = true; + break; + } + } + for (size_t i = 0; i < BFS_COUNTOF(opt_preds); ++i) { if (opt_preds[i].eval_fn == expr->eval_fn) { infer_pred_facts(state, opt_preds[i].pred); @@ -1026,9 +1079,11 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct } if (expr->always_true) { + expr->probability = 1.0; set_facts_impossible(&state->facts_when_false); } if (expr->always_false) { + expr->probability = 0.0; set_facts_impossible(&state->facts_when_true); } diff --git a/src/parse.c b/src/parse.c index 0b4224c..5766237 100644 --- a/src/parse.c +++ b/src/parse.c @@ -86,26 +86,15 @@ struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { expr->persistent_fds = 0; expr->ephemeral_fds = 0; expr->pure = false; + expr->always_true = false; + expr->always_false = false; expr->cost = FAST_COST; + expr->probability = 0.5; expr->evaluations = 0; expr->successes = 0; expr->elapsed.tv_sec = 0; expr->elapsed.tv_nsec = 0; - if (eval_fn == eval_true) { - expr->always_true = true; - expr->always_false = false; - expr->probability = 1.0; - } else if (eval_fn == eval_false) { - expr->always_true = false; - expr->always_false = true; - expr->probability = 0.0; - } else { - expr->always_true = false; - expr->always_false = false; - expr->probability = 0.5; - } - // Prevent bfs_expr_free() from freeing uninitialized pointers on error paths if (bfs_expr_is_parent(expr)) { expr->lhs = NULL; @@ -196,21 +185,6 @@ static struct bfs_expr *new_binary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *l return expr; } -/** - * Set an expression to always return true. - */ -static void expr_set_always_true(struct bfs_expr *expr) { - expr->always_true = true; - expr->probability = 1.0; -} - -/** - * Set an expression to never return. - */ -static void expr_set_never_returns(struct bfs_expr *expr) { - expr->always_true = expr->always_false = true; -} - /** * Color use flags. */ @@ -462,7 +436,6 @@ static bool parse_expr_warning(const struct parser_state *state, const struct bf * Fill in a "-print"-type expression. */ static void init_print_expr(struct parser_state *state, struct bfs_expr *expr) { - expr_set_always_true(expr); expr->cost = PRINT_COST; expr->cfile = state->ctx->cout; expr->path = NULL; @@ -1359,9 +1332,7 @@ static struct bfs_expr *parse_exec(struct parser_state *state, int flags, int ar expr->exec = execbuf; - if (execbuf->flags & BFS_EXEC_MULTI) { - expr_set_always_true(expr); - } else { + if (!(execbuf->flags & BFS_EXEC_MULTI)) { expr->cost = 1000000.0; } @@ -1395,7 +1366,6 @@ static struct bfs_expr *parse_exit(struct parser_state *state, int arg1, int arg struct bfs_expr *expr = parse_action(state, eval_exit, argc); if (expr) { - expr_set_never_returns(expr); expr->num = status; } return expr; @@ -1525,7 +1495,6 @@ static struct bfs_expr *parse_fls(struct parser_state *state, int arg1, int arg2 goto fail; } - expr_set_always_true(expr); expr->cost = PRINT_COST; return expr; @@ -1540,7 +1509,6 @@ fail: static struct bfs_expr *parse_fprint(struct parser_state *state, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(state, eval_fprint); if (expr) { - expr_set_always_true(expr); expr->cost = PRINT_COST; if (expr_open(state, expr, expr->argv[1]) != 0) { goto fail; @@ -1559,7 +1527,6 @@ fail: static struct bfs_expr *parse_fprint0(struct parser_state *state, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(state, eval_fprint0); if (expr) { - expr_set_always_true(expr); expr->cost = PRINT_COST; if (expr_open(state, expr, expr->argv[1]) != 0) { goto fail; @@ -1595,8 +1562,6 @@ static struct bfs_expr *parse_fprintf(struct parser_state *state, int arg1, int return NULL; } - expr_set_always_true(expr); - expr->cost = PRINT_COST; if (expr_open(state, expr, file) != 0) { @@ -2373,23 +2338,14 @@ static struct bfs_expr *parse_printx(struct parser_state *state, int arg1, int a */ static struct bfs_expr *parse_prune(struct parser_state *state, int arg1, int arg2) { state->prune_arg = state->argv; - - struct bfs_expr *expr = parse_nullary_action(state, eval_prune); - if (expr) { - expr_set_always_true(expr); - } - return expr; + return parse_nullary_action(state, eval_prune); } /** * Parse -quit. */ static struct bfs_expr *parse_quit(struct parser_state *state, int arg1, int arg2) { - struct bfs_expr *expr = parse_nullary_action(state, eval_quit); - if (expr) { - expr_set_never_returns(expr); - } - return expr; + return parse_nullary_action(state, eval_quit); } /** -- cgit v1.2.3 From cb3805257092cea2fa6d1fce8d2f99f6b01f44ed Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 24 Jan 2023 17:17:06 -0500 Subject: opt: Move costs out of the parser --- src/opt.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/parse.c | 54 +++--------------------------------------------------- 2 files changed, 57 insertions(+), 51 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index c80d584..1daf390 100644 --- a/src/opt.c +++ b/src/opt.c @@ -804,6 +804,8 @@ static struct bfs_expr *optimize_empty(struct opt_state *state, struct bfs_expr static struct bfs_expr *optimize_exec(struct opt_state *state, struct bfs_expr *expr) { if (expr->exec->flags & BFS_EXEC_MULTI) { expr->always_true = true; + } else { + expr->cost = 1000000.0; } return expr; @@ -929,6 +931,50 @@ static bfs_eval_fn *const opt_always_false[] = { eval_quit, }; +#define FAST_COST 40.0 +#define FNMATCH_COST 400.0 +#define STAT_COST 1000.0 +#define PRINT_COST 20000.0 + +/** + * Table of expression costs. + */ +static const struct { + /** The evaluation function with this cost. */ + bfs_eval_fn *eval_fn; + /** The matching cost. */ + float cost; +} opt_costs[] = { + {eval_access, STAT_COST}, + {eval_acl, STAT_COST}, + {eval_capable, STAT_COST}, + {eval_empty, 2 * STAT_COST}, // readdir() is worse than stat() + {eval_fls, PRINT_COST}, + {eval_fprint, PRINT_COST}, + {eval_fprint0, PRINT_COST}, + {eval_fprintf, PRINT_COST}, + {eval_fprintx, PRINT_COST}, + {eval_fstype, STAT_COST}, + {eval_gid, STAT_COST}, + {eval_inum, STAT_COST}, + {eval_links, STAT_COST}, + {eval_lname, FNMATCH_COST}, + {eval_name, FNMATCH_COST}, + {eval_newer, STAT_COST}, + {eval_nogroup, STAT_COST}, + {eval_nouser, STAT_COST}, + {eval_path, FNMATCH_COST}, + {eval_perm, STAT_COST}, + {eval_samefile, STAT_COST}, + {eval_size, STAT_COST}, + {eval_sparse, STAT_COST}, + {eval_time, STAT_COST}, + {eval_uid, STAT_COST}, + {eval_used, STAT_COST}, + {eval_xattr, STAT_COST}, + {eval_xattrname, STAT_COST}, +}; + /** * Table of simple predicates. */ @@ -1017,6 +1063,14 @@ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs } } + expr->cost = FAST_COST; + for (size_t i = 0; i < BFS_COUNTOF(opt_costs); ++i) { + if (opt_costs[i].eval_fn == expr->eval_fn) { + expr->cost = opt_costs[i].cost; + break; + } + } + for (size_t i = 0; i < BFS_COUNTOF(opt_preds); ++i) { if (opt_preds[i].eval_fn == expr->eval_fn) { infer_pred_facts(state, opt_preds[i].pred); diff --git a/src/parse.c b/src/parse.c index 5766237..eee185b 100644 --- a/src/parse.c +++ b/src/parse.c @@ -68,11 +68,6 @@ static char *fake_or_arg = "-o"; static char *fake_print_arg = "-print"; static char *fake_true_arg = "-true"; -// Cost estimation constants -#define FAST_COST 40.0 -#define STAT_COST 1000.0 -#define PRINT_COST 20000.0 - struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { struct bfs_expr *expr = malloc(sizeof(*expr)); if (!expr) { @@ -88,7 +83,7 @@ struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { expr->pure = false; expr->always_true = false; expr->always_false = false; - expr->cost = FAST_COST; + expr->cost = 0.0; expr->probability = 0.5; expr->evaluations = 0; expr->successes = 0; @@ -436,7 +431,6 @@ static bool parse_expr_warning(const struct parser_state *state, const struct bf * Fill in a "-print"-type expression. */ static void init_print_expr(struct parser_state *state, struct bfs_expr *expr) { - expr->cost = PRINT_COST; expr->cfile = state->ctx->cout; expr->path = NULL; } @@ -1021,7 +1015,6 @@ static struct bfs_expr *parse_access(struct parser_state *state, int flag, int a } expr->num = flag; - expr->cost = STAT_COST; switch (flag) { case R_OK: @@ -1045,7 +1038,6 @@ static struct bfs_expr *parse_acl(struct parser_state *state, int flag, int arg2 #if BFS_CAN_CHECK_ACL struct bfs_expr *expr = parse_nullary_test(state, eval_acl); if (expr) { - expr->cost = STAT_COST; expr->probability = 0.00002; } return expr; @@ -1069,7 +1061,6 @@ static struct bfs_expr *parse_newer(struct parser_state *state, int field, int a goto fail; } - expr->cost = STAT_COST; expr->reftime = sb.mtime; expr->stat_field = field; return expr; @@ -1088,7 +1079,6 @@ static struct bfs_expr *parse_min(struct parser_state *state, int field, int arg return NULL; } - expr->cost = STAT_COST; expr->reftime = state->now; expr->stat_field = field; expr->time_unit = BFS_MINUTES; @@ -1104,7 +1094,6 @@ static struct bfs_expr *parse_time(struct parser_state *state, int field, int ar return NULL; } - expr->cost = STAT_COST; expr->reftime = state->now; expr->stat_field = field; @@ -1173,7 +1162,6 @@ static struct bfs_expr *parse_capable(struct parser_state *state, int flag, int #if BFS_CAN_CHECK_CAPABILITIES struct bfs_expr *expr = parse_nullary_test(state, eval_capable); if (expr) { - expr->cost = STAT_COST; expr->probability = 0.000002; } return expr; @@ -1309,7 +1297,6 @@ static struct bfs_expr *parse_empty(struct parser_state *state, int arg1, int ar return NULL; } - expr->cost = 2000.0; expr->probability = 0.01; expr->ephemeral_fds = 1; return expr; @@ -1332,10 +1319,6 @@ static struct bfs_expr *parse_exec(struct parser_state *state, int flags, int ar expr->exec = execbuf; - if (!(execbuf->flags & BFS_EXEC_MULTI)) { - expr->cost = 1000000.0; - } - expr->ephemeral_fds = 2; if (execbuf->flags & BFS_EXEC_CHDIR) { if (execbuf->flags & BFS_EXEC_MULTI) { @@ -1495,7 +1478,6 @@ static struct bfs_expr *parse_fls(struct parser_state *state, int arg1, int arg2 goto fail; } - expr->cost = PRINT_COST; return expr; fail: @@ -1509,7 +1491,6 @@ fail: static struct bfs_expr *parse_fprint(struct parser_state *state, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(state, eval_fprint); if (expr) { - expr->cost = PRINT_COST; if (expr_open(state, expr, expr->argv[1]) != 0) { goto fail; } @@ -1527,7 +1508,6 @@ fail: static struct bfs_expr *parse_fprint0(struct parser_state *state, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(state, eval_fprint0); if (expr) { - expr->cost = PRINT_COST; if (expr_open(state, expr, expr->argv[1]) != 0) { goto fail; } @@ -1562,8 +1542,6 @@ static struct bfs_expr *parse_fprintf(struct parser_state *state, int arg1, int return NULL; } - expr->cost = PRINT_COST; - if (expr_open(state, expr, file) != 0) { goto fail; } @@ -1594,7 +1572,6 @@ static struct bfs_expr *parse_fstype(struct parser_state *state, int arg1, int a return NULL; } - expr->cost = STAT_COST; return expr; } @@ -1623,7 +1600,6 @@ static struct bfs_expr *parse_group(struct parser_state *state, int arg1, int ar goto fail; } - expr->cost = STAT_COST; return expr; fail: @@ -1643,11 +1619,7 @@ static struct bfs_expr *parse_unique(struct parser_state *state, int arg1, int a * Parse -used N. */ static struct bfs_expr *parse_used(struct parser_state *state, int arg1, int arg2) { - struct bfs_expr *expr = parse_test_icmp(state, eval_used); - if (expr) { - expr->cost = STAT_COST; - } - return expr; + return parse_test_icmp(state, eval_used); } /** @@ -1675,7 +1647,6 @@ static struct bfs_expr *parse_user(struct parser_state *state, int arg1, int arg goto fail; } - expr->cost = STAT_COST; return expr; fail: @@ -1708,7 +1679,6 @@ static struct bfs_expr *parse_ignore_races(struct parser_state *state, int ignor static struct bfs_expr *parse_inum(struct parser_state *state, int arg1, int arg2) { struct bfs_expr *expr = parse_test_icmp(state, eval_inum); if (expr) { - expr->cost = STAT_COST; expr->probability = expr->int_cmp == BFS_INT_EQUAL ? 0.01 : 0.50; } return expr; @@ -1720,7 +1690,6 @@ static struct bfs_expr *parse_inum(struct parser_state *state, int arg1, int arg static struct bfs_expr *parse_links(struct parser_state *state, int arg1, int arg2) { struct bfs_expr *expr = parse_test_icmp(state, eval_links); if (expr) { - expr->cost = STAT_COST; expr->probability = bfs_expr_cmp(expr, 1) ? 0.99 : 0.01; } return expr; @@ -1795,8 +1764,6 @@ static struct bfs_expr *parse_fnmatch(const struct parser_state *state, struct b return expr; } - expr->cost = 400.0; - if (strchr(pattern, '*')) { expr->probability = 0.5; } else { @@ -1942,8 +1909,6 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int expr->reftime = *reftime; } - expr->cost = STAT_COST; - return expr; fail: @@ -1960,7 +1925,6 @@ static struct bfs_expr *parse_nogroup(struct parser_state *state, int arg1, int return NULL; } - expr->cost = STAT_COST; expr->probability = 0.01; // Who knows how many FDs getgrgid_r() needs? Probably at least one for @@ -2006,7 +1970,6 @@ static struct bfs_expr *parse_nouser(struct parser_state *state, int arg1, int a return NULL; } - expr->cost = STAT_COST; expr->probability = 0.01; // Who knows how many FDs getpwuid_r() needs? Probably at least one for @@ -2272,8 +2235,6 @@ static struct bfs_expr *parse_perm(struct parser_state *state, int field, int ar goto fail; } - expr->cost = STAT_COST; - return expr; fail: @@ -2468,7 +2429,6 @@ static struct bfs_expr *parse_samefile(struct parser_state *state, int arg1, int expr->dev = sb.dev; expr->ino = sb.ino; - expr->cost = STAT_COST; expr->probability = 0.01; return expr; @@ -2531,7 +2491,6 @@ static struct bfs_expr *parse_since(struct parser_state *state, int field, int a goto fail; } - expr->cost = STAT_COST; expr->stat_field = field; return expr; @@ -2589,7 +2548,6 @@ static struct bfs_expr *parse_size(struct parser_state *state, int arg1, int arg goto bad_unit; } - expr->cost = STAT_COST; expr->probability = expr->int_cmp == BFS_INT_EQUAL ? 0.01 : 0.50; return expr; @@ -2605,11 +2563,7 @@ fail: * Parse -sparse. */ static struct bfs_expr *parse_sparse(struct parser_state *state, int arg1, int arg2) { - struct bfs_expr *expr = parse_nullary_test(state, eval_sparse); - if (expr) { - expr->cost = STAT_COST; - } - return expr; + return parse_nullary_test(state, eval_sparse); } /** @@ -2728,7 +2682,6 @@ static struct bfs_expr *parse_xattr(struct parser_state *state, int arg1, int ar #if BFS_CAN_CHECK_XATTRS struct bfs_expr *expr = parse_nullary_test(state, eval_xattr); if (expr) { - expr->cost = STAT_COST; expr->probability = 0.01; } return expr; @@ -2745,7 +2698,6 @@ static struct bfs_expr *parse_xattrname(struct parser_state *state, int arg1, in #if BFS_CAN_CHECK_XATTRS struct bfs_expr *expr = parse_unary_test(state, eval_xattrname); if (expr) { - expr->cost = STAT_COST; expr->probability = 0.01; } return expr; -- cgit v1.2.3 From 220fb782da63fd648dedb02a7c43c837792b3d4a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 24 Jan 2023 17:23:18 -0500 Subject: opt: Move probabilities out of the parser --- src/opt.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/parse.c | 149 ++++++++++++------------------------------------------------ 2 files changed, 161 insertions(+), 120 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index 1daf390..e76e216 100644 --- a/src/opt.c +++ b/src/opt.c @@ -54,6 +54,7 @@ #include #include #include +#include #include static char *fake_and_arg = "-a"; @@ -776,15 +777,23 @@ static void infer_icmp_facts(struct opt_state *state, const struct bfs_expr *exp /** Optimize -{execut,read,writ}able. */ static struct bfs_expr *optimize_access(struct opt_state *state, struct bfs_expr *expr) { + expr->probability = 1.0; + if (expr->num & R_OK) { infer_pred_facts(state, READABLE_PRED); + expr->probability *= 0.99; } + if (expr->num & W_OK) { infer_pred_facts(state, WRITABLE_PRED); + expr->probability *= 0.8; } + if (expr->num & X_OK) { infer_pred_facts(state, EXECUTABLE_PRED); + expr->probability *= 0.2; } + return expr; } @@ -811,6 +820,17 @@ static struct bfs_expr *optimize_exec(struct opt_state *state, struct bfs_expr * return expr; } +/** Optimize -name/-lname/-path. */ +static struct bfs_expr *optimize_fnmatch(struct opt_state *state, struct bfs_expr *expr) { + if (strchr(expr->argv[1], '*')) { + expr->probability = 0.5; + } else { + expr->probability = 0.1; + } + + return expr; +} + /** Optimize -gid. */ static struct bfs_expr *optimize_gid(struct opt_state *state, struct bfs_expr *expr) { struct range *range = &state->facts_when_true.ranges[GID_RANGE]; @@ -825,6 +845,30 @@ static struct bfs_expr *optimize_gid(struct opt_state *state, struct bfs_expr *e return expr; } +/** Optimize -inum. */ +static struct bfs_expr *optimize_inum(struct opt_state *state, struct bfs_expr *expr) { + struct range *range = &state->facts_when_true.ranges[INUM_RANGE]; + if (range->min == range->max) { + expr->probability = 0.01; + } else { + expr->probability = 0.5; + } + + return expr; +} + +/** Optimize -links. */ +static struct bfs_expr *optimize_links(struct opt_state *state, struct bfs_expr *expr) { + struct range *range = &state->facts_when_true.ranges[LINKS_RANGE]; + if (1 >= range->min && 1 <= range->max) { + expr->probability = 0.99; + } else { + expr->probability = 0.5; + } + + return expr; +} + /** Optimize -uid. */ static struct bfs_expr *optimize_uid(struct opt_state *state, struct bfs_expr *expr) { struct range *range = &state->facts_when_true.ranges[UID_RANGE]; @@ -847,10 +891,59 @@ static struct bfs_expr *optimize_samefile(struct opt_state *state, struct bfs_ex return expr; } +/** Optimize -size. */ +static struct bfs_expr *optimize_size(struct opt_state *state, struct bfs_expr *expr) { + struct range *range = &state->facts_when_true.ranges[SIZE_RANGE]; + if (range->min == range->max) { + expr->probability = 0.01; + } else { + expr->probability = 0.5; + } + + return expr; +} + +/** Estimate probability for -x?type. */ +static void estimate_type_probability(struct bfs_expr *expr) { + unsigned int types = expr->num; + + expr->probability = 0.0; + if (types & (1 << BFS_BLK)) { + expr->probability += 0.00000721183; + } + if (types & (1 << BFS_CHR)) { + expr->probability += 0.0000499855; + } + if (types & (1 << BFS_DIR)) { + expr->probability += 0.114475; + } + if (types & (1 << BFS_DOOR)) { + expr->probability += 0.000001; + } + if (types & (1 << BFS_FIFO)) { + expr->probability += 0.00000248684; + } + if (types & (1 << BFS_REG)) { + expr->probability += 0.859772; + } + if (types & (1 << BFS_LNK)) { + expr->probability += 0.0256816; + } + if (types & (1 << BFS_SOCK)) { + expr->probability += 0.0000116881; + } + if (types & (1 << BFS_WHT)) { + expr->probability += 0.000001; + } +} + /** Optimize -type. */ static struct bfs_expr *optimize_type(struct opt_state *state, struct bfs_expr *expr) { state->facts_when_true.types &= expr->num; state->facts_when_false.types &= ~expr->num; + + estimate_type_probability(expr); + return expr; } @@ -865,6 +958,9 @@ static struct bfs_expr *optimize_xtype(struct opt_state *state, struct bfs_expr state->facts_when_true.xtypes &= expr->num; state->facts_when_false.xtypes &= ~expr->num; + + estimate_type_probability(expr); + return expr; } @@ -975,6 +1071,29 @@ static const struct { {eval_xattrname, STAT_COST}, }; +/** + * Table of expression probabilities. + */ +static const struct { + /** The evaluation function with this cost. */ + bfs_eval_fn *eval_fn; + /** The matching probability. */ + float probability; +} opt_probs[] = { + {eval_acl, 0.00002}, + {eval_capable, 0.000002}, + {eval_empty, 0.01}, + {eval_false, 0.0}, + {eval_hidden, 0.01}, + {eval_nogroup, 0.01}, + {eval_nouser, 0.01}, + {eval_samefile, 0.01}, + {eval_true, 1.0}, + {eval_xattr, 0.01}, + {eval_xattrname, 0.01}, +}; + + /** * Table of simple predicates. */ @@ -1026,7 +1145,13 @@ static const struct { {eval_empty, optimize_empty}, {eval_exec, optimize_exec}, {eval_gid, optimize_gid}, + {eval_inum, optimize_inum}, + {eval_links, optimize_links}, + {eval_lname, optimize_fnmatch}, + {eval_name, optimize_fnmatch}, + {eval_path, optimize_fnmatch}, {eval_samefile, optimize_samefile}, + {eval_size, optimize_size}, {eval_type, optimize_type}, {eval_uid, optimize_uid}, {eval_xtype, optimize_xtype}, @@ -1071,6 +1196,13 @@ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs } } + for (size_t i = 0; i < BFS_COUNTOF(opt_probs); ++i) { + if (opt_probs[i].eval_fn == expr->eval_fn) { + expr->probability = opt_probs[i].probability; + break; + } + } + for (size_t i = 0; i < BFS_COUNTOF(opt_preds); ++i) { if (opt_preds[i].eval_fn == expr->eval_fn) { infer_pred_facts(state, opt_preds[i].pred); diff --git a/src/parse.c b/src/parse.c index eee185b..f2582e0 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1010,24 +1010,9 @@ static struct bfs_expr *parse_xargs_safe(struct parser_state *state, int arg1, i */ static struct bfs_expr *parse_access(struct parser_state *state, int flag, int arg2) { struct bfs_expr *expr = parse_nullary_test(state, eval_access); - if (!expr) { - return NULL; - } - - expr->num = flag; - - switch (flag) { - case R_OK: - expr->probability = 0.99; - break; - case W_OK: - expr->probability = 0.8; - break; - case X_OK: - expr->probability = 0.2; - break; + if (expr) { + expr->num = flag; } - return expr; } @@ -1036,11 +1021,7 @@ static struct bfs_expr *parse_access(struct parser_state *state, int flag, int a */ static struct bfs_expr *parse_acl(struct parser_state *state, int flag, int arg2) { #if BFS_CAN_CHECK_ACL - struct bfs_expr *expr = parse_nullary_test(state, eval_acl); - if (expr) { - expr->probability = 0.00002; - } - return expr; + return parse_nullary_test(state, eval_acl); #else parse_error(state, "Missing platform support.\n"); return NULL; @@ -1160,11 +1141,7 @@ fail: */ static struct bfs_expr *parse_capable(struct parser_state *state, int flag, int arg2) { #if BFS_CAN_CHECK_CAPABILITIES - struct bfs_expr *expr = parse_nullary_test(state, eval_capable); - if (expr) { - expr->probability = 0.000002; - } - return expr; + return parse_nullary_test(state, eval_capable); #else parse_error(state, "Missing platform support.\n"); return NULL; @@ -1293,12 +1270,9 @@ static struct bfs_expr *parse_depth_limit(struct parser_state *state, int is_min */ static struct bfs_expr *parse_empty(struct parser_state *state, int arg1, int arg2) { struct bfs_expr *expr = parse_nullary_test(state, eval_empty); - if (!expr) { - return NULL; + if (expr) { + expr->ephemeral_fds = 1; } - - expr->probability = 0.01; - expr->ephemeral_fds = 1; return expr; } @@ -1658,11 +1632,7 @@ fail: * Parse -hidden. */ static struct bfs_expr *parse_hidden(struct parser_state *state, int arg1, int arg2) { - struct bfs_expr *expr = parse_nullary_test(state, eval_hidden); - if (expr) { - expr->probability = 0.01; - } - return expr; + return parse_nullary_test(state, eval_hidden); } /** @@ -1677,22 +1647,14 @@ static struct bfs_expr *parse_ignore_races(struct parser_state *state, int ignor * Parse -inum N. */ static struct bfs_expr *parse_inum(struct parser_state *state, int arg1, int arg2) { - struct bfs_expr *expr = parse_test_icmp(state, eval_inum); - if (expr) { - expr->probability = expr->int_cmp == BFS_INT_EQUAL ? 0.01 : 0.50; - } - return expr; + return parse_test_icmp(state, eval_inum); } /** * Parse -links N. */ static struct bfs_expr *parse_links(struct parser_state *state, int arg1, int arg2) { - struct bfs_expr *expr = parse_test_icmp(state, eval_links); - if (expr) { - expr->probability = bfs_expr_cmp(expr, 1) ? 0.99 : 0.01; - } - return expr; + return parse_test_icmp(state, eval_links); } /** @@ -1764,12 +1726,6 @@ static struct bfs_expr *parse_fnmatch(const struct parser_state *state, struct b return expr; } - if (strchr(pattern, '*')) { - expr->probability = 0.5; - } else { - expr->probability = 0.1; - } - return expr; } @@ -1921,16 +1877,11 @@ fail: */ static struct bfs_expr *parse_nogroup(struct parser_state *state, int arg1, int arg2) { struct bfs_expr *expr = parse_nullary_test(state, eval_nogroup); - if (!expr) { - return NULL; + if (expr) { + // Who knows how many FDs getgrgid_r() needs? Probably at least + // one for /etc/group + expr->ephemeral_fds = 1; } - - expr->probability = 0.01; - - // Who knows how many FDs getgrgid_r() needs? Probably at least one for - // /etc/group - expr->ephemeral_fds = 1; - return expr; } @@ -1943,8 +1894,6 @@ static struct bfs_expr *parse_nohidden(struct parser_state *state, int arg1, int return NULL; } - hidden->probability = 0.01; - if (parse_exclude(state, hidden) != 0) { return NULL; } @@ -1966,16 +1915,11 @@ static struct bfs_expr *parse_noleaf(struct parser_state *state, int arg1, int a */ static struct bfs_expr *parse_nouser(struct parser_state *state, int arg1, int arg2) { struct bfs_expr *expr = parse_nullary_test(state, eval_nouser); - if (!expr) { - return NULL; + if (expr) { + // Who knows how many FDs getpwuid_r() needs? Probably at least + // one for /etc/passwd + expr->ephemeral_fds = 1; } - - expr->probability = 0.01; - - // Who knows how many FDs getpwuid_r() needs? Probably at least one for - // /etc/passwd - expr->ephemeral_fds = 1; - return expr; } @@ -2428,9 +2372,6 @@ static struct bfs_expr *parse_samefile(struct parser_state *state, int arg1, int expr->dev = sb.dev; expr->ino = sb.ino; - - expr->probability = 0.01; - return expr; } @@ -2548,8 +2489,6 @@ static struct bfs_expr *parse_size(struct parser_state *state, int arg1, int arg goto bad_unit; } - expr->probability = expr->int_cmp == BFS_INT_EQUAL ? 0.01 : 0.50; - return expr; bad_unit: @@ -2584,50 +2523,37 @@ static struct bfs_expr *parse_type(struct parser_state *state, int x, int arg2) return NULL; } - unsigned int types = 0; - float probability = 0.0; + expr->num = 0; const char *c = expr->argv[1]; while (true) { - enum bfs_type type; - float type_prob; - switch (*c) { case 'b': - type = BFS_BLK; - type_prob = 0.00000721183; + expr->num |= 1 << BFS_BLK; break; case 'c': - type = BFS_CHR; - type_prob = 0.0000499855; + expr->num |= 1 << BFS_CHR; break; case 'd': - type = BFS_DIR; - type_prob = 0.114475; + expr->num |= 1 << BFS_DIR; break; case 'D': - type = BFS_DOOR; - type_prob = 0.000001; + expr->num |= 1 << BFS_DOOR; break; case 'p': - type = BFS_FIFO; - type_prob = 0.00000248684; + expr->num |= 1 << BFS_FIFO; break; case 'f': - type = BFS_REG; - type_prob = 0.859772; + expr->num |= 1 << BFS_REG; break; case 'l': - type = BFS_LNK; - type_prob = 0.0256816; + expr->num |= 1 << BFS_LNK; break; case 's': - type = BFS_SOCK; - type_prob = 0.0000116881; + expr->num |= 1 << BFS_SOCK; break; case 'w': - type = BFS_WHT; - type_prob = 0.000001; + expr->num |= 1 << BFS_WHT; break; case '\0': @@ -2639,12 +2565,6 @@ static struct bfs_expr *parse_type(struct parser_state *state, int x, int arg2) goto fail; } - unsigned int flag = 1 << type; - if (!(types & flag)) { - types |= flag; - probability += type_prob; - } - ++c; if (*c == '\0') { break; @@ -2657,9 +2577,6 @@ static struct bfs_expr *parse_type(struct parser_state *state, int x, int arg2) } } - expr->num = types; - expr->probability = probability; - return expr; fail: @@ -2680,11 +2597,7 @@ static struct bfs_expr *parse_warn(struct parser_state *state, int warn, int arg */ static struct bfs_expr *parse_xattr(struct parser_state *state, int arg1, int arg2) { #if BFS_CAN_CHECK_XATTRS - struct bfs_expr *expr = parse_nullary_test(state, eval_xattr); - if (expr) { - expr->probability = 0.01; - } - return expr; + return parse_nullary_test(state, eval_xattr); #else parse_error(state, "Missing platform support.\n"); return NULL; @@ -2696,11 +2609,7 @@ static struct bfs_expr *parse_xattr(struct parser_state *state, int arg1, int ar */ static struct bfs_expr *parse_xattrname(struct parser_state *state, int arg1, int arg2) { #if BFS_CAN_CHECK_XATTRS - struct bfs_expr *expr = parse_unary_test(state, eval_xattrname); - if (expr) { - expr->probability = 0.01; - } - return expr; + return parse_unary_test(state, eval_xattrname); #else parse_error(state, "Missing platform support.\n"); return NULL; -- cgit v1.2.3 From 9463fdd30d392c98de7b5712d30dfbaeada40e99 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 25 Jan 2023 16:14:11 -0500 Subject: Replace license boilerplate with SPDX tags And while I'm at it, remove years from copyright declarations. Link: https://spdx.dev/about/ Link: https://daniel.haxx.se/blog/2023/01/08/copyright-without-years/ --- LICENSE | 33 +++++++++++++++++++++------------ Makefile | 17 ++--------------- completions/bfs.bash | 21 ++++----------------- src/bar.c | 17 ++--------------- src/bar.h | 17 ++--------------- src/bfstd.c | 17 ++--------------- src/bfstd.h | 17 ++--------------- src/bftw.c | 17 ++--------------- src/bftw.h | 17 ++--------------- src/color.c | 17 ++--------------- src/color.h | 17 ++--------------- src/config.h | 17 ++--------------- src/ctx.c | 17 ++--------------- src/ctx.h | 17 ++--------------- src/darray.c | 17 ++--------------- src/darray.h | 17 ++--------------- src/diag.c | 17 ++--------------- src/diag.h | 17 ++--------------- src/dir.c | 17 ++--------------- src/dir.h | 17 ++--------------- src/dstring.c | 17 ++--------------- src/dstring.h | 17 ++--------------- src/eval.c | 17 ++--------------- src/eval.h | 17 ++--------------- src/exec.c | 17 ++--------------- src/exec.h | 17 ++--------------- src/expr.h | 17 ++--------------- src/fsade.c | 17 ++--------------- src/fsade.h | 17 ++--------------- src/main.c | 17 ++--------------- src/mtab.c | 17 ++--------------- src/mtab.h | 17 ++--------------- src/opt.c | 17 ++--------------- src/opt.h | 17 ++--------------- src/parse.c | 17 ++--------------- src/parse.h | 17 ++--------------- src/printf.c | 17 ++--------------- src/printf.h | 17 ++--------------- src/pwcache.c | 17 ++--------------- src/pwcache.h | 17 ++--------------- src/stat.c | 17 ++--------------- src/stat.h | 17 ++--------------- src/trie.c | 17 ++--------------- src/trie.h | 17 ++--------------- src/typo.c | 17 ++--------------- src/typo.h | 17 ++--------------- src/xregex.c | 17 ++--------------- src/xregex.h | 18 ++---------------- src/xspawn.c | 17 ++--------------- src/xspawn.h | 17 ++--------------- src/xtime.c | 17 ++--------------- src/xtime.h | 17 ++--------------- tests/bfstd.c | 17 ++--------------- tests/find-color.sh | 17 ++--------------- tests/ls-color.sh | 17 ++--------------- tests/mksock.c | 17 ++--------------- tests/tests.sh | 17 ++--------------- tests/trie.c | 17 ++--------------- tests/xtimegm.c | 17 ++--------------- tests/xtouch.c | 17 ++--------------- 60 files changed, 141 insertions(+), 900 deletions(-) (limited to 'src/opt.c') diff --git a/LICENSE b/LICENSE index 069b145..290e3d3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,12 +1,21 @@ -Copyright (C) 2015-2021 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. +Copyright © 2015-2023 Tavian Barnes and the bfs contributors + +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 is licensed under the Zero Clause BSD License. Individual files contain the following +tag instead of the full license text: + + SPDX-License-Identifier: 0BSD + +This enables machine processing of license information based on the SPDX License +Identifiers that are available here: https://spdx.org/licenses/ diff --git a/Makefile b/Makefile index b39a88a..0247b47 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,5 @@ -############################################################################ -# bfs # -# Copyright (C) 2015-2023 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. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD ifneq ($(wildcard .git),) VERSION := $(shell git describe --always 2>/dev/null) diff --git a/completions/bfs.bash b/completions/bfs.bash index f734ab1..2f52e8d 100644 --- a/completions/bfs.bash +++ b/completions/bfs.bash @@ -1,21 +1,8 @@ -# bash completion script for bfs +# Copyright © Benjamin Mundt +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD -############################################################################ -# bfs # -# Copyright (C) 2020 Benjamin Mundt # -# Copyright (C) 2021 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. # -############################################################################ +# bash completion script for bfs _bfs() { local cur prev words cword diff --git a/src/bar.c b/src/bar.c index 37d33c8..bd5d381 100644 --- a/src/bar.c +++ b/src/bar.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "bar.h" #include "bfstd.h" diff --git a/src/bar.h b/src/bar.h index 3e509d6..20d92a9 100644 --- a/src/bar.h +++ b/src/bar.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A terminal status bar. diff --git a/src/bfstd.c b/src/bfstd.c index 3a37250..437e9c9 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "bfstd.h" #include "config.h" diff --git a/src/bfstd.h b/src/bfstd.h index 79307cc..0e11b66 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Standard library wrappers and polyfills. diff --git a/src/bftw.c b/src/bftw.c index 5f3ebde..9feca79 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The bftw() implementation consists of the following components: diff --git a/src/bftw.h b/src/bftw.h index c458e1b..77697ed 100644 --- a/src/bftw.h +++ b/src/bftw.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2021 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A file-walking API based on nftw(). diff --git a/src/color.c b/src/color.c index 7c16ec5..589e631 100644 --- a/src/color.c +++ b/src/color.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "color.h" #include "bfstd.h" diff --git a/src/color.h b/src/color.h index 5b350cc..737c34b 100644 --- a/src/color.h +++ b/src/color.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2021 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Utilities for colored output on ANSI terminals. diff --git a/src/config.h b/src/config.h index 810f913..5ae9c82 100644 --- a/src/config.h +++ b/src/config.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Configuration and feature/platform detection. diff --git a/src/ctx.c b/src/ctx.c index 0403299..ff4a2a7 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "ctx.h" #include "color.h" diff --git a/src/ctx.h b/src/ctx.h index 6755d02..4c748b7 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * bfs execution context. diff --git a/src/darray.c b/src/darray.c index 6585d30..42b8397 100644 --- a/src/darray.c +++ b/src/darray.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "darray.h" #include diff --git a/src/darray.h b/src/darray.h index 4464381..cc6cc42 100644 --- a/src/darray.h +++ b/src/darray.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A dynamic array library. diff --git a/src/diag.c b/src/diag.c index b02473a..53db98e 100644 --- a/src/diag.c +++ b/src/diag.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "diag.h" #include "bfstd.h" diff --git a/src/diag.h b/src/diag.h index 8d0b19f..2952e30 100644 --- a/src/diag.h +++ b/src/diag.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Formatters for diagnostic messages. diff --git a/src/dir.c b/src/dir.c index 2081cc5..3d18eb2 100644 --- a/src/dir.c +++ b/src/dir.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2021-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "dir.h" #include "bfstd.h" diff --git a/src/dir.h b/src/dir.h index 69344c6..01eaaba 100644 --- a/src/dir.h +++ b/src/dir.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2021 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Directories and their contents. diff --git a/src/dstring.c b/src/dstring.c index f344d09..9112e54 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "dstring.h" #include diff --git a/src/dstring.h b/src/dstring.h index df20a04..ee3b345 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A dynamic string library. diff --git a/src/eval.c b/src/eval.c index d297af1..53ce605 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Implementation of all the primary expressions. diff --git a/src/eval.h b/src/eval.h index a50dc4e..3d70319 100644 --- a/src/eval.h +++ b/src/eval.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The evaluation functions that implement primary expressions like -name, diff --git a/src/exec.c b/src/exec.c index 8630469..6bde1c1 100644 --- a/src/exec.c +++ b/src/exec.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "exec.h" #include "bfstd.h" diff --git a/src/exec.h b/src/exec.h index a3e3c71..9d4192d 100644 --- a/src/exec.h +++ b/src/exec.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * 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. * - * * - * 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Implementation of -exec/-execdir/-ok/-okdir. diff --git a/src/expr.h b/src/expr.h index a52007a..1628cac 100644 --- a/src/expr.h +++ b/src/expr.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The expression tree representation. diff --git a/src/fsade.c b/src/fsade.c index 45969d1..a61a6b8 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2021 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "fsade.h" #include "config.h" diff --git a/src/fsade.h b/src/fsade.h index f45c6fd..9bef892 100644 --- a/src/fsade.h +++ b/src/fsade.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A facade over (file)system features that are (un)implemented differently diff --git a/src/main.c b/src/main.c index fbddbe5..4f99580 100644 --- a/src/main.c +++ b/src/main.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * - main(): the entry point for bfs(1), a breadth-first version of find(1) diff --git a/src/mtab.c b/src/mtab.c index 07d7a53..ae6dfd2 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * 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. * - * * - * 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "mtab.h" #include "bfstd.h" diff --git a/src/mtab.h b/src/mtab.h index 807539d..5dfdf6c 100644 --- a/src/mtab.h +++ b/src/mtab.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * 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. * - * * - * 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A facade over platform-specific APIs for enumerating mounted filesystems. diff --git a/src/opt.c b/src/opt.c index e76e216..5505b7b 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The expression optimizer. Different optimization levels are supported: diff --git a/src/opt.h b/src/opt.h index 5f8180d..28cadb9 100644 --- a/src/opt.h +++ b/src/opt.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Optimization. diff --git a/src/parse.c b/src/parse.c index f2582e0..30bf56d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The command line parser. Expressions are parsed by recursive descent, with a diff --git a/src/parse.h b/src/parse.h index 7e29a03..6895c9f 100644 --- a/src/parse.h +++ b/src/parse.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * bfs command line parsing. diff --git a/src/printf.c b/src/printf.c index 7c0c8db..1b4f2d4 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "printf.h" #include "bfstd.h" diff --git a/src/printf.h b/src/printf.h index a8c5f2a..2bff087 100644 --- a/src/printf.h +++ b/src/printf.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Implementation of -printf/-fprintf. diff --git a/src/pwcache.c b/src/pwcache.c index 868ec8f..5026dee 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "pwcache.h" #include "darray.h" diff --git a/src/pwcache.h b/src/pwcache.h index f1ca0bf..b6c0b67 100644 --- a/src/pwcache.h +++ b/src/pwcache.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A caching wrapper for /etc/{passwd,group}. diff --git a/src/stat.c b/src/stat.c index 94dedef..aaa5eac 100644 --- a/src/stat.c +++ b/src/stat.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "stat.h" #include "bfstd.h" diff --git a/src/stat.h b/src/stat.h index 44cbfad..7a70146 100644 --- a/src/stat.h +++ b/src/stat.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2019 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A facade over the stat() API that unifies some details that diverge between diff --git a/src/trie.c b/src/trie.c index 08a99b5..77c43cc 100644 --- a/src/trie.c +++ b/src/trie.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * This is an implementation of a "qp trie," as documented at diff --git a/src/trie.h b/src/trie.h index 2ede6ea..03ee64d 100644 --- a/src/trie.h +++ b/src/trie.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #ifndef BFS_TRIE_H #define BFS_TRIE_H diff --git a/src/typo.c b/src/typo.c index c16cab4..305711d 100644 --- a/src/typo.c +++ b/src/typo.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "typo.h" #include diff --git a/src/typo.h b/src/typo.h index 0347aae..13eaa67 100644 --- a/src/typo.h +++ b/src/typo.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #ifndef BFS_TYPO_H #define BFS_TYPO_H diff --git a/src/xregex.c b/src/xregex.c index 6f0e5a1..5f5480f 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "xregex.h" #include "config.h" diff --git a/src/xregex.h b/src/xregex.h index b2f56a5..998a2b0 100644 --- a/src/xregex.h +++ b/src/xregex.h @@ -1,19 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes and bfs * - * contributors * - * * - * 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes and the bfs contributors +// SPDX-License-Identifier: 0BSD #ifndef BFS_XREGEX_H #define BFS_XREGEX_H diff --git a/src/xspawn.c b/src/xspawn.c index f76267b..a30c264 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "xspawn.h" #include "bfstd.h" diff --git a/src/xspawn.h b/src/xspawn.h index cd6a42e..3dbf5d2 100644 --- a/src/xspawn.h +++ b/src/xspawn.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A process-spawning library inspired by posix_spawn(). diff --git a/src/xtime.c b/src/xtime.c index 079d42a..82690d0 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "xtime.h" #include diff --git a/src/xtime.h b/src/xtime.h index b49cd04..75d1f4e 100644 --- a/src/xtime.h +++ b/src/xtime.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Date/time handling. diff --git a/tests/bfstd.c b/tests/bfstd.c index 4a8181b..8c61072 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "../src/bfstd.h" #include diff --git a/tests/find-color.sh b/tests/find-color.sh index ecdd5af..47de2a2 100755 --- a/tests/find-color.sh +++ b/tests/find-color.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2019 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. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD set -e diff --git a/tests/ls-color.sh b/tests/ls-color.sh index c82a58d..6d33f53 100755 --- a/tests/ls-color.sh +++ b/tests/ls-color.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2019 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. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD # Prints the "ground truth" coloring of a path using ls diff --git a/tests/mksock.c b/tests/mksock.c index 5068bc8..05edbeb 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * There's no standard Unix utility that creates a socket file, so this small diff --git a/tests/tests.sh b/tests/tests.sh index e57db4e..98d332c 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2015-2022 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. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD set -euP umask 022 diff --git a/tests/trie.c b/tests/trie.c index 88e92da..c2af18a 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #undef NDEBUG diff --git a/tests/xtimegm.c b/tests/xtimegm.c index d774b9e..bab64ba 100644 --- a/tests/xtimegm.c +++ b/tests/xtimegm.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "../src/xtime.h" #include diff --git a/tests/xtouch.c b/tests/xtouch.c index 9a91ec7..506c73d 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 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. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "../src/bfstd.h" #include "../src/xtime.h" -- cgit v1.2.3 From 8368e139c176dcde3b125f4a180ff868729b1862 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 10 May 2023 15:42:54 -0400 Subject: config: s/BFS_COUNTOF/countof/ --- src/config.h | 2 +- src/fsade.c | 2 +- src/opt.c | 16 ++++++++-------- tests/trie.c | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'src/opt.c') diff --git a/src/config.h b/src/config.h index e17f90b..60bc930 100644 --- a/src/config.h +++ b/src/config.h @@ -134,7 +134,7 @@ /** * Get the length of an array. */ -#define BFS_COUNTOF(array) (sizeof(array) / sizeof(0[array])) +#define countof(array) (sizeof(array) / sizeof(0[array])) // Lower bound on BFS_FLEX_SIZEOF() #define BFS_FLEX_LB(type, member, length) (offsetof(type, member) + sizeof(((type *)NULL)->member[0]) * (length)) diff --git a/src/fsade.c b/src/fsade.c index aefbb75..4d67940 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -201,7 +201,7 @@ int bfs_check_acl(const struct BFTW *ftwbuf) { const char *path = fake_at(ftwbuf); int ret = -1, error = 0; - for (size_t i = 0; i < BFS_COUNTOF(acl_types) && ret <= 0; ++i) { + for (size_t i = 0; i < countof(acl_types) && ret <= 0; ++i) { acl_type_t type = acl_types[i]; if (type == ACL_TYPE_DEFAULT && ftwbuf->type != BFS_DIR) { diff --git a/src/opt.c b/src/opt.c index 5505b7b..731dd10 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1154,21 +1154,21 @@ static const struct { * Look up the appropriate optimizer for an expression and call it. */ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs_expr *expr) { - for (size_t i = 0; i < BFS_COUNTOF(opt_pure); ++i) { + for (size_t i = 0; i < countof(opt_pure); ++i) { if (opt_pure[i] == expr->eval_fn) { expr->pure = true; break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_always_true); ++i) { + for (size_t i = 0; i < countof(opt_always_true); ++i) { if (opt_always_true[i] == expr->eval_fn) { expr->always_true = true; break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_always_false); ++i) { + for (size_t i = 0; i < countof(opt_always_false); ++i) { if (opt_always_false[i] == expr->eval_fn) { expr->always_false = true; break; @@ -1176,35 +1176,35 @@ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs } expr->cost = FAST_COST; - for (size_t i = 0; i < BFS_COUNTOF(opt_costs); ++i) { + for (size_t i = 0; i < countof(opt_costs); ++i) { if (opt_costs[i].eval_fn == expr->eval_fn) { expr->cost = opt_costs[i].cost; break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_probs); ++i) { + for (size_t i = 0; i < countof(opt_probs); ++i) { if (opt_probs[i].eval_fn == expr->eval_fn) { expr->probability = opt_probs[i].probability; break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_preds); ++i) { + for (size_t i = 0; i < countof(opt_preds); ++i) { if (opt_preds[i].eval_fn == expr->eval_fn) { infer_pred_facts(state, opt_preds[i].pred); break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_ranges); ++i) { + for (size_t i = 0; i < countof(opt_ranges); ++i) { if (opt_ranges[i].eval_fn == expr->eval_fn) { infer_icmp_facts(state, expr, opt_ranges[i].range); break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_fns); ++i) { + for (size_t i = 0; i < countof(opt_fns); ++i) { if (opt_fns[i].eval_fn == expr->eval_fn) { return opt_fns[i].opt_fn(state, expr); } diff --git a/tests/trie.c b/tests/trie.c index c2af18a..ced14d4 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -38,7 +38,7 @@ const char *keys[] = { ">>>", }; -const size_t nkeys = BFS_COUNTOF(keys); +const size_t nkeys = countof(keys); int main(void) { struct trie trie; -- cgit v1.2.3 From 59f87eed2b930af2f31fd1d1fb2589f80f426ee0 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 11 May 2023 10:12:35 -0400 Subject: config: Provide and In anticipation of C23, since those headers won't be necessary any more. --- src/bfstd.c | 1 - src/bfstd.h | 2 +- src/bftw.c | 1 - src/color.c | 1 - src/color.h | 1 - src/config.h | 6 +++++- src/ctx.h | 2 +- src/diag.h | 1 - src/dir.c | 2 -- src/eval.h | 2 +- src/exec.c | 1 - src/expr.h | 2 +- src/fsade.h | 1 - src/main.c | 2 +- src/mtab.c | 1 - src/mtab.h | 2 +- src/opt.c | 1 - src/parse.c | 1 - src/printf.c | 1 - src/pwcache.c | 2 +- src/stat.c | 1 - src/trie.c | 1 - src/trie.h | 1 - src/xspawn.c | 1 - src/xtime.c | 2 +- tests/xtimegm.c | 2 +- tests/xtouch.c | 2 +- 27 files changed, 15 insertions(+), 28 deletions(-) (limited to 'src/opt.c') diff --git a/src/bfstd.c b/src/bfstd.c index 1dc322b..932f2c4 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/src/bfstd.h b/src/bfstd.h index 028e4e6..e4fd1f1 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -8,7 +8,7 @@ #ifndef BFS_BFSTD_H #define BFS_BFSTD_H -#include +#include "config.h" #include // #include diff --git a/src/bftw.c b/src/bftw.c index 56701a7..14805de 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/src/color.c b/src/color.c index 589e631..eeadf98 100644 --- a/src/color.c +++ b/src/color.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/src/color.h b/src/color.h index 737c34b..3db2b07 100644 --- a/src/color.h +++ b/src/color.h @@ -10,7 +10,6 @@ #include "config.h" #include -#include #include /** diff --git a/src/config.h b/src/config.h index 229bafb..4408feb 100644 --- a/src/config.h +++ b/src/config.h @@ -8,9 +8,13 @@ #ifndef BFS_CONFIG_H #define BFS_CONFIG_H -#include #include +#if __STDC_VERSION__ < 202311L +# include +# include +#endif + // bfs packaging configuration #ifndef BFS_COMMAND diff --git a/src/ctx.h b/src/ctx.h index 4c748b7..0dc9f08 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -9,8 +9,8 @@ #define BFS_CTX_H #include "bftw.h" +#include "config.h" #include "trie.h" -#include #include #include #include diff --git a/src/diag.h b/src/diag.h index 2952e30..987d4b4 100644 --- a/src/diag.h +++ b/src/diag.h @@ -11,7 +11,6 @@ #include "ctx.h" #include "config.h" #include -#include struct bfs_expr; diff --git a/src/dir.c b/src/dir.c index eb6e3e0..30db5df 100644 --- a/src/dir.c +++ b/src/dir.c @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/src/eval.h b/src/eval.h index 3d70319..bdb9440 100644 --- a/src/eval.h +++ b/src/eval.h @@ -9,7 +9,7 @@ #ifndef BFS_EVAL_H #define BFS_EVAL_H -#include +#include "config.h" struct bfs_ctx; struct bfs_expr; diff --git a/src/exec.c b/src/exec.c index 6bde1c1..7f22d36 100644 --- a/src/exec.c +++ b/src/exec.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/src/expr.h b/src/expr.h index 1628cac..356689d 100644 --- a/src/expr.h +++ b/src/expr.h @@ -9,9 +9,9 @@ #define BFS_EXPR_H #include "color.h" +#include "config.h" #include "eval.h" #include "stat.h" -#include #include #include #include diff --git a/src/fsade.h b/src/fsade.h index 557da26..0d9ecaf 100644 --- a/src/fsade.h +++ b/src/fsade.h @@ -10,7 +10,6 @@ #define BFS_FSADE_H #include "config.h" -#include #define BFS_CAN_CHECK_ACL BFS_USE_SYS_ACL_H diff --git a/src/main.c b/src/main.c index 4f99580..24a5035 100644 --- a/src/main.c +++ b/src/main.c @@ -40,13 +40,13 @@ */ #include "bfstd.h" +#include "config.h" #include "ctx.h" #include "eval.h" #include "parse.h" #include #include #include -#include #include #include #include diff --git a/src/mtab.c b/src/mtab.c index 27f1743..1d1ad94 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -9,7 +9,6 @@ #include "trie.h" #include #include -#include #include #include #include diff --git a/src/mtab.h b/src/mtab.h index 5dfdf6c..ca4372c 100644 --- a/src/mtab.h +++ b/src/mtab.h @@ -8,7 +8,7 @@ #ifndef BFS_MTAB_H #define BFS_MTAB_H -#include +#include "config.h" struct bfs_stat; diff --git a/src/opt.c b/src/opt.c index 731dd10..4ce9425 100644 --- a/src/opt.c +++ b/src/opt.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/src/parse.c b/src/parse.c index 15f38a4..55f1e74 100644 --- a/src/parse.c +++ b/src/parse.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/src/printf.c b/src/printf.c index 454fbee..9ccc216 100644 --- a/src/printf.c +++ b/src/printf.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/src/pwcache.c b/src/pwcache.c index 5026dee..f52e4e1 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -2,12 +2,12 @@ // SPDX-License-Identifier: 0BSD #include "pwcache.h" +#include "config.h" #include "darray.h" #include "trie.h" #include #include #include -#include #include #include #include diff --git a/src/stat.c b/src/stat.c index f3d9046..7973d71 100644 --- a/src/stat.c +++ b/src/stat.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/src/trie.c b/src/trie.c index bfe97e6..e0e00ff 100644 --- a/src/trie.c +++ b/src/trie.c @@ -86,7 +86,6 @@ #include "list.h" #include #include -#include #include #include #include diff --git a/src/trie.h b/src/trie.h index 58974aa..6bd211e 100644 --- a/src/trie.h +++ b/src/trie.h @@ -5,7 +5,6 @@ #define BFS_TRIE_H #include "config.h" -#include #include #include diff --git a/src/xspawn.c b/src/xspawn.c index a6d18a3..00fb76e 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -7,7 +7,6 @@ #include "list.h" #include #include -#include #include #include #include diff --git a/src/xtime.c b/src/xtime.c index 82690d0..406d694 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -2,9 +2,9 @@ // SPDX-License-Identifier: 0BSD #include "xtime.h" +#include "config.h" #include #include -#include #include #include #include diff --git a/tests/xtimegm.c b/tests/xtimegm.c index bab64ba..b2479b7 100644 --- a/tests/xtimegm.c +++ b/tests/xtimegm.c @@ -2,7 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "../src/xtime.h" -#include +#include "../src/config.h" #include #include #include diff --git a/tests/xtouch.c b/tests/xtouch.c index 7e29547..50416ba 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -2,10 +2,10 @@ // SPDX-License-Identifier: 0BSD #include "../src/bfstd.h" +#include "../src/config.h" #include "../src/xtime.h" #include #include -#include #include #include #include -- cgit v1.2.3 From 526133c11eb9a26a4cffb20bcd10bcbb36d940de Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 May 2023 16:44:30 -0400 Subject: Switch from assert() to bfs_assert()/bfs_verify() --- src/bfstd.c | 3 +- src/bftw.c | 28 ++++++++-------- src/color.c | 5 ++- src/diag.c | 3 +- src/dir.c | 4 +-- src/eval.c | 11 +++---- src/exec.c | 7 ++-- src/opt.c | 13 ++++---- src/parse.c | 7 ++-- src/printf.c | 5 ++- src/trie.c | 33 ++++++++++--------- src/xregex.c | 4 +-- tests/bfstd.c | 13 ++++---- tests/bit.c | 100 +++++++++++++++++++++++++++++----------------------------- tests/trie.c | 56 ++++++++++++++++---------------- 15 files changed, 140 insertions(+), 152 deletions(-) (limited to 'src/opt.c') diff --git a/src/bfstd.c b/src/bfstd.c index 91383a2..37f5276 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -5,7 +5,6 @@ #include "config.h" #include "diag.h" #include "xregex.h" -#include #include #include #include @@ -393,7 +392,7 @@ void close_quietly(int fd) { int xclose(int fd) { int ret = close(fd); if (ret != 0) { - assert(errno != EBADF); + bfs_verify(errno != EBADF); } return ret; } diff --git a/src/bftw.c b/src/bftw.c index 14805de..6ec5cfa 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -19,13 +19,13 @@ #include "bftw.h" #include "bfstd.h" #include "config.h" +#include "diag.h" #include "dir.h" #include "dstring.h" #include "list.h" #include "mtab.h" #include "stat.h" #include "trie.h" -#include #include #include #include @@ -114,7 +114,7 @@ static void bftw_cache_remove(struct bftw_cache *cache, struct bftw_file *file) /** Close a bftw_file. */ static void bftw_file_close(struct bftw_cache *cache, struct bftw_file *file) { - assert(file->fd >= 0); + bfs_assert(file->fd >= 0); if (LIST_ATTACHED(cache, file, lru)) { bftw_cache_remove(cache, file); @@ -137,7 +137,7 @@ static int bftw_cache_pop(struct bftw_cache *cache) { /** Add a bftw_file to the cache. */ static int bftw_cache_add(struct bftw_cache *cache, struct bftw_file *file) { - assert(file->fd >= 0); + bfs_assert(file->fd >= 0); if (cache->capacity == 0 && bftw_cache_pop(cache) != 0) { bftw_file_close(cache, file); @@ -145,7 +145,7 @@ static int bftw_cache_add(struct bftw_cache *cache, struct bftw_file *file) { return -1; } - assert(cache->capacity > 0); + bfs_assert(cache->capacity > 0); --cache->capacity; LIST_INSERT(cache, cache->target, file, lru); @@ -169,9 +169,9 @@ static size_t bftw_child_nameoff(const struct bftw_file *parent) { /** Destroy a cache. */ static void bftw_cache_destroy(struct bftw_cache *cache) { - assert(!cache->head); - assert(!cache->tail); - assert(!cache->target); + bfs_assert(!cache->head); + bfs_assert(!cache->tail); + bfs_assert(!cache->target); } /** Create a new bftw_file. */ @@ -230,7 +230,7 @@ static struct bftw_file *bftw_file_new(struct bftw_file *parent, const char *nam * The opened file descriptor, or negative on error. */ static int bftw_file_openat(struct bftw_cache *cache, struct bftw_file *file, struct bftw_file *base, const char *at_path) { - assert(file->fd < 0); + bfs_assert(file->fd < 0); int at_fd = AT_FDCWD; if (base) { @@ -332,7 +332,7 @@ static struct bfs_dir *bftw_file_opendir(struct bftw_cache *cache, struct bftw_f /** Free a bftw_file. */ static void bftw_file_free(struct bftw_cache *cache, struct bftw_file *file) { - assert(file->refcount == 0); + bfs_assert(file->refcount == 0); if (file->fd >= 0) { bftw_file_close(cache, file); @@ -770,7 +770,7 @@ static enum bftw_action bftw_call_back(struct bftw_state *state, const char *nam /** Pop a directory to read from the queue. */ static bool bftw_pop_dir(struct bftw_state *state) { - assert(!state->file); + bfs_assert(!state->file); if (!state->dirs.head) { return false; @@ -787,7 +787,7 @@ static bool bftw_pop_dir(struct bftw_state *state) { /** Pop a file to visit from the queue. */ static bool bftw_pop_file(struct bftw_state *state) { - assert(!state->file); + bfs_assert(!state->file); state->file = state->files.head; if (state->file) { @@ -802,8 +802,8 @@ static bool bftw_pop_file(struct bftw_state *state) { * Open the current directory. */ static int bftw_opendir(struct bftw_state *state) { - assert(!state->dir); - assert(!state->de); + bfs_assert(!state->dir); + bfs_assert(!state->de); state->direrror = 0; @@ -863,7 +863,7 @@ static int bftw_gc(struct bftw_state *state, enum bftw_gc_flags flags) { if (state->dir) { struct bftw_file *file = state->file; - assert(file && file->fd >= 0); + bfs_assert(file && file->fd >= 0); if (file->refcount > 1) { // Keep the fd around if any subdirectories exist diff --git a/src/color.c b/src/color.c index 43ea9a4..a723084 100644 --- a/src/color.c +++ b/src/color.c @@ -12,7 +12,6 @@ #include "fsade.h" #include "stat.h" #include "trie.h" -#include #include #include #include @@ -699,7 +698,7 @@ static int print_colored(CFILE *cfile, const char *esc, const char *str, size_t /** Find the offset of the first broken path component. */ static ssize_t first_broken_offset(const char *path, const struct BFTW *ftwbuf, enum bfs_stat_flags flags, size_t max) { ssize_t ret = max; - assert(ret >= 0); + bfs_assert(ret >= 0); if (bftw_type(ftwbuf, flags) != BFS_ERROR) { goto out; @@ -1100,7 +1099,7 @@ static int cbuff(CFILE *cfile, const char *format, ...) { } int cvfprintf(CFILE *cfile, const char *format, va_list args) { - assert(dstrlen(cfile->buffer) == 0); + bfs_assert(dstrlen(cfile->buffer) == 0); int ret = -1; if (cvbuff(cfile, format, args) == 0) { diff --git a/src/diag.c b/src/diag.c index d7ffaa6..04e3678 100644 --- a/src/diag.c +++ b/src/diag.c @@ -6,7 +6,6 @@ #include "ctx.h" #include "color.h" #include "expr.h" -#include #include #include #include @@ -117,7 +116,7 @@ static bool highlight_expr_recursive(const struct bfs_ctx *ctx, const struct bfs for (size_t i = 0; i < ctx->argc; ++i) { if (&ctx->argv[i] == expr->argv) { for (size_t j = 0; j < expr->argc; ++j) { - assert(i + j < ctx->argc); + bfs_assert(i + j < ctx->argc); args[i + j] = true; ret = true; } diff --git a/src/dir.c b/src/dir.c index 6739f10..d9ee63b 100644 --- a/src/dir.c +++ b/src/dir.c @@ -4,7 +4,7 @@ #include "dir.h" #include "bfstd.h" #include "config.h" -#include +#include "diag.h" #include #include #include @@ -249,7 +249,7 @@ int bfs_closedir(struct bfs_dir *dir) { int ret = xclose(dir->fd); #else int ret = closedir(dir->dir); - assert(ret == 0 || errno != EBADF); + bfs_verify(ret == 0 || errno != EBADF); #endif free(dir); return ret; diff --git a/src/eval.c b/src/eval.c index 6cad3a9..342debd 100644 --- a/src/eval.c +++ b/src/eval.c @@ -26,7 +26,6 @@ #include "trie.h" #include "xregex.h" #include "xtime.h" -#include #include #include #include @@ -990,7 +989,7 @@ static bool eval_expr(struct bfs_expr *expr, struct bfs_eval *state) { } } - assert(!state->quit); + bfs_assert(!state->quit); bool ret = expr->eval_fn(expr, state); @@ -1006,10 +1005,10 @@ static bool eval_expr(struct bfs_expr *expr, struct bfs_eval *state) { } if (bfs_expr_never_returns(expr)) { - assert(state->quit); + bfs_assert(state->quit); } else if (!state->quit) { - assert(!expr->always_true || ret); - assert(!expr->always_false || !ret); + bfs_assert(!expr->always_true || ret); + bfs_assert(!expr->always_false || !ret); } return ret; @@ -1511,7 +1510,7 @@ static void dump_bftw_flags(enum bftw_flags flags) { DEBUG_FLAG(flags, BFTW_SORT); DEBUG_FLAG(flags, BFTW_BUFFER); - assert(!flags); + bfs_assert(flags == 0, "Missing bftw flag 0x%X", flags); } /** diff --git a/src/exec.c b/src/exec.c index 7f22d36..5912ad6 100644 --- a/src/exec.c +++ b/src/exec.c @@ -10,7 +10,6 @@ #include "diag.h" #include "dstring.h" #include "xspawn.h" -#include #include #include #include @@ -276,12 +275,12 @@ static void bfs_exec_free_arg(char *arg, const char *tmpl) { /** Open a file to use as the working directory. */ static int bfs_exec_openwd(struct bfs_exec *execbuf, const struct BFTW *ftwbuf) { - assert(execbuf->wd_fd < 0); - assert(!execbuf->wd_path); + bfs_assert(execbuf->wd_fd < 0); + bfs_assert(!execbuf->wd_path); if (ftwbuf->at_fd != AT_FDCWD) { // Rely on at_fd being the immediate parent - assert(xbaseoff(ftwbuf->at_path) == 0); + bfs_assert(xbaseoff(ftwbuf->at_path) == 0); execbuf->wd_fd = ftwbuf->at_fd; if (!(execbuf->flags & BFS_EXEC_MULTI)) { diff --git a/src/opt.c b/src/opt.c index 4ce9425..4699af4 100644 --- a/src/opt.c +++ b/src/opt.c @@ -35,7 +35,6 @@ #include "exec.h" #include "expr.h" #include "pwcache.h" -#include #include #include #include @@ -308,7 +307,7 @@ struct opt_state { /** Log an optimization. */ BFS_FORMATTER(3, 4) static bool opt_debug(const struct opt_state *state, int level, const char *format, ...) { - assert(state->ctx->optlevel >= level); + bfs_assert(state->ctx->optlevel >= level); if (bfs_debug(state->ctx, DEBUG_OPT, "${cyn}-O%d${rs}: ", level)) { va_list args; @@ -387,7 +386,7 @@ static struct bfs_expr *de_morgan(const struct opt_state *state, struct bfs_expr has_parent = false; } - assert(expr->eval_fn == eval_and || expr->eval_fn == eval_or); + bfs_assert(expr->eval_fn == eval_and || expr->eval_fn == eval_or); if (expr->eval_fn == eval_and) { expr->eval_fn = eval_or; expr->argv = &fake_or_arg; @@ -446,7 +445,7 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct * Optimize a negation. */ static struct bfs_expr *optimize_not_expr(const struct opt_state *state, struct bfs_expr *expr) { - assert(expr->eval_fn == eval_not); + bfs_assert(expr->eval_fn == eval_not); struct bfs_expr *rhs = expr->rhs; @@ -498,7 +497,7 @@ fail: /** Optimize a conjunction. */ static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct bfs_expr *expr) { - assert(expr->eval_fn == eval_and); + bfs_assert(expr->eval_fn == eval_and); struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; @@ -569,7 +568,7 @@ fail: /** Optimize a disjunction. */ static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct bfs_expr *expr) { - assert(expr->eval_fn == eval_or); + bfs_assert(expr->eval_fn == eval_or); struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; @@ -673,7 +672,7 @@ static struct bfs_expr *ignore_result(const struct opt_state *state, struct bfs_ /** Optimize a comma expression. */ static struct bfs_expr *optimize_comma_expr(const struct opt_state *state, struct bfs_expr *expr) { - assert(expr->eval_fn == eval_comma); + bfs_assert(expr->eval_fn == eval_comma); struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; diff --git a/src/parse.c b/src/parse.c index 807892c..1a04e68 100644 --- a/src/parse.c +++ b/src/parse.c @@ -29,7 +29,6 @@ #include "xregex.h" #include "xspawn.h" #include "xtime.h" -#include #include #include #include @@ -134,7 +133,7 @@ static struct bfs_expr *new_unary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *rh expr->lhs = NULL; expr->rhs = rhs; - assert(bfs_expr_is_parent(expr)); + bfs_assert(bfs_expr_is_parent(expr)); expr->persistent_fds = rhs->persistent_fds; expr->ephemeral_fds = rhs->ephemeral_fds; @@ -154,7 +153,7 @@ static struct bfs_expr *new_binary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *l expr->lhs = lhs; expr->rhs = rhs; - assert(bfs_expr_is_parent(expr)); + bfs_assert(bfs_expr_is_parent(expr)); expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; if (lhs->ephemeral_fds > rhs->ephemeral_fds) { @@ -263,7 +262,7 @@ static void init_highlight(const struct bfs_ctx *ctx, bool *args) { static void highlight_args(const struct bfs_ctx *ctx, char **argv, size_t argc, bool *args) { size_t i = argv - ctx->argv; for (size_t j = 0; j < argc; ++j) { - assert(i + j < ctx->argc); + bfs_assert(i + j < ctx->argc); args[i + j] = true; } } diff --git a/src/printf.c b/src/printf.c index 9ccc216..6520d2d 100644 --- a/src/printf.c +++ b/src/printf.c @@ -16,7 +16,6 @@ #include "pwcache.h" #include "stat.h" #include "xtime.h" -#include #include #include #include @@ -74,7 +73,7 @@ static bool should_color(CFILE *cfile, const struct bfs_printf *directive) { #define BFS_PRINTF_BUF(buf, format, ...) \ char buf[256]; \ int ret = snprintf(buf, sizeof(buf), format, __VA_ARGS__); \ - assert(ret >= 0 && (size_t)ret < sizeof(buf)); \ + bfs_assert(ret >= 0 && (size_t)ret < sizeof(buf)); \ (void)ret /** @@ -190,7 +189,7 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, break; } - assert(ret >= 0 && (size_t)ret < sizeof(buf)); + bfs_assert(ret >= 0 && (size_t)ret < sizeof(buf)); (void)ret; return dyn_fprintf(cfile->file, directive, buf); diff --git a/src/trie.c b/src/trie.c index a2921de..8543eb1 100644 --- a/src/trie.c +++ b/src/trie.c @@ -86,7 +86,6 @@ #include "config.h" #include "diag.h" #include "list.h" -#include #include #include #include @@ -139,27 +138,27 @@ static bool trie_is_leaf(uintptr_t ptr) { /** Decode a pointer to a leaf. */ static struct trie_leaf *trie_decode_leaf(uintptr_t ptr) { - assert(trie_is_leaf(ptr)); + bfs_assert(trie_is_leaf(ptr)); return (struct trie_leaf *)(ptr ^ 1); } /** Encode a pointer to a leaf. */ static uintptr_t trie_encode_leaf(const struct trie_leaf *leaf) { uintptr_t ptr = (uintptr_t)leaf ^ 1; - assert(trie_is_leaf(ptr)); + bfs_assert(trie_is_leaf(ptr)); return ptr; } /** Decode a pointer to an internal node. */ static struct trie_node *trie_decode_node(uintptr_t ptr) { - assert(!trie_is_leaf(ptr)); + bfs_assert(!trie_is_leaf(ptr)); return (struct trie_node *)ptr; } /** Encode a pointer to an internal node. */ static uintptr_t trie_encode_node(const struct trie_node *node) { uintptr_t ptr = (uintptr_t)node; - assert(!trie_is_leaf(ptr)); + bfs_assert(!trie_is_leaf(ptr)); return ptr; } @@ -341,9 +340,9 @@ static void trie_leaf_free(struct trie *trie, struct trie_leaf *leaf) { /** Compute the size of a trie node with a certain number of children. */ static size_t trie_node_size(unsigned int size) { // Empty nodes aren't supported - assert(size > 0); + bfs_assert(size > 0); // Node size must be a power of two - assert(has_single_bit(size)); + bfs_assert(has_single_bit(size)); return flex_sizeof(struct trie_node, children, size); } @@ -435,7 +434,7 @@ static struct trie_leaf *trie_node_insert(struct trie *trie, uintptr_t *ptr, str unsigned int bit = 1U << nibble; // The child must not already be present - assert(!(node->bitmap & bit)); + bfs_assert(!(node->bitmap & bit)); node->bitmap |= bit; unsigned int target = count_ones(node->bitmap & (bit - 1)); @@ -474,7 +473,7 @@ static struct trie_leaf *trie_node_insert(struct trie *trie, uintptr_t *ptr, str static uintptr_t *trie_jump(uintptr_t *ptr, const char *key, size_t *offset) { // We only ever need to jump to leaf nodes, since internal nodes are // guaranteed to be within OFFSET_MAX anyway - assert(trie_is_leaf(*ptr)); + bfs_assert(trie_is_leaf(*ptr)); struct trie_node *node = malloc(trie_node_size(1)); if (!node) { @@ -512,7 +511,7 @@ static uintptr_t *trie_jump(uintptr_t *ptr, const char *key, size_t *offset) { static struct trie_leaf *trie_split(struct trie *trie, uintptr_t *ptr, struct trie_leaf *leaf, struct trie_leaf *rep, size_t offset, size_t mismatch) { unsigned char key_nibble = trie_key_nibble(leaf->key, mismatch); unsigned char rep_nibble = trie_key_nibble(rep->key, mismatch); - assert(key_nibble != rep_nibble); + bfs_assert(key_nibble != rep_nibble); struct trie_node *node = malloc(trie_node_size(2)); if (!node) { @@ -570,11 +569,11 @@ static struct trie_leaf *trie_insert_mem_impl(struct trie *trie, const void *key unsigned char nibble = trie_key_nibble(key, offset); unsigned int bit = 1U << nibble; if (node->bitmap & bit) { - assert(offset < mismatch); + bfs_assert(offset < mismatch); unsigned int index = count_ones(node->bitmap & (bit - 1)); ptr = &node->children[index]; } else { - assert(offset == mismatch); + bfs_assert(offset == mismatch); return trie_node_insert(trie, ptr, leaf, nibble); } } @@ -600,7 +599,7 @@ static void trie_free_singletons(struct trie *trie, uintptr_t ptr) { struct trie_node *node = trie_decode_node(ptr); // Make sure the bitmap is a power of two, i.e. it has just one child - assert(has_single_bit(node->bitmap)); + bfs_assert(has_single_bit(node->bitmap)); ptr = node->children[0]; free(node); @@ -651,12 +650,12 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { while (!trie_is_leaf(*child)) { struct trie_node *node = trie_decode_node(*child); offset += node->offset; - assert((offset >> 1) < leaf->length); + bfs_assert((offset >> 1) < leaf->length); unsigned char nibble = trie_key_nibble(leaf->key, offset); unsigned int bit = 1U << nibble; unsigned int bitmap = node->bitmap; - assert(bitmap & bit); + bfs_assert(bitmap & bit); unsigned int index = count_ones(bitmap & (bit - 1)); // Advance the parent pointer, unless this node had only one child @@ -669,7 +668,7 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { child = &node->children[index]; } - assert(trie_decode_leaf(*child) == leaf); + bfs_assert(trie_decode_leaf(*child) == leaf); if (!parent) { trie_free_singletons(trie, trie->root); @@ -683,7 +682,7 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { node->bitmap ^= child_bit; unsigned int parent_size = count_ones(node->bitmap); - assert(parent_size > 0); + bfs_assert(parent_size > 0); if (parent_size == 1 && trie_collapse_node(parent, node, child_index) == 0) { return; } diff --git a/src/xregex.c b/src/xregex.c index a7153b7..1143f23 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -3,7 +3,7 @@ #include "xregex.h" #include "config.h" -#include +#include "diag.h" #include #include #include @@ -140,7 +140,7 @@ int bfs_regcomp(struct bfs_regex **preg, const char *pattern, enum bfs_regex_typ syntax = ONIG_SYNTAX_GREP; break; } - assert(syntax); + bfs_assert(syntax); OnigOptionType options = syntax->options; if (flags & BFS_REGEX_ICASE) { diff --git a/tests/bfstd.c b/tests/bfstd.c index a986a23..1812a00 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,10 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD -#undef NDEBUG #include "../src/bfstd.h" #include "../src/config.h" -#include +#include "../src/diag.h" #include #include #include @@ -39,11 +38,11 @@ int main(void) { alignas(64) int foo; int bar[]; }; - assert(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); - assert(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); - assert(flex_sizeof(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) - == align_floor(alignof(struct flexible), SIZE_MAX)); - assert(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); + bfs_verify(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); + bfs_verify(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); + bfs_verify(flex_sizeof(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) + == align_floor(alignof(struct flexible), SIZE_MAX)); + bfs_verify(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); // From man 3p basename check_base_dir("usr", ".", "usr"); diff --git a/tests/bit.c b/tests/bit.c index 7a7b0f3..cb339f4 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -1,11 +1,8 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD -#undef NDEBUG - #include "../src/bit.h" #include "../src/diag.h" -#include #include #include #include @@ -54,68 +51,71 @@ bfs_static_assert(UINTMAX_MAX == UWIDTH_MAX(UINTMAX_WIDTH)); bfs_static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH)); bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); +#define verify_eq(a, b) \ + bfs_verify((a) == (b), "(0x%jX) %s != %s (0x%jX)", (uintmax_t)(a), #a, #b, (uintmax_t)(b)) + int main(void) { - assert(bswap((uint8_t)0x12) == 0x12); - assert(bswap((uint16_t)0x1234) == 0x3412); - assert(bswap((uint32_t)0x12345678) == 0x78563412); - assert(bswap((uint64_t)0x1234567812345678) == 0x7856341278563412); - - assert(count_ones(0x0) == 0); - assert(count_ones(0x1) == 1); - assert(count_ones(0x2) == 1); - assert(count_ones(0x3) == 2); - assert(count_ones(0x137F) == 10); - - assert(count_zeros(0) == INT_WIDTH); - assert(count_zeros(0L) == LONG_WIDTH); - assert(count_zeros(0LL) == LLONG_WIDTH); - assert(count_zeros((uint8_t)0) == 8); - assert(count_zeros((uint16_t)0) == 16); - assert(count_zeros((uint32_t)0) == 32); - assert(count_zeros((uint64_t)0) == 64); - - assert(rotate_left((uint8_t)0xA1, 4) == 0x1A); - assert(rotate_left((uint16_t)0x1234, 12) == 0x4123); - assert(rotate_left((uint32_t)0x12345678, 20) == 0x67812345); - assert(rotate_left((uint32_t)0x12345678, 0) == 0x12345678); - - assert(rotate_right((uint8_t)0xA1, 4) == 0x1A); - assert(rotate_right((uint16_t)0x1234, 12) == 0x2341); - assert(rotate_right((uint32_t)0x12345678, 20) == 0x45678123); - assert(rotate_right((uint32_t)0x12345678, 0) == 0x12345678); + verify_eq(bswap((uint8_t)0x12), 0x12); + verify_eq(bswap((uint16_t)0x1234), 0x3412); + verify_eq(bswap((uint32_t)0x12345678), 0x78563412); + verify_eq(bswap((uint64_t)0x1234567812345678), 0x7856341278563412); + + verify_eq(count_ones(0x0), 0); + verify_eq(count_ones(0x1), 1); + verify_eq(count_ones(0x2), 1); + verify_eq(count_ones(0x3), 2); + verify_eq(count_ones(0x137F), 10); + + verify_eq(count_zeros(0), INT_WIDTH); + verify_eq(count_zeros(0L), LONG_WIDTH); + verify_eq(count_zeros(0LL), LLONG_WIDTH); + verify_eq(count_zeros((uint8_t)0), 8); + verify_eq(count_zeros((uint16_t)0), 16); + verify_eq(count_zeros((uint32_t)0), 32); + verify_eq(count_zeros((uint64_t)0), 64); + + verify_eq(rotate_left((uint8_t)0xA1, 4), 0x1A); + verify_eq(rotate_left((uint16_t)0x1234, 12), 0x4123); + verify_eq(rotate_left((uint32_t)0x12345678, 20), 0x67812345); + verify_eq(rotate_left((uint32_t)0x12345678, 0), 0x12345678); + + verify_eq(rotate_right((uint8_t)0xA1, 4), 0x1A); + verify_eq(rotate_right((uint16_t)0x1234, 12), 0x2341); + verify_eq(rotate_right((uint32_t)0x12345678, 20), 0x45678123); + verify_eq(rotate_right((uint32_t)0x12345678, 0), 0x12345678); for (int i = 0; i < 16; ++i) { uint16_t n = (uint16_t)1 << i; for (int j = i; j < 16; ++j) { uint16_t m = (uint16_t)1 << j; uint16_t nm = n | m; - assert(count_ones(nm) == 1 + (n != m)); - assert(count_zeros(nm) == 15 - (n != m)); - assert(leading_zeros(nm) == 15 - j); - assert(trailing_zeros(nm) == i); - assert(first_leading_one(nm) == j + 1); - assert(first_trailing_one(nm) == i + 1); - assert(bit_width(nm) == j + 1); - assert(bit_floor(nm) == m); + verify_eq(count_ones(nm), 1 + (n != m)); + verify_eq(count_zeros(nm), 15 - (n != m)); + verify_eq(leading_zeros(nm), 15 - j); + verify_eq(trailing_zeros(nm), i); + verify_eq(first_leading_one(nm), j + 1); + verify_eq(first_trailing_one(nm), i + 1); + verify_eq(bit_width(nm), j + 1); + verify_eq(bit_floor(nm), m); if (n == m) { - assert(bit_ceil(nm) == m); - assert(has_single_bit(nm)); + verify_eq(bit_ceil(nm), m); + bfs_verify(has_single_bit(nm)); } else { if (j < 15) { - assert(bit_ceil(nm) == (m << 1)); + verify_eq(bit_ceil(nm), (m << 1)); } - assert(!has_single_bit(nm)); + bfs_verify(!has_single_bit(nm)); } } } - assert(leading_zeros((uint16_t)0) == 16); - assert(trailing_zeros((uint16_t)0) == 16); - assert(first_leading_one(0) == 0); - assert(first_trailing_one(0) == 0); - assert(bit_width(0) == 0); - assert(bit_floor(0) == 0); - assert(bit_ceil(0) == 1); + verify_eq(leading_zeros((uint16_t)0), 16); + verify_eq(trailing_zeros((uint16_t)0), 16); + verify_eq(first_leading_one(0), 0); + verify_eq(first_trailing_one(0), 0); + verify_eq(bit_width(0), 0); + verify_eq(bit_floor(0), 0); + verify_eq(bit_ceil(0), 1); return EXIT_SUCCESS; } diff --git a/tests/trie.c b/tests/trie.c index ced14d4..e687f96 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,11 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD -#undef NDEBUG - #include "../src/trie.h" #include "../src/config.h" -#include +#include "../src/diag.h" #include #include @@ -45,7 +43,7 @@ int main(void) { trie_init(&trie); for (size_t i = 0; i < nkeys; ++i) { - assert(!trie_find_str(&trie, keys[i])); + bfs_verify(!trie_find_str(&trie, keys[i])); const char *prefix = NULL; for (size_t j = 0; j < i; ++j) { @@ -58,38 +56,38 @@ int main(void) { struct trie_leaf *leaf = trie_find_prefix(&trie, keys[i]); if (prefix) { - assert(leaf); - assert(strcmp(prefix, leaf->key) == 0); + bfs_verify(leaf); + bfs_verify(strcmp(prefix, leaf->key) == 0); } else { - assert(!leaf); + bfs_verify(!leaf); } leaf = trie_insert_str(&trie, keys[i]); - assert(leaf); - assert(strcmp(keys[i], leaf->key) == 0); - assert(leaf->length == strlen(keys[i]) + 1); + bfs_verify(leaf); + bfs_verify(strcmp(keys[i], leaf->key) == 0); + bfs_verify(leaf->length == strlen(keys[i]) + 1); } { size_t i = 0; TRIE_FOR_EACH(&trie, leaf) { - assert(leaf == trie_find_str(&trie, keys[i])); - assert(!leaf->prev || leaf->prev->next == leaf); - assert(!leaf->next || leaf->next->prev == leaf); + bfs_verify(leaf == trie_find_str(&trie, keys[i])); + bfs_verify(!leaf->prev || leaf->prev->next == leaf); + bfs_verify(!leaf->next || leaf->next->prev == leaf); ++i; } - assert(i == nkeys); + bfs_verify(i == nkeys); } for (size_t i = 0; i < nkeys; ++i) { struct trie_leaf *leaf = trie_find_str(&trie, keys[i]); - assert(leaf); - assert(strcmp(keys[i], leaf->key) == 0); - assert(leaf->length == strlen(keys[i]) + 1); + bfs_verify(leaf); + bfs_verify(strcmp(keys[i], leaf->key) == 0); + bfs_verify(leaf->length == strlen(keys[i]) + 1); trie_remove(&trie, leaf); leaf = trie_find_str(&trie, keys[i]); - assert(!leaf); + bfs_verify(!leaf); const char *postfix = NULL; for (size_t j = i + 1; j < nkeys; ++j) { @@ -102,33 +100,33 @@ int main(void) { leaf = trie_find_postfix(&trie, keys[i]); if (postfix) { - assert(leaf); - assert(strcmp(postfix, leaf->key) == 0); + bfs_verify(leaf); + bfs_verify(strcmp(postfix, leaf->key) == 0); } else { - assert(!leaf); + bfs_verify(!leaf); } } TRIE_FOR_EACH(&trie, leaf) { - assert(false); + bfs_verify(false); } // This tests the "jump" node handling on 32-bit platforms size_t longsize = 1 << 20; char *longstr = malloc(longsize); - assert(longstr); + bfs_verify(longstr); memset(longstr, 0xAC, longsize); - assert(!trie_find_mem(&trie, longstr, longsize)); - assert(trie_insert_mem(&trie, longstr, longsize)); + bfs_verify(!trie_find_mem(&trie, longstr, longsize)); + bfs_verify(trie_insert_mem(&trie, longstr, longsize)); memset(longstr + longsize/2, 0xAB, longsize/2); - assert(!trie_find_mem(&trie, longstr, longsize)); - assert(trie_insert_mem(&trie, longstr, longsize)); + bfs_verify(!trie_find_mem(&trie, longstr, longsize)); + bfs_verify(trie_insert_mem(&trie, longstr, longsize)); memset(longstr, 0xAA, longsize/2); - assert(!trie_find_mem(&trie, longstr, longsize)); - assert(trie_insert_mem(&trie, longstr, longsize)); + bfs_verify(!trie_find_mem(&trie, longstr, longsize)); + bfs_verify(trie_insert_mem(&trie, longstr, longsize)); free(longstr); trie_destroy(&trie); -- cgit v1.2.3 From a5550d478234efbb89d1c23ae3234bed626a47f6 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 16 Jun 2023 10:21:53 -0400 Subject: opt: Wait until purity is computed to update facts_when_impure Since we moved purity out of the parser, side-effect detection has been unnecessarily pessimistic due to this bug. The fix restores warnings in cases like $ bfs -false bfs: warning: This command won't do anything. Fixes: 693b5f60dc9787d9237920cc0c87fe0e010194ee --- src/opt.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index 4699af4..14de081 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1225,10 +1225,6 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct return ret; } - if (!bfs_expr_is_parent(expr) && !expr->pure) { - facts_union(state->facts_when_impure, state->facts_when_impure, &state->facts); - } - expr = optimize_expr_lookup(state, expr); if (!expr) { return NULL; @@ -1247,6 +1243,8 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct expr->ephemeral_fds = lhs->ephemeral_fds; } } + } else if (!expr->pure) { + facts_union(state->facts_when_impure, state->facts_when_impure, &state->facts); } if (expr->always_true) { -- cgit v1.2.3 From 52de184ba28551734e1cb13233588504ab5f62ec Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 27 Sep 2023 12:11:15 -0400 Subject: Formatting fixes --- src/color.c | 6 +++--- src/ctx.c | 1 - src/darray.c | 4 ++-- src/diag.c | 8 ++++---- src/diag.h | 2 +- src/dstring.c | 4 ++-- src/eval.c | 15 +++++++-------- src/exec.c | 16 ++++++++-------- src/fsade.c | 6 +++--- src/ioq.c | 2 +- src/opt.c | 11 +++++------ src/opt.h | 1 - src/parse.c | 45 ++++++++++++++++++++++----------------------- src/printf.c | 46 +++++++++++++++++++++++----------------------- src/pwcache.c | 1 - src/stat.c | 2 +- src/trie.c | 2 +- src/trie.h | 4 ++-- src/xregex.c | 2 +- src/xspawn.c | 2 +- src/xtime.c | 12 ++++++------ tests/bfstd.c | 2 +- tests/mksock.c | 2 +- tests/trie.c | 4 ++-- tests/xtimegm.c | 6 +++--- 25 files changed, 100 insertions(+), 106 deletions(-) (limited to 'src/opt.c') diff --git a/src/color.c b/src/color.c index 8d0b995..788d35d 100644 --- a/src/color.c +++ b/src/color.c @@ -1143,11 +1143,11 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { if (verbose) { double rate = 0.0, time = 0.0; if (expr->evaluations) { - rate = 100.0*expr->successes/expr->evaluations; - time = (1.0e9*expr->elapsed.tv_sec + expr->elapsed.tv_nsec)/expr->evaluations; + rate = 100.0 * expr->successes / expr->evaluations; + time = (1.0e9 * expr->elapsed.tv_sec + expr->elapsed.tv_nsec) / expr->evaluations; } if (cbuff(cfile, " [${ylw}%zu${rs}/${ylw}%zu${rs}=${ylw}%g%%${rs}; ${ylw}%gns${rs}]", - expr->successes, expr->evaluations, rate, time)) { + expr->successes, expr->evaluations, rate, time)) { return -1; } } diff --git a/src/ctx.c b/src/ctx.c index 3a44e68..9a24a33 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -168,7 +168,6 @@ void bfs_ctx_flush(const struct bfs_ctx *ctx) { } else if (cfile == ctx->cout) { bfs_error(ctx, "(standard output): %m.\n"); } - } // Flush the user/group caches, in case the executed command edits the diff --git a/src/darray.c b/src/darray.c index 42b8397..3e66a55 100644 --- a/src/darray.c +++ b/src/darray.c @@ -55,7 +55,7 @@ void *darray_push(void *da, const void *item, size_t size) { size_t i = header->length++; if (i >= capacity) { capacity *= 2; - header = realloc(header, sizeof(*header) + capacity*size); + header = realloc(header, sizeof(*header) + capacity * size); if (!header) { // This failure will be detected by darray_check() return da; @@ -64,7 +64,7 @@ void *darray_push(void *da, const void *item, size_t size) { } char *data = darray_data(header); - memcpy(data + i*size, item, size); + memcpy(data + i * size, item, size); return data; } diff --git a/src/diag.c b/src/diag.c index fa9db39..fa66525 100644 --- a/src/diag.c +++ b/src/diag.c @@ -4,9 +4,9 @@ #include "diag.h" #include "alloc.h" #include "bfstd.h" -#include "ctx.h" #include "color.h" #include "config.h" +#include "ctx.h" #include "dstring.h" #include "expr.h" #include @@ -31,14 +31,14 @@ void bfs_perror(const struct bfs_ctx *ctx, const char *str) { bfs_error(ctx, "%s: %m.\n", str); } -void bfs_error(const struct bfs_ctx *ctx, const char *format, ...) { +void bfs_error(const struct bfs_ctx *ctx, const char *format, ...) { va_list args; va_start(args, format); bfs_verror(ctx, format, args); va_end(args); } -bool bfs_warning(const struct bfs_ctx *ctx, 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(ctx, format, args); @@ -46,7 +46,7 @@ bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...) { return ret; } -bool bfs_debug(const struct bfs_ctx *ctx, 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(ctx, flag, format, args); diff --git a/src/diag.h b/src/diag.h index e019db0..fea8847 100644 --- a/src/diag.h +++ b/src/diag.h @@ -8,8 +8,8 @@ #ifndef BFS_DIAG_H #define BFS_DIAG_H -#include "ctx.h" #include "config.h" +#include "ctx.h" #include /** diff --git a/src/dstring.c b/src/dstring.c index ef4e733..f947741 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -169,7 +169,7 @@ char *dstrprintf(const char *format, ...) { char *dstrvprintf(const char *format, va_list args) { // Guess a capacity to try to avoid reallocating - dchar *str = dstralloc(2*strlen(format)); + dchar *str = dstralloc(2 * strlen(format)); if (!str) { return NULL; } @@ -195,7 +195,7 @@ int dstrcatf(dchar **str, const char *format, ...) { int dstrvcatf(dchar **str, const char *format, va_list args) { // Guess a capacity to try to avoid calling vsnprintf() twice size_t len = dstrlen(*str); - dstreserve(str, len + 2*strlen(format)); + dstreserve(str, len + 2 * strlen(format)); size_t cap = dstrheader(*str)->capacity; va_list copy; diff --git a/src/eval.c b/src/eval.c index 3550751..9f4896a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -239,7 +239,7 @@ bool eval_time(const struct bfs_expr *expr, struct bfs_eval *state) { time_t diff = timespec_diff(&expr->reftime, time); switch (expr->time_unit) { case BFS_DAYS: - diff /= 60*24; + diff /= 60 * 24; fallthru; case BFS_MINUTES: diff /= 60; @@ -271,7 +271,7 @@ bool eval_used(const struct bfs_expr *expr, struct bfs_eval *state) { return false; } - long long day_seconds = 60*60*24; + long long day_seconds = 60 * 60 * 24; diff = (diff + day_seconds - 1) / day_seconds; return bfs_expr_cmp(expr, diff); } @@ -685,7 +685,7 @@ bool eval_fls(const struct bfs_expr *expr, struct bfs_eval *state) { uintmax_t ino = statbuf->ino; uintmax_t block_size = ctx->posixly_correct ? 512 : 1024; - uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + block_size - 1)/block_size; + uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + block_size - 1) / block_size; char mode[11]; xstrmode(statbuf->mode, mode); char acl = bfs_check_acl(ftwbuf) > 0 ? '+' : ' '; @@ -721,8 +721,8 @@ bool eval_fls(const struct bfs_expr *expr, struct bfs_eval *state) { time_t time = statbuf->mtime.tv_sec; time_t now = ctx->now.tv_sec; - time_t six_months_ago = now - 6*30*24*60*60; - time_t tomorrow = now + 24*60*60; + time_t six_months_ago = now - 6 * 30 * 24 * 60 * 60; + time_t tomorrow = now + 24 * 60 * 60; struct tm tm; if (xlocaltime(&time, &tm) != 0) { goto error; @@ -823,7 +823,6 @@ bool eval_fprintx(const struct bfs_expr *expr, struct bfs_eval *state) { ++path; } - if (fputc('\n', file) == EOF) { goto error; } @@ -905,7 +904,7 @@ bool eval_size(const struct bfs_expr *expr, struct bfs_eval *state) { }; off_t scale = scales[expr->size_unit]; - off_t size = (statbuf->size + scale - 1)/scale; // Round up + off_t size = (statbuf->size + scale - 1) / scale; // Round up return bfs_expr_cmp(expr, size); } @@ -918,7 +917,7 @@ bool eval_sparse(const struct bfs_expr *expr, struct bfs_eval *state) { return false; } - blkcnt_t expected = (statbuf->size + BFS_STAT_BLKSIZE - 1)/BFS_STAT_BLKSIZE; + blkcnt_t expected = (statbuf->size + BFS_STAT_BLKSIZE - 1) / BFS_STAT_BLKSIZE; return statbuf->blocks < expected; } diff --git a/src/exec.c b/src/exec.c index 7b55522..0e0d585 100644 --- a/src/exec.c +++ b/src/exec.c @@ -5,9 +5,9 @@ #include "alloc.h" #include "bfstd.h" #include "bftw.h" -#include "ctx.h" #include "color.h" #include "config.h" +#include "ctx.h" #include "diag.h" #include "dstring.h" #include "xspawn.h" @@ -348,7 +348,7 @@ static int bfs_exec_spawn(const struct bfs_exec *execbuf) { if (execbuf->flags & BFS_EXEC_MULTI) { bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments] (size %zu)\n", - execbuf->argv[0], execbuf->argc - 1, execbuf->arg_size); + execbuf->argv[0], execbuf->argc - 1, execbuf->arg_size); } else { bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments]\n", execbuf->argv[0], execbuf->argc - 1); } @@ -471,7 +471,7 @@ static bool bfs_exec_args_remain(const struct bfs_exec *execbuf) { static size_t bfs_exec_estimate_max(const struct bfs_exec *execbuf) { size_t min = execbuf->arg_min; size_t max = execbuf->arg_max; - return min + (max - min)/2; + return min + (max - min) / 2; } /** Update the ARG_MAX lower bound from a successful execution. */ @@ -486,7 +486,7 @@ static void bfs_exec_update_min(struct bfs_exec *execbuf) { size_t estimate = bfs_exec_estimate_max(execbuf); bfs_exec_debug(execbuf, "ARG_MAX between [%zu, %zu], trying %zu\n", - execbuf->arg_min, execbuf->arg_max, estimate); + execbuf->arg_min, execbuf->arg_max, estimate); } } @@ -502,7 +502,7 @@ static size_t bfs_exec_update_max(struct bfs_exec *execbuf) { // Trim a fraction off the max size to avoid repeated failures near the // top end of the working range - size -= size/16; + size -= size / 16; if (size < execbuf->arg_max) { execbuf->arg_max = size; @@ -515,7 +515,7 @@ static size_t bfs_exec_update_max(struct bfs_exec *execbuf) { // Binary search for a more precise bound size_t estimate = bfs_exec_estimate_max(execbuf); bfs_exec_debug(execbuf, "ARG_MAX between [%zu, %zu], trying %zu\n", - execbuf->arg_min, execbuf->arg_max, estimate); + execbuf->arg_min, execbuf->arg_max, estimate); return estimate; } @@ -589,7 +589,7 @@ static bool bfs_exec_would_overflow(const struct bfs_exec *execbuf, const char * size_t next_size = execbuf->arg_size + bfs_exec_arg_size(arg); if (next_size > arg_max) { bfs_exec_debug(execbuf, "Command size (%zu) would exceed maximum (%zu), executing buffered command\n", - next_size, arg_max); + next_size, arg_max); return true; } @@ -601,7 +601,7 @@ static int bfs_exec_push(struct bfs_exec *execbuf, char *arg) { execbuf->argv[execbuf->argc] = arg; if (execbuf->argc + 1 >= execbuf->argv_cap) { - size_t cap = 2*execbuf->argv_cap; + size_t cap = 2 * execbuf->argv_cap; char **argv = realloc(execbuf->argv, sizeof_array(char *, cap)); if (!argv) { return -1; diff --git a/src/fsade.c b/src/fsade.c index 8dec5a8..cbff47b 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -3,9 +3,9 @@ #include "fsade.h" #include "atomic.h" -#include "config.h" #include "bfstd.h" #include "bftw.h" +#include "config.h" #include "dir.h" #include "dstring.h" #include "sanity.h" @@ -292,7 +292,7 @@ int bfs_check_xattrs(const struct BFTW *ftwbuf) { ssize_t len; #if BFS_USE_SYS_EXTATTR_H - ssize_t (*extattr_list)(const char *, int, void*, size_t) = + ssize_t (*extattr_list)(const char *, int, void *, size_t) = ftwbuf->type == BFS_LNK ? extattr_list_link : extattr_list_file; len = extattr_list(path, EXTATTR_NAMESPACE_SYSTEM, NULL, 0); @@ -331,7 +331,7 @@ int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name) { ssize_t len; #if BFS_USE_SYS_EXTATTR_H - ssize_t (*extattr_get)(const char *, int, const char *, void*, size_t) = + ssize_t (*extattr_get)(const char *, int, const char *, void *, size_t) = ftwbuf->type == BFS_LNK ? extattr_get_link : extattr_get_file; len = extattr_get(path, EXTATTR_NAMESPACE_SYSTEM, name, NULL, 0); diff --git a/src/ioq.c b/src/ioq.c index f7ca8c6..d3ba2de 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -9,8 +9,8 @@ #include "config.h" #include "diag.h" #include "dir.h" -#include "thread.h" #include "sanity.h" +#include "thread.h" #include #include #include diff --git a/src/opt.c b/src/opt.c index 14de081..77c2798 100644 --- a/src/opt.c +++ b/src/opt.c @@ -535,8 +535,8 @@ static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct expr->pure = lhs->pure && rhs->pure; expr->always_true = lhs->always_true && rhs->always_true; expr->always_false = lhs->always_false || rhs->always_false; - expr->cost = lhs->cost + lhs->probability*rhs->cost; - expr->probability = lhs->probability*rhs->probability; + expr->cost = lhs->cost + lhs->probability * rhs->cost; + expr->probability = lhs->probability * rhs->probability; return expr; } @@ -606,8 +606,8 @@ static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct b expr->pure = lhs->pure && rhs->pure; expr->always_true = lhs->always_true || rhs->always_true; expr->always_false = lhs->always_false && rhs->always_false; - expr->cost = lhs->cost + (1 - lhs->probability)*rhs->cost; - expr->probability = lhs->probability + rhs->probability - lhs->probability*rhs->probability; + expr->cost = lhs->cost + (1 - lhs->probability) * rhs->cost; + expr->probability = lhs->probability + rhs->probability - lhs->probability * rhs->probability; return expr; } @@ -1078,7 +1078,6 @@ static const struct { {eval_xattrname, 0.01}, }; - /** * Table of simple predicates. */ @@ -1331,7 +1330,7 @@ static bool reorder_expr_recursive(const struct opt_state *state, struct bfs_exp if (expr->eval_fn == eval_and || expr->eval_fn == eval_or) { if (lhs->pure && rhs->pure) { float rhs_prob = expr->eval_fn == eval_and ? rhs->probability : 1.0 - rhs->probability; - float swapped_cost = rhs->cost + rhs_prob*lhs->cost; + float swapped_cost = rhs->cost + rhs_prob * lhs->cost; ret |= reorder_expr(state, expr, swapped_cost); } } diff --git a/src/opt.h b/src/opt.h index 28cadb9..4aac129 100644 --- a/src/opt.h +++ b/src/opt.h @@ -21,4 +21,3 @@ struct bfs_ctx; int bfs_optimize(struct bfs_ctx *ctx); #endif // BFS_OPT_H - diff --git a/src/parse.c b/src/parse.c index 3416d9e..7766a7b 100644 --- a/src/parse.c +++ b/src/parse.c @@ -41,8 +41,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -1797,10 +1797,10 @@ static int parse_reftime(const struct parser_state *state, struct bfs_expr *expr #else int gmtoff = -timezone; #endif - int tz_hour = gmtoff/3600; - int tz_min = (labs(gmtoff)/60)%60; + int tz_hour = gmtoff / 3600; + int tz_min = (labs(gmtoff) / 60) % 60; fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d%+03d:%02d\n", - year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min); + year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min); if (xgmtime(&state->now.tv_sec, &tm) != 0) { parse_perror(state, "xgmtime()"); @@ -1832,8 +1832,8 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int expr->stat_field = parse_newerxy_field(arg[6]); if (!expr->stat_field) { parse_expr_error(state, expr, - "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n", - arg[6]); + "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n", + arg[6]); goto fail; } @@ -1845,8 +1845,8 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int enum bfs_stat_field field = parse_newerxy_field(arg[7]); if (!field) { parse_expr_error(state, expr, - "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n", - arg[7]); + "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n", + arg[7]); goto fail; } @@ -1855,7 +1855,6 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int goto fail; } - const struct timespec *reftime = bfs_stat_time(&sb, field); if (!reftime) { parse_expr_error(state, expr, "Couldn't get file %s.\n", bfs_stat_field_name(field)); @@ -1905,7 +1904,7 @@ static struct bfs_expr *parse_nohidden(struct parser_state *state, int arg1, int */ static struct bfs_expr *parse_noleaf(struct parser_state *state, int arg1, int arg2) { parse_warning(state, "${ex}%s${rs} does not apply the optimization that ${blu}%s${rs} inhibits.\n\n", - BFS_COMMAND, state->argv[0]); + BFS_COMMAND, state->argv[0]); return parse_nullary_option(state); } @@ -2730,12 +2729,12 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg } cfprintf(cout, "Usage: ${ex}%s${rs} [${cyn}flags${rs}...] [${mag}paths${rs}...] [${blu}expression${rs}...]\n\n", - state->command); + state->command); cfprintf(cout, "${ex}%s${rs} is compatible with ${ex}find${rs}, with some extensions. " - "${cyn}Flags${rs} (${cyn}-H${rs}/${cyn}-L${rs}/${cyn}-P${rs} etc.), ${mag}paths${rs},\n" - "and ${blu}expressions${rs} may be freely mixed in any order.\n\n", - BFS_COMMAND); + "${cyn}Flags${rs} (${cyn}-H${rs}/${cyn}-L${rs}/${cyn}-P${rs} etc.), ${mag}paths${rs},\n" + "and ${blu}expressions${rs} may be freely mixed in any order.\n\n", + BFS_COMMAND); cfprintf(cout, "${bld}Flags:${rs}\n\n"); @@ -2807,7 +2806,7 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg cfprintf(cout, " ${blu}-ignore_readdir_race${rs}\n"); cfprintf(cout, " ${blu}-noignore_readdir_race${rs}\n"); cfprintf(cout, " Whether to report an error if ${ex}%s${rs} detects that the file tree is modified\n", - BFS_COMMAND); + BFS_COMMAND); cfprintf(cout, " during the search (default: ${blu}-noignore_readdir_race${rs})\n"); cfprintf(cout, " ${blu}-maxdepth${rs} ${bld}N${rs}\n"); cfprintf(cout, " ${blu}-mindepth${rs} ${bld}N${rs}\n"); @@ -3462,14 +3461,14 @@ static struct bfs_expr *parse_whole_expr(struct parser_state *state) { if (state->mount_arg && state->xdev_arg) { parse_conflict_warning(state, state->mount_arg, 1, state->xdev_arg, 1, - "${blu}%s${rs} is redundant in the presence of ${blu}%s${rs}.\n\n", - state->xdev_arg[0], state->mount_arg[0]); + "${blu}%s${rs} is redundant in the presence of ${blu}%s${rs}.\n\n", + state->xdev_arg[0], state->mount_arg[0]); } if (state->ctx->warn && state->depth_arg && state->prune_arg) { parse_conflict_warning(state, state->depth_arg, 1, state->prune_arg, 1, - "${blu}%s${rs} does not work in the presence of ${blu}%s${rs}.\n", - state->prune_arg[0], state->depth_arg[0]); + "${blu}%s${rs} does not work in the presence of ${blu}%s${rs}.\n", + state->prune_arg[0], state->depth_arg[0]); if (state->interactive) { bfs_warning(state->ctx, "Do you want to continue? "); @@ -3483,8 +3482,8 @@ static struct bfs_expr *parse_whole_expr(struct parser_state *state) { if (state->ok_expr && state->files0_stdin_arg) { parse_conflict_error(state, state->ok_expr->argv, state->ok_expr->argc, state->files0_stdin_arg, 2, - "${blu}%s${rs} conflicts with ${blu}%s${rs} ${bld}%s${rs}.\n", - state->ok_expr->argv[0], state->files0_stdin_arg[0], state->files0_stdin_arg[1]); + "${blu}%s${rs} conflicts with ${blu}%s${rs} ${bld}%s${rs}.\n", + state->ok_expr->argv[0], state->files0_stdin_arg[0], state->files0_stdin_arg[1]); goto fail; } @@ -3644,7 +3643,7 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { static void dump_costs(const struct bfs_ctx *ctx) { const struct bfs_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); + bfs_debug(ctx, DEBUG_COST, "Probability: ~${ylw}%g%%${rs}\n", 100.0 * expr->probability); } struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { @@ -3654,7 +3653,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { goto fail; } - static char* default_argv[] = {BFS_COMMAND, NULL}; + static char *default_argv[] = {BFS_COMMAND, NULL}; if (argc < 1) { argc = 1; argv = default_argv; diff --git a/src/printf.c b/src/printf.c index 5de5a28..98bcb0f 100644 --- a/src/printf.c +++ b/src/printf.c @@ -113,14 +113,14 @@ static int bfs_printf_ctime(CFILE *cfile, const struct bfs_printf *directive, co } BFS_PRINTF_BUF(buf, "%s %s %2d %.2d:%.2d:%.2d.%09ld0 %4d", - days[tm.tm_wday], - months[tm.tm_mon], - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - (long)ts->tv_nsec, - 1900 + tm.tm_year); + days[tm.tm_wday], + months[tm.tm_mon], + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (long)ts->tv_nsec, + 1900 + tm.tm_year); return dyn_fprintf(cfile->file, directive, buf); } @@ -152,19 +152,19 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, break; case '+': ret = snprintf(buf, sizeof(buf), "%4d-%.2d-%.2d+%.2d:%.2d:%.2d.%09ld0", - 1900 + tm.tm_year, - tm.tm_mon + 1, - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - (long)ts->tv_nsec); + 1900 + tm.tm_year, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (long)ts->tv_nsec); break; case 'k': ret = snprintf(buf, sizeof(buf), "%2d", tm.tm_hour); break; case 'l': - ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11)%12 + 1); + ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11) % 12 + 1); break; case 's': ret = snprintf(buf, sizeof(buf), "%lld", (long long)ts->tv_sec); @@ -174,10 +174,10 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, break; case 'T': ret = snprintf(buf, sizeof(buf), "%.2d:%.2d:%.2d.%09ld0", - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - (long)ts->tv_nsec); + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (long)ts->tv_nsec); break; // POSIX strftime() features @@ -202,7 +202,7 @@ static int bfs_printf_b(CFILE *cfile, const struct bfs_printf *directive, const return -1; } - uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 511)/512; + uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 511) / 512; BFS_PRINTF_BUF(buf, "%ju", blocks); return dyn_fprintf(cfile->file, directive, buf); } @@ -338,7 +338,7 @@ static int bfs_printf_k(CFILE *cfile, const struct bfs_printf *directive, const return -1; } - uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 1023)/1024; + uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 1023) / 1024; BFS_PRINTF_BUF(buf, "%ju", blocks); return dyn_fprintf(cfile->file, directive, buf); } @@ -452,7 +452,7 @@ static int bfs_printf_S(CFILE *cfile, const struct bfs_printf *directive, const if (statbuf->size == 0 && statbuf->blocks == 0) { sparsity = 1.0; } else { - sparsity = (double)BFS_STAT_BLKSIZE*statbuf->blocks/statbuf->size; + sparsity = (double)BFS_STAT_BLKSIZE * statbuf->blocks / statbuf->size; } return dyn_fprintf(cfile->file, directive, sparsity); } diff --git a/src/pwcache.c b/src/pwcache.c index 0e2f5c1..c728ba9 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -4,7 +4,6 @@ #include "pwcache.h" #include "alloc.h" #include "config.h" -#include "darray.h" #include "trie.h" #include #include diff --git a/src/stat.c b/src/stat.c index e8f48ee..d7387c6 100644 --- a/src/stat.c +++ b/src/stat.c @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include #if defined(STATX_BASIC_STATS) && (!__ANDROID__ || __ANDROID_API__ >= 30) # define BFS_HAS_LIBC_STATX true diff --git a/src/trie.c b/src/trie.c index 77aa2d0..55544e6 100644 --- a/src/trie.c +++ b/src/trie.c @@ -700,7 +700,7 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { } if (child_index < parent_size) { - memmove(child, child + 1, (parent_size - child_index)*sizeof(*child)); + memmove(child, child + 1, (parent_size - child_index) * sizeof(*child)); } if (has_single_bit(parent_size)) { diff --git a/src/trie.h b/src/trie.h index 2f51db5..02088f1 100644 --- a/src/trie.h +++ b/src/trie.h @@ -4,8 +4,8 @@ #ifndef BFS_TRIE_H #define BFS_TRIE_H -#include "config.h" #include "alloc.h" +#include "config.h" #include "list.h" #include #include @@ -143,6 +143,6 @@ void trie_destroy(struct trie *trie); * Iterate over the leaves of a trie. */ #define for_trie(leaf, trie) \ - for_list(struct trie_leaf, leaf, trie) + for_list (struct trie_leaf, leaf, trie) #endif // BFS_TRIE_H diff --git a/src/xregex.c b/src/xregex.c index beb6676..b9c04bf 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -5,8 +5,8 @@ #include "alloc.h" #include "config.h" #include "diag.h" -#include "thread.h" #include "sanity.h" +#include "thread.h" #include #include #include diff --git a/src/xspawn.c b/src/xspawn.c index 80bafef..7fb63e0 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -187,7 +187,7 @@ fail: // In case of a write error, the parent will still see that we exited // unsuccessfully, but won't know why - (void) xwrite(pipefd[1], &error, sizeof(error)); + (void)xwrite(pipefd[1], &error, sizeof(error)); xclose(pipefd[1]); _Exit(127); diff --git a/src/xtime.c b/src/xtime.c index 79dafad..e90bdb1 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -81,7 +81,7 @@ static int safe_add(int *value, int delta) { static int floor_div(int n, int d) { int a = n < 0; - return (n + a)/d - a; + return (n + a) / d - a; } static int wrap(int *value, int max, int *next) { @@ -93,7 +93,7 @@ static int wrap(int *value, int max, int *next) { static int month_length(int year, int month) { static const int month_lengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int ret = month_lengths[month]; - if (month == 1 && year%4 == 0 && (year%100 != 0 || (year + 300)%400 == 0)) { + if (month == 1 && year % 4 == 0 && (year % 100 != 0 || (year + 300) % 400 == 0)) { ++ret; } return ret; @@ -156,13 +156,13 @@ int xtimegm(struct tm *tm, time_t *timep) { leap_days = floor_div(tm->tm_year + 3, 4) - floor_div(tm->tm_year + 99, 100) + floor_div(tm->tm_year + 299, 400) - 17; } - long long epoch_days = 365LL*(tm->tm_year - 70) + leap_days + tm->tm_yday; - tm->tm_wday = (epoch_days + 4)%7; + long long epoch_days = 365LL * (tm->tm_year - 70) + leap_days + tm->tm_yday; + tm->tm_wday = (epoch_days + 4) % 7; if (tm->tm_wday < 0) { tm->tm_wday += 7; } - long long epoch_time = tm->tm_sec + 60*(tm->tm_min + 60*(tm->tm_hour + 24*epoch_days)); + long long epoch_time = tm->tm_sec + 60 * (tm->tm_min + 60 * (tm->tm_hour + 24 * epoch_days)); *timep = (time_t)epoch_time; if ((long long)*timep != epoch_time) { goto overflow; @@ -296,7 +296,7 @@ end: goto error; } - int offset = 60*tz_hour + tz_min; + int offset = 60 * tz_hour + tz_min; if (tz_negative) { result->tv_sec -= offset; } else { diff --git a/tests/bfstd.c b/tests/bfstd.c index 83964e5..33b3792 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -7,8 +7,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/tests/mksock.c b/tests/mksock.c index 42ef322..7023b4f 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -9,8 +9,8 @@ #include "../src/bfstd.h" #include #include -#include #include +#include #include #include #include diff --git a/tests/trie.c b/tests/trie.c index 6ea94a2..656fd85 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -120,11 +120,11 @@ int main(void) { bfs_verify(!trie_find_mem(&trie, longstr, longsize)); bfs_verify(trie_insert_mem(&trie, longstr, longsize)); - memset(longstr + longsize/2, 0xAB, longsize/2); + memset(longstr + longsize / 2, 0xAB, longsize / 2); bfs_verify(!trie_find_mem(&trie, longstr, longsize)); bfs_verify(trie_insert_mem(&trie, longstr, longsize)); - memset(longstr, 0xAA, longsize/2); + memset(longstr, 0xAA, longsize / 2); bfs_verify(!trie_find_mem(&trie, longstr, longsize)); bfs_verify(trie_insert_mem(&trie, longstr, longsize)); diff --git a/tests/xtimegm.c b/tests/xtimegm.c index b2479b7..973b2eb 100644 --- a/tests/xtimegm.c +++ b/tests/xtimegm.c @@ -42,9 +42,9 @@ static bool tm_equal(const struct tm *tma, const struct tm *tmb) { static void tm_print(FILE *file, const struct tm *tm) { fprintf(file, "Y%d M%d D%d h%d m%d s%d wd%d yd%d%s\n", - tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_wday, tm->tm_yday, + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_wday, tm->tm_yday, tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); } -- cgit v1.2.3 From 026e8fbd248561396752552efa3cc04e0ac832b7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 9 Nov 2023 12:59:20 -0500 Subject: config: s/BFS_FORMATTER/attr_format/ --- src/bar.c | 2 +- src/color.c | 4 ++-- src/color.h | 4 ++-- src/config.h | 4 ++-- src/diag.h | 14 +++++++------- src/dstring.h | 8 ++++---- src/eval.c | 2 +- src/exec.c | 2 +- src/opt.c | 4 ++-- src/parse.c | 14 +++++++------- 10 files changed, 29 insertions(+), 29 deletions(-) (limited to 'src/opt.c') diff --git a/src/bar.c b/src/bar.c index 57cc4dd..2e50dbe 100644 --- a/src/bar.c +++ b/src/bar.c @@ -127,7 +127,7 @@ static void reset_before_death_by(int sig) { } /** printf() to the status bar with a single write(). */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) static int bfs_bar_printf(struct bfs_bar *bar, const char *format, ...) { va_list args; va_start(args, format); diff --git a/src/color.c b/src/color.c index fbb5edf..7644ca3 100644 --- a/src/color.c +++ b/src/color.c @@ -1105,7 +1105,7 @@ static int print_link_target(CFILE *cfile, const struct BFTW *ftwbuf) { } /** Format some colored output to the buffer. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) static int cbuff(CFILE *cfile, const char *format, ...); /** Dump a parsed expression tree, for debugging. */ @@ -1177,7 +1177,7 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { return 0; } -BFS_FORMATTER(2, 0) +attr_format(2, 0) static int cvbuff(CFILE *cfile, const char *format, va_list args) { const struct colors *colors = cfile->colors; int error = errno; diff --git a/src/color.h b/src/color.h index b118f77..8a81573 100644 --- a/src/color.h +++ b/src/color.h @@ -100,13 +100,13 @@ int cfclose(CFILE *cfile); * @return * 0 on success, -1 on failure. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) int cfprintf(CFILE *cfile, const char *format, ...); /** * cfprintf() variant that takes a va_list. */ -BFS_FORMATTER(2, 0) +attr_format(2, 0) int cvfprintf(CFILE *cfile, const char *format, va_list args); #endif // BFS_COLOR_H diff --git a/src/config.h b/src/config.h index e11941a..f19677c 100644 --- a/src/config.h +++ b/src/config.h @@ -188,9 +188,9 @@ * Adds compiler warnings for bad printf()-style function calls, if supported. */ #if __has_attribute(format) -# define BFS_FORMATTER(fmt, args) __attribute__((format(printf, fmt, args))) +# define attr_format(fmt, args) __attribute__((format(printf, fmt, args))) #else -# define BFS_FORMATTER(fmt, args) +# define attr_format(fmt, args) #endif /** diff --git a/src/diag.h b/src/diag.h index 838a794..8c7ed57 100644 --- a/src/diag.h +++ b/src/diag.h @@ -44,7 +44,7 @@ struct bfs_loc { /** * Print a message to standard error and abort. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...); /** @@ -121,7 +121,7 @@ void bfs_perror(const struct bfs_ctx *ctx, const char *str); /** * Shorthand for printing error messages. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); /** @@ -129,7 +129,7 @@ void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); * * @return Whether a warning was printed. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); /** @@ -137,25 +137,25 @@ bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); * * @return Whether a debug message was printed. */ -BFS_FORMATTER(3, 4) +attr_format(3, 4) bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...); /** * bfs_error() variant that takes a va_list. */ -BFS_FORMATTER(2, 0) +attr_format(2, 0) void bfs_verror(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_warning() variant that takes a va_list. */ -BFS_FORMATTER(2, 0) +attr_format(2, 0) bool bfs_vwarning(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_debug() variant that takes a va_list. */ -BFS_FORMATTER(3, 0) +attr_format(3, 0) bool bfs_vdebug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, va_list args); /** diff --git a/src/dstring.h b/src/dstring.h index 6496a4f..fd98df8 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -230,7 +230,7 @@ int dstrxcpy(dchar **dest, const char *str, size_t len); * @return * The created string, or NULL on failure. */ -BFS_FORMATTER(1, 2) +attr_format(1, 2) char *dstrprintf(const char *format, ...); /** @@ -243,7 +243,7 @@ char *dstrprintf(const char *format, ...); * @return * The created string, or NULL on failure. */ -BFS_FORMATTER(1, 0) +attr_format(1, 0) char *dstrvprintf(const char *format, va_list args); /** @@ -258,7 +258,7 @@ char *dstrvprintf(const char *format, va_list args); * @return * 0 on success, -1 on failure. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) int dstrcatf(dchar **str, const char *format, ...); /** @@ -273,7 +273,7 @@ int dstrcatf(dchar **str, const char *format, ...); * @return * 0 on success, -1 on failure. */ -BFS_FORMATTER(2, 0) +attr_format(2, 0) int dstrvcatf(dchar **str, const char *format, va_list args); /** diff --git a/src/eval.c b/src/eval.c index b511eba..eb4a0ca 100644 --- a/src/eval.c +++ b/src/eval.c @@ -58,7 +58,7 @@ struct bfs_eval { /** * Print an error message. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) static void eval_error(struct bfs_eval *state, const char *format, ...) { // By POSIX, any errors should be accompanied by a non-zero exit status *state->ret = EXIT_FAILURE; diff --git a/src/exec.c b/src/exec.c index f0730d2..ba82439 100644 --- a/src/exec.c +++ b/src/exec.c @@ -22,7 +22,7 @@ #include /** Print some debugging info. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) static void bfs_exec_debug(const struct bfs_exec *execbuf, const char *format, ...) { const struct bfs_ctx *ctx = execbuf->ctx; diff --git a/src/opt.c b/src/opt.c index 77c2798..5da73d8 100644 --- a/src/opt.c +++ b/src/opt.c @@ -305,7 +305,7 @@ struct opt_state { }; /** Log an optimization. */ -BFS_FORMATTER(3, 4) +attr_format(3, 4) static bool opt_debug(const struct opt_state *state, int level, const char *format, ...) { bfs_assert(state->ctx->optlevel >= level); @@ -321,7 +321,7 @@ static bool opt_debug(const struct opt_state *state, int level, const char *form } /** Warn about an expression. */ -BFS_FORMATTER(3, 4) +attr_format(3, 4) static void opt_warning(const struct opt_state *state, const struct bfs_expr *expr, const char *format, ...) { if (bfs_expr_warning(state->ctx, expr)) { va_list args; diff --git a/src/parse.c b/src/parse.c index d21ab40..d3938fc 100644 --- a/src/parse.c +++ b/src/parse.c @@ -247,7 +247,7 @@ static void highlight_args(const struct bfs_ctx *ctx, char **argv, size_t argc, /** * Print an error message during parsing. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) static void parse_error(const struct parser_state *state, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -267,7 +267,7 @@ static void parse_error(const struct parser_state *state, const char *format, .. /** * Print an error about some command line arguments. */ -BFS_FORMATTER(4, 5) +attr_format(4, 5) static void parse_argv_error(const struct parser_state *state, char **argv, size_t argc, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -287,7 +287,7 @@ static void parse_argv_error(const struct parser_state *state, char **argv, size /** * Print an error about conflicting command line arguments. */ -BFS_FORMATTER(6, 7) +attr_format(6, 7) static void parse_conflict_error(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -308,7 +308,7 @@ static void parse_conflict_error(const struct parser_state *state, char **argv1, /** * Print an error about an expression. */ -BFS_FORMATTER(3, 4) +attr_format(3, 4) static void parse_expr_error(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -325,7 +325,7 @@ static void parse_expr_error(const struct parser_state *state, const struct bfs_ /** * Print a warning message during parsing. */ -BFS_FORMATTER(2, 3) +attr_format(2, 3) static bool parse_warning(const struct parser_state *state, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -348,7 +348,7 @@ static bool parse_warning(const struct parser_state *state, const char *format, /** * Print a warning about conflicting command line arguments. */ -BFS_FORMATTER(6, 7) +attr_format(6, 7) static bool parse_conflict_warning(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -372,7 +372,7 @@ static bool parse_conflict_warning(const struct parser_state *state, char **argv /** * Print a warning about an expression. */ -BFS_FORMATTER(3, 4) +attr_format(3, 4) static bool parse_expr_warning(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; -- cgit v1.2.3 From 5b38f658ee42bef05cecb6cadec65b25d9e94993 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 16 Nov 2023 12:25:09 -0500 Subject: config: New variadic attr(...) macro --- src/alloc.c | 2 +- src/alloc.h | 10 ++++---- src/bar.c | 2 +- src/color.c | 4 ++-- src/color.h | 4 ++-- src/config.h | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/diag.h | 37 ++++++++++++------------------ src/dstring.h | 18 +++++++-------- src/eval.c | 2 +- src/exec.c | 2 +- src/fsade.c | 6 ++--- src/ioq.c | 4 ++-- src/mtab.c | 2 +- src/opt.c | 4 ++-- src/parse.c | 14 +++++------ src/trie.c | 2 +- src/xspawn.c | 2 +- 17 files changed, 127 insertions(+), 62 deletions(-) (limited to 'src/opt.c') diff --git a/src/alloc.c b/src/alloc.c index 467f0f0..b65d0c5 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -176,7 +176,7 @@ void arena_init(struct arena *arena, size_t align, size_t size) { } /** Allocate a new slab. */ -attr_cold +attr(cold) static int slab_alloc(struct arena *arena) { // Make the initial allocation size ~4K size_t size = 4096; diff --git a/src/alloc.h b/src/alloc.h index 7132470..f21ddb9 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -114,8 +114,7 @@ static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t s * @return * The allocated memory, or NULL on failure. */ -attr_malloc(free, 1) -attr_aligned_alloc(1, 2) +attr(malloc(free, 1), aligned_alloc(1, 2)) void *alloc(size_t align, size_t size); /** @@ -128,8 +127,7 @@ void *alloc(size_t align, size_t size); * @return * The allocated memory, or NULL on failure. */ -attr_malloc(free, 1) -attr_aligned_alloc(1, 2) +attr(malloc(free, 1), aligned_alloc(1, 2)) void *zalloc(size_t align, size_t size); /** Allocate memory for the given type. */ @@ -252,7 +250,7 @@ void arena_free(struct arena *arena, void *ptr); /** * Allocate an object out of the arena. */ -attr_malloc(arena_free, 2) +attr(malloc(arena_free, 2)) void *arena_alloc(struct arena *arena); /** @@ -334,7 +332,7 @@ void varena_free(struct varena *varena, void *ptr, size_t count); * @return * The allocated struct, or NULL on failure. */ -attr_malloc(varena_free, 2) +attr(malloc(varena_free, 2)) void *varena_alloc(struct varena *varena, size_t count); /** diff --git a/src/bar.c b/src/bar.c index 2e50dbe..babadc3 100644 --- a/src/bar.c +++ b/src/bar.c @@ -127,7 +127,7 @@ static void reset_before_death_by(int sig) { } /** printf() to the status bar with a single write(). */ -attr_format(2, 3) +attr(format(2, 3)) static int bfs_bar_printf(struct bfs_bar *bar, const char *format, ...) { va_list args; va_start(args, format); diff --git a/src/color.c b/src/color.c index 05ba1a3..4c2c8ca 100644 --- a/src/color.c +++ b/src/color.c @@ -1105,7 +1105,7 @@ static int print_link_target(CFILE *cfile, const struct BFTW *ftwbuf) { } /** Format some colored output to the buffer. */ -attr_format(2, 3) +attr(format(2, 3)) static int cbuff(CFILE *cfile, const char *format, ...); /** Dump a parsed expression tree, for debugging. */ @@ -1177,7 +1177,7 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { return 0; } -attr_format(2, 0) +attr(format(2, 0)) static int cvbuff(CFILE *cfile, const char *format, va_list args) { const struct colors *colors = cfile->colors; int error = errno; diff --git a/src/color.h b/src/color.h index 8a81573..81f0e2a 100644 --- a/src/color.h +++ b/src/color.h @@ -100,13 +100,13 @@ int cfclose(CFILE *cfile); * @return * 0 on success, -1 on failure. */ -attr_format(2, 3) +attr(format(2, 3)) int cfprintf(CFILE *cfile, const char *format, ...); /** * cfprintf() variant that takes a va_list. */ -attr_format(2, 0) +attr(format(2, 0)) int cvfprintf(CFILE *cfile, const char *format, va_list args); #endif // BFS_COLOR_H diff --git a/src/config.h b/src/config.h index 9f95674..aa03552 100644 --- a/src/config.h +++ b/src/config.h @@ -301,4 +301,78 @@ typedef long double max_align_t; # define attr_target_clones(...) #endif +/** + * Shorthand for multiple attributes at once. attr(a, b(c), d) is equivalent to + * + * attr_a + * attr_b(c) + * attr_d + */ +#define attr(...) \ + attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none) + +/** + * attr() helper. For exposition, pretend we support only 2 args, instead of 9. + * There are a few cases: + * + * attr() + * => attr__(attr_, none, none) + * => attr_ => + * attr_none => + * attr_too_many_none() => + * + * attr(a) + * => attr__(attr_a, none, none) + * => attr_a => __attribute__((a)) + * attr_none => + * attr_too_many_none() => + * + * attr(a, b(c)) + * => attr__(attr_a, b(c), none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_none(none) => + * + * attr(a, b(c), d) + * => attr__(attr_a, b(c), d, none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_d(none, none) => error + * + * Some attribute names are the same as standard library functions, e.g. printf. + * Standard libraries are permitted to define these functions as macros, like + * + * #define printf(...) __builtin_printf(__VA_ARGS__) + * + * The token paste in + * + * #define attr(...) attr__(attr_##__VA_ARGS__, none, none) + * + * is necessary to prevent macro expansion before evaluating attr__(). + * Otherwise, we could get + * + * attr(printf(1, 2)) + * => attr__(__builtin_printf(1, 2), none, none) + * => attr____builtin_printf(1, 2) + * => error + */ +#define attr__(a1, a2, a3, a4, a5, a6, a7, a8, a9, none, ...) \ + a1 \ + attr_##a2 \ + attr_##a3 \ + attr_##a4 \ + attr_##a5 \ + attr_##a6 \ + attr_##a7 \ + attr_##a8 \ + attr_##a9 \ + attr_too_many_##none(__VA_ARGS__) + +// Ignore `attr_none` from expanding 1-9 argument attr(a1, a2, ...) +#define attr_none +// Ignore `attr_` from expanding 0-argument attr() +#define attr_ +// Only trigger an error on more than 9 arguments +#define attr_too_many_none(...) + #endif // BFS_CONFIG_H diff --git a/src/diag.h b/src/diag.h index 981419e..aa6a44f 100644 --- a/src/diag.h +++ b/src/diag.h @@ -44,8 +44,7 @@ struct bfs_loc { /** * Print a message to standard error and abort. */ -attr_cold -attr_format(2, 3) +attr(cold, format(2, 3)) noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...); /** @@ -117,14 +116,13 @@ const char *debug_flag_name(enum debug_flags flag); /** * Like perror(), but decorated like bfs_error(). */ -attr_cold +attr(cold) void bfs_perror(const struct bfs_ctx *ctx, const char *str); /** * Shorthand for printing error messages. */ -attr_cold -attr_format(2, 3) +attr(cold, format(2, 3)) void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); /** @@ -132,8 +130,7 @@ void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); * * @return Whether a warning was printed. */ -attr_cold -attr_format(2, 3) +attr(cold, format(2, 3)) bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); /** @@ -141,71 +138,67 @@ bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); * * @return Whether a debug message was printed. */ -attr_cold -attr_format(3, 4) +attr(cold, format(3, 4)) bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...); /** * bfs_error() variant that takes a va_list. */ -attr_cold -attr_format(2, 0) +attr(cold, format(2, 0)) void bfs_verror(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_warning() variant that takes a va_list. */ -attr_cold -attr_format(2, 0) +attr(cold, format(2, 0)) bool bfs_vwarning(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_debug() variant that takes a va_list. */ -attr_cold -attr_format(3, 0) +attr(cold, format(3, 0)) bool bfs_vdebug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, va_list args); /** * Print the error message prefix. */ -attr_cold +attr(cold) void bfs_error_prefix(const struct bfs_ctx *ctx); /** * Print the warning message prefix. */ -attr_cold +attr(cold) bool bfs_warning_prefix(const struct bfs_ctx *ctx); /** * Print the debug message prefix. */ -attr_cold +attr(cold) bool bfs_debug_prefix(const struct bfs_ctx *ctx, enum debug_flags flag); /** * Highlight parts of the command line in an error message. */ -attr_cold +attr(cold) void bfs_argv_error(const struct bfs_ctx *ctx, const bool args[]); /** * Highlight parts of an expression in an error message. */ -attr_cold +attr(cold) void bfs_expr_error(const struct bfs_ctx *ctx, const struct bfs_expr *expr); /** * Highlight parts of the command line in a warning message. */ -attr_cold +attr(cold) bool bfs_argv_warning(const struct bfs_ctx *ctx, const bool args[]); /** * Highlight parts of an expression in a warning message. */ -attr_cold +attr(cold) bool bfs_expr_warning(const struct bfs_ctx *ctx, const struct bfs_expr *expr); #endif // BFS_DIAG_H diff --git a/src/dstring.h b/src/dstring.h index 2a94438..1be1185 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -41,7 +41,7 @@ void dstrfree(dchar *dstr); * @param cap * The initial capacity of the string. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstralloc(size_t cap); /** @@ -50,7 +50,7 @@ dchar *dstralloc(size_t cap); * @param str * The NUL-terminated string to copy. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstrdup(const char *str); /** @@ -61,7 +61,7 @@ dchar *dstrdup(const char *str); * @param n * The maximum number of characters to copy from str. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstrndup(const char *str, size_t n); /** @@ -70,7 +70,7 @@ dchar *dstrndup(const char *str, size_t n); * @param dstr * The dynamic string to copy. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstrddup(const dchar *dstr); /** @@ -81,7 +81,7 @@ dchar *dstrddup(const dchar *dstr); * @param len * The length of the string, which may include internal NUL bytes. */ -attr_malloc(dstrfree, 1) +attr(malloc(dstrfree, 1)) dchar *dstrxdup(const char *str, size_t len); /** @@ -243,7 +243,7 @@ int dstrxcpy(dchar **dest, const char *str, size_t len); * @return * The created string, or NULL on failure. */ -attr_format(1, 2) +attr(format(1, 2)) char *dstrprintf(const char *format, ...); /** @@ -256,7 +256,7 @@ char *dstrprintf(const char *format, ...); * @return * The created string, or NULL on failure. */ -attr_format(1, 0) +attr(format(1, 0)) char *dstrvprintf(const char *format, va_list args); /** @@ -271,7 +271,7 @@ char *dstrvprintf(const char *format, va_list args); * @return * 0 on success, -1 on failure. */ -attr_format(2, 3) +attr(format(2, 3)) int dstrcatf(dchar **str, const char *format, ...); /** @@ -286,7 +286,7 @@ int dstrcatf(dchar **str, const char *format, ...); * @return * 0 on success, -1 on failure. */ -attr_format(2, 0) +attr(format(2, 0)) int dstrvcatf(dchar **str, const char *format, va_list args); /** diff --git a/src/eval.c b/src/eval.c index a990fd4..859ad7e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -57,7 +57,7 @@ struct bfs_eval { /** * Print an error message. */ -attr_format(2, 3) +attr(format(2, 3)) static void eval_error(struct bfs_eval *state, const char *format, ...) { // By POSIX, any errors should be accompanied by a non-zero exit status *state->ret = EXIT_FAILURE; diff --git a/src/exec.c b/src/exec.c index 90b3598..ba2fec8 100644 --- a/src/exec.c +++ b/src/exec.c @@ -22,7 +22,7 @@ #include /** Print some debugging info. */ -attr_format(2, 3) +attr(format(2, 3)) static void bfs_exec_debug(const struct bfs_exec *execbuf, const char *format, ...) { const struct bfs_ctx *ctx = execbuf->ctx; diff --git a/src/fsade.c b/src/fsade.c index 4d22d99..a48eeb0 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -32,7 +32,7 @@ * Many of the APIs used here don't have *at() variants, but we can try to * emulate something similar if /proc/self/fd is available. */ -attr_maybe_unused +attr(maybe_unused) static const char *fake_at(const struct BFTW *ftwbuf) { static atomic int proc_works = -1; @@ -66,7 +66,7 @@ fail: return ftwbuf->path; } -attr_maybe_unused +attr(maybe_unused) static void free_fake_at(const struct BFTW *ftwbuf, const char *path) { if (path != ftwbuf->path) { dstrfree((dchar *)path); @@ -76,7 +76,7 @@ static void free_fake_at(const struct BFTW *ftwbuf, const char *path) { /** * Check if an error was caused by the absence of support or data for a feature. */ -attr_maybe_unused +attr(maybe_unused) static bool is_absence_error(int error) { // If the OS doesn't support the feature, it's obviously not enabled for // any files diff --git a/src/ioq.c b/src/ioq.c index bd8d111..2739338 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -273,7 +273,7 @@ static struct ioq_monitor *ioq_slot_monitor(struct ioqq *ioqq, ioq_slot *slot) { } /** Atomically wait for a slot to change. */ -attr_noinline +attr(noinline) static uintptr_t ioq_slot_wait(struct ioqq *ioqq, ioq_slot *slot, uintptr_t value) { struct ioq_monitor *monitor = ioq_slot_monitor(ioqq, slot); mutex_lock(&monitor->mutex); @@ -303,7 +303,7 @@ done: } /** Wake up any threads waiting on a slot. */ -attr_noinline +attr(noinline) static void ioq_slot_wake(struct ioqq *ioqq, ioq_slot *slot) { struct ioq_monitor *monitor = ioq_slot_monitor(ioqq, slot); diff --git a/src/mtab.c b/src/mtab.c index 082150c..cc726a2 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -61,7 +61,7 @@ struct bfs_mtab { /** * Add an entry to the mount table. */ -attr_maybe_unused +attr(maybe_unused) static int bfs_mtab_add(struct bfs_mtab *mtab, const char *path, const char *type) { struct bfs_mount *mount = RESERVE(struct bfs_mount, &mtab->mounts, &mtab->nmounts); if (!mount) { diff --git a/src/opt.c b/src/opt.c index 5da73d8..3ee5e81 100644 --- a/src/opt.c +++ b/src/opt.c @@ -305,7 +305,7 @@ struct opt_state { }; /** Log an optimization. */ -attr_format(3, 4) +attr(format(3, 4)) static bool opt_debug(const struct opt_state *state, int level, const char *format, ...) { bfs_assert(state->ctx->optlevel >= level); @@ -321,7 +321,7 @@ static bool opt_debug(const struct opt_state *state, int level, const char *form } /** Warn about an expression. */ -attr_format(3, 4) +attr(format(3, 4)) static void opt_warning(const struct opt_state *state, const struct bfs_expr *expr, const char *format, ...) { if (bfs_expr_warning(state->ctx, expr)) { va_list args; diff --git a/src/parse.c b/src/parse.c index 778fc68..35d22fb 100644 --- a/src/parse.c +++ b/src/parse.c @@ -246,7 +246,7 @@ static void highlight_args(const struct bfs_ctx *ctx, char **argv, size_t argc, /** * Print an error message during parsing. */ -attr_format(2, 3) +attr(format(2, 3)) static void parse_error(const struct parser_state *state, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -266,7 +266,7 @@ static void parse_error(const struct parser_state *state, const char *format, .. /** * Print an error about some command line arguments. */ -attr_format(4, 5) +attr(format(4, 5)) static void parse_argv_error(const struct parser_state *state, char **argv, size_t argc, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -286,7 +286,7 @@ static void parse_argv_error(const struct parser_state *state, char **argv, size /** * Print an error about conflicting command line arguments. */ -attr_format(6, 7) +attr(format(6, 7)) static void parse_conflict_error(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -307,7 +307,7 @@ static void parse_conflict_error(const struct parser_state *state, char **argv1, /** * Print an error about an expression. */ -attr_format(3, 4) +attr(format(3, 4)) static void parse_expr_error(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -324,7 +324,7 @@ static void parse_expr_error(const struct parser_state *state, const struct bfs_ /** * Print a warning message during parsing. */ -attr_format(2, 3) +attr(format(2, 3)) static bool parse_warning(const struct parser_state *state, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -347,7 +347,7 @@ static bool parse_warning(const struct parser_state *state, const char *format, /** * Print a warning about conflicting command line arguments. */ -attr_format(6, 7) +attr(format(6, 7)) static bool parse_conflict_warning(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -371,7 +371,7 @@ static bool parse_conflict_warning(const struct parser_state *state, char **argv /** * Print a warning about an expression. */ -attr_format(3, 4) +attr(format(3, 4)) static bool parse_expr_warning(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; diff --git a/src/trie.c b/src/trie.c index cf55cee..bd5300d 100644 --- a/src/trie.c +++ b/src/trie.c @@ -95,7 +95,7 @@ bfs_static_assert(CHAR_WIDTH == 8); #if __i386__ || __x86_64__ -# define trie_clones attr_target_clones("popcnt", "default") +# define trie_clones attr(target_clones("popcnt", "default")) #else # define trie_clones #endif diff --git a/src/xspawn.c b/src/xspawn.c index 03287c3..40115a1 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -83,7 +83,7 @@ int bfs_spawn_destroy(struct bfs_spawn *ctx) { #if _POSIX_SPAWN > 0 /** Set some posix_spawnattr flags. */ -attr_maybe_unused +attr(maybe_unused) static int bfs_spawn_addflags(struct bfs_spawn *ctx, short flags) { short prev; errno = posix_spawnattr_getflags(&ctx->attr, &prev); -- cgit v1.2.3 From a85fc263a89fb89bc8b2a8166747660a177b1773 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 22 Nov 2023 09:33:49 -0500 Subject: config: s/attr_format/attr_printf/ --- src/bar.c | 2 +- src/color.c | 4 ++-- src/color.h | 4 ++-- src/config.h | 4 ++-- src/diag.h | 14 +++++++------- src/dstring.h | 8 ++++---- src/eval.c | 2 +- src/exec.c | 2 +- src/opt.c | 4 ++-- src/parse.c | 14 +++++++------- 10 files changed, 29 insertions(+), 29 deletions(-) (limited to 'src/opt.c') diff --git a/src/bar.c b/src/bar.c index babadc3..8ab4112 100644 --- a/src/bar.c +++ b/src/bar.c @@ -127,7 +127,7 @@ static void reset_before_death_by(int sig) { } /** printf() to the status bar with a single write(). */ -attr(format(2, 3)) +attr(printf(2, 3)) static int bfs_bar_printf(struct bfs_bar *bar, const char *format, ...) { va_list args; va_start(args, format); diff --git a/src/color.c b/src/color.c index 4c2c8ca..5247cbf 100644 --- a/src/color.c +++ b/src/color.c @@ -1105,7 +1105,7 @@ static int print_link_target(CFILE *cfile, const struct BFTW *ftwbuf) { } /** Format some colored output to the buffer. */ -attr(format(2, 3)) +attr(printf(2, 3)) static int cbuff(CFILE *cfile, const char *format, ...); /** Dump a parsed expression tree, for debugging. */ @@ -1177,7 +1177,7 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { return 0; } -attr(format(2, 0)) +attr(printf(2, 0)) static int cvbuff(CFILE *cfile, const char *format, va_list args) { const struct colors *colors = cfile->colors; int error = errno; diff --git a/src/color.h b/src/color.h index 81f0e2a..760aa9d 100644 --- a/src/color.h +++ b/src/color.h @@ -100,13 +100,13 @@ int cfclose(CFILE *cfile); * @return * 0 on success, -1 on failure. */ -attr(format(2, 3)) +attr(printf(2, 3)) int cfprintf(CFILE *cfile, const char *format, ...); /** * cfprintf() variant that takes a va_list. */ -attr(format(2, 0)) +attr(printf(2, 0)) int cvfprintf(CFILE *cfile, const char *format, va_list args); #endif // BFS_COLOR_H diff --git a/src/config.h b/src/config.h index aa03552..46f1efe 100644 --- a/src/config.h +++ b/src/config.h @@ -240,9 +240,9 @@ typedef long double max_align_t; * Adds compiler warnings for bad printf()-style function calls, if supported. */ #if __has_attribute(format) -# define attr_format(fmt, args) __attribute__((format(printf, fmt, args))) +# define attr_printf(fmt, args) __attribute__((format(printf, fmt, args))) #else -# define attr_format(fmt, args) +# define attr_printf(fmt, args) #endif /** diff --git a/src/diag.h b/src/diag.h index aa6a44f..791c065 100644 --- a/src/diag.h +++ b/src/diag.h @@ -44,7 +44,7 @@ struct bfs_loc { /** * Print a message to standard error and abort. */ -attr(cold, format(2, 3)) +attr(cold, printf(2, 3)) noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...); /** @@ -122,7 +122,7 @@ void bfs_perror(const struct bfs_ctx *ctx, const char *str); /** * Shorthand for printing error messages. */ -attr(cold, format(2, 3)) +attr(cold, printf(2, 3)) void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); /** @@ -130,7 +130,7 @@ void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); * * @return Whether a warning was printed. */ -attr(cold, format(2, 3)) +attr(cold, printf(2, 3)) bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); /** @@ -138,25 +138,25 @@ bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); * * @return Whether a debug message was printed. */ -attr(cold, format(3, 4)) +attr(cold, printf(3, 4)) bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...); /** * bfs_error() variant that takes a va_list. */ -attr(cold, format(2, 0)) +attr(cold, printf(2, 0)) void bfs_verror(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_warning() variant that takes a va_list. */ -attr(cold, format(2, 0)) +attr(cold, printf(2, 0)) bool bfs_vwarning(const struct bfs_ctx *ctx, const char *format, va_list args); /** * bfs_debug() variant that takes a va_list. */ -attr(cold, format(3, 0)) +attr(cold, printf(3, 0)) bool bfs_vdebug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, va_list args); /** diff --git a/src/dstring.h b/src/dstring.h index 1be1185..6006199 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -243,7 +243,7 @@ int dstrxcpy(dchar **dest, const char *str, size_t len); * @return * The created string, or NULL on failure. */ -attr(format(1, 2)) +attr(printf(1, 2)) char *dstrprintf(const char *format, ...); /** @@ -256,7 +256,7 @@ char *dstrprintf(const char *format, ...); * @return * The created string, or NULL on failure. */ -attr(format(1, 0)) +attr(printf(1, 0)) char *dstrvprintf(const char *format, va_list args); /** @@ -271,7 +271,7 @@ char *dstrvprintf(const char *format, va_list args); * @return * 0 on success, -1 on failure. */ -attr(format(2, 3)) +attr(printf(2, 3)) int dstrcatf(dchar **str, const char *format, ...); /** @@ -286,7 +286,7 @@ int dstrcatf(dchar **str, const char *format, ...); * @return * 0 on success, -1 on failure. */ -attr(format(2, 0)) +attr(printf(2, 0)) int dstrvcatf(dchar **str, const char *format, va_list args); /** diff --git a/src/eval.c b/src/eval.c index 859ad7e..372fcf5 100644 --- a/src/eval.c +++ b/src/eval.c @@ -57,7 +57,7 @@ struct bfs_eval { /** * Print an error message. */ -attr(format(2, 3)) +attr(printf(2, 3)) static void eval_error(struct bfs_eval *state, const char *format, ...) { // By POSIX, any errors should be accompanied by a non-zero exit status *state->ret = EXIT_FAILURE; diff --git a/src/exec.c b/src/exec.c index ba2fec8..60bfd28 100644 --- a/src/exec.c +++ b/src/exec.c @@ -22,7 +22,7 @@ #include /** Print some debugging info. */ -attr(format(2, 3)) +attr(printf(2, 3)) static void bfs_exec_debug(const struct bfs_exec *execbuf, const char *format, ...) { const struct bfs_ctx *ctx = execbuf->ctx; diff --git a/src/opt.c b/src/opt.c index 3ee5e81..ddcd1ab 100644 --- a/src/opt.c +++ b/src/opt.c @@ -305,7 +305,7 @@ struct opt_state { }; /** Log an optimization. */ -attr(format(3, 4)) +attr(printf(3, 4)) static bool opt_debug(const struct opt_state *state, int level, const char *format, ...) { bfs_assert(state->ctx->optlevel >= level); @@ -321,7 +321,7 @@ static bool opt_debug(const struct opt_state *state, int level, const char *form } /** Warn about an expression. */ -attr(format(3, 4)) +attr(printf(3, 4)) static void opt_warning(const struct opt_state *state, const struct bfs_expr *expr, const char *format, ...) { if (bfs_expr_warning(state->ctx, expr)) { va_list args; diff --git a/src/parse.c b/src/parse.c index 35d22fb..8d9c3f0 100644 --- a/src/parse.c +++ b/src/parse.c @@ -246,7 +246,7 @@ static void highlight_args(const struct bfs_ctx *ctx, char **argv, size_t argc, /** * Print an error message during parsing. */ -attr(format(2, 3)) +attr(printf(2, 3)) static void parse_error(const struct parser_state *state, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -266,7 +266,7 @@ static void parse_error(const struct parser_state *state, const char *format, .. /** * Print an error about some command line arguments. */ -attr(format(4, 5)) +attr(printf(4, 5)) static void parse_argv_error(const struct parser_state *state, char **argv, size_t argc, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -286,7 +286,7 @@ static void parse_argv_error(const struct parser_state *state, char **argv, size /** * Print an error about conflicting command line arguments. */ -attr(format(6, 7)) +attr(printf(6, 7)) static void parse_conflict_error(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -307,7 +307,7 @@ static void parse_conflict_error(const struct parser_state *state, char **argv1, /** * Print an error about an expression. */ -attr(format(3, 4)) +attr(printf(3, 4)) static void parse_expr_error(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -324,7 +324,7 @@ static void parse_expr_error(const struct parser_state *state, const struct bfs_ /** * Print a warning message during parsing. */ -attr(format(2, 3)) +attr(printf(2, 3)) static bool parse_warning(const struct parser_state *state, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -347,7 +347,7 @@ static bool parse_warning(const struct parser_state *state, const char *format, /** * Print a warning about conflicting command line arguments. */ -attr(format(6, 7)) +attr(printf(6, 7)) static bool parse_conflict_warning(const struct parser_state *state, char **argv1, size_t argc1, char **argv2, size_t argc2, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; @@ -371,7 +371,7 @@ static bool parse_conflict_warning(const struct parser_state *state, char **argv /** * Print a warning about an expression. */ -attr(format(3, 4)) +attr(printf(3, 4)) static bool parse_expr_warning(const struct parser_state *state, const struct bfs_expr *expr, const char *format, ...) { int error = errno; const struct bfs_ctx *ctx = state->ctx; -- cgit v1.2.3 From c60d93493f958cde1769a90058dac719ae1efa8c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 29 Jul 2023 14:44:26 -0400 Subject: opt: Use more standard terminology for data flow domains --- src/opt.c | 592 +++++++++++++++++++++++++++++++------------------------------- 1 file changed, 294 insertions(+), 298 deletions(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index ddcd1ab..a989670 100644 --- a/src/opt.c +++ b/src/opt.c @@ -6,14 +6,13 @@ * * -O1: basic logical simplifications, like folding (-true -and -foo) to -foo. * - * -O2: dead code elimination and data flow analysis. struct opt_facts is used + * -O2: dead code elimination and data flow analysis. struct df_domain is used * to record data flow facts that are true at various points of evaluation. - * Specifically, struct opt_facts records the facts that must be true before an - * expression is evaluated (state->facts), and those that must be true after the - * expression is evaluated, given that it returns true (state->facts_when_true) - * or false (state->facts_when_true). Additionally, state->facts_when_impure - * records the possible data flow facts before any expressions with side effects - * are evaluated. + * Specifically, struct df_domain records the state before an expression is + * evaluated (opt->before), and after an expression returns true + * (opt->after_true) or false (opt->after_false). Additionally, opt->impure + * records the possible state before any expression with side effects is + * evaluated. * * -O3: expression re-ordering to reduce expected cost. In an expression like * (-foo -and -bar), if both -foo and -bar are pure (no side effects), they can @@ -22,8 +21,8 @@ * -bar is likely to return false. * * -O4/-Ofast: aggressive optimizations that may affect correctness in corner - * cases. The main effect is to use facts_when_impure to determine if any side- - * effects are reachable at all, and skipping the traversal if not. + * cases. The main effect is to use impure to determine if any side-effects are + * reachable at all, and skipping the traversal if not. */ #include "opt.h" @@ -46,16 +45,58 @@ static char *fake_and_arg = "-a"; static char *fake_or_arg = "-o"; static char *fake_not_arg = "!"; +/** + * The data flow domain for predicates. + */ +enum df_pred { + /** The bottom state (unreachable). */ + PRED_BOTTOM = 0, + /** The predicate is known to be false. */ + PRED_FALSE = 1 << false, + /** The predicate is known to be true. */ + PRED_TRUE = 1 << true, + /** The top state (unknown). */ + PRED_TOP = PRED_FALSE | PRED_TRUE, +}; + +/** Make a predicate known. */ +static void constrain_pred(enum df_pred *pred, bool value) { + *pred &= 1 << value; +} + +/** Compute the join (union) of two predicates. */ +static void pred_join(enum df_pred *dest, enum df_pred src) { + *dest |= src; +} + /** * A contrained integer range. */ -struct range { +struct df_range { /** The (inclusive) minimum value. */ long long min; /** The (inclusive) maximum value. */ long long max; }; +/** Initialize an empty range. */ +static void range_init_bottom(struct df_range *range) { + range->min = LLONG_MAX; + range->max = LLONG_MIN; +} + +/** Check if a range is empty. */ +static bool range_is_bottom(const struct df_range *range) { + return range->min > range->max; +} + +/** Initialize a full range. */ +static void range_init_top(struct df_range *range) { + // All ranges we currently track are non-negative + range->min = 0; + range->max = LLONG_MAX; +} + /** Compute the minimum of two values. */ static long long min_value(long long a, long long b) { if (a < b) { @@ -75,17 +116,17 @@ static long long max_value(long long a, long long b) { } /** Constrain the minimum of a range. */ -static void constrain_min(struct range *range, long long value) { +static void constrain_min(struct df_range *range, long long value) { range->min = max_value(range->min, value); } /** Contrain the maximum of a range. */ -static void constrain_max(struct range *range, long long value) { +static void constrain_max(struct df_range *range, long long value) { range->max = min_value(range->max, value); } /** Remove a single value from a range. */ -static void range_remove(struct range *range, long long value) { +static void range_remove(struct df_range *range, long long value) { if (range->min == value) { if (range->min == LLONG_MAX) { range->max = LLONG_MIN; @@ -104,20 +145,9 @@ static void range_remove(struct range *range, long long value) { } /** Compute the union of two ranges. */ -static void range_union(struct range *result, const struct range *lhs, const struct range *rhs) { - result->min = min_value(lhs->min, rhs->min); - result->max = max_value(lhs->max, rhs->max); -} - -/** Check if a range contains no values. */ -static bool range_is_impossible(const struct range *range) { - return range->min > range->max; -} - -/** Set a range to contain no values. */ -static void set_range_impossible(struct range *range) { - range->min = LLONG_MAX; - range->max = LLONG_MIN; +static void range_join(struct df_range *dest, const struct df_range *src) { + dest->min = min_value(dest->min, src->min); + dest->max = max_value(dest->max, src->max); } /** @@ -140,42 +170,6 @@ enum range_type { RANGE_TYPES, }; -/** - * A possibly-known value of a predicate. - */ -enum known_pred { - /** The state is impossible to reach. */ - PRED_IMPOSSIBLE = -2, - /** The value of the predicate is not known. */ - PRED_UNKNOWN = -1, - /** The predicate is known to be false. */ - PRED_FALSE = false, - /** The predicate is known to be true. */ - PRED_TRUE = true, -}; - -/** Make a predicate known. */ -static void constrain_pred(enum known_pred *pred, bool value) { - if (*pred == PRED_UNKNOWN) { - *pred = value; - } else if (*pred == !value) { - *pred = PRED_IMPOSSIBLE; - } -} - -/** Compute the union of two known predicates. */ -static enum known_pred pred_union(enum known_pred lhs, enum known_pred rhs) { - if (lhs == PRED_IMPOSSIBLE) { - return rhs; - } else if (rhs == PRED_IMPOSSIBLE) { - return lhs; - } else if (lhs == rhs) { - return lhs; - } else { - return PRED_UNKNOWN; - } -} - /** * Types of predicates we track. */ @@ -207,14 +201,14 @@ enum pred_type { }; /** - * Data flow facts about an evaluation point. + * The data flow analysis domain. */ -struct opt_facts { - /** The value ranges we track. */ - struct range ranges[RANGE_TYPES]; - +struct df_domain { /** The predicates we track. */ - enum known_pred preds[PRED_TYPES]; + enum df_pred preds[PRED_TYPES]; + + /** The value ranges we track. */ + struct df_range ranges[RANGE_TYPES]; /** Bitmask of possible file types. */ unsigned int types; @@ -222,97 +216,101 @@ struct opt_facts { unsigned int xtypes; }; -/** Initialize some data flow facts. */ -static void facts_init(struct opt_facts *facts) { - for (int i = 0; i < RANGE_TYPES; ++i) { - struct range *range = &facts->ranges[i]; - range->min = 0; // All ranges we currently track are non-negative - range->max = LLONG_MAX; - } - +/** Set a data flow value to bottom. */ +static void df_init_bottom(struct df_domain *value) { for (int i = 0; i < PRED_TYPES; ++i) { - facts->preds[i] = PRED_UNKNOWN; + value->preds[i] = PRED_BOTTOM; } - facts->types = ~0; - facts->xtypes = ~0; -} - -/** Compute the union of two fact sets. */ -static void facts_union(struct opt_facts *result, const struct opt_facts *lhs, const struct opt_facts *rhs) { for (int i = 0; i < RANGE_TYPES; ++i) { - range_union(&result->ranges[i], &lhs->ranges[i], &rhs->ranges[i]); + range_init_bottom(&value->ranges[i]); } - for (int i = 0; i < PRED_TYPES; ++i) { - result->preds[i] = pred_union(lhs->preds[i], rhs->preds[i]); - } - - result->types = lhs->types | rhs->types; - result->xtypes = lhs->xtypes | rhs->xtypes; + value->types = 0; + value->xtypes = 0; } /** Determine whether a fact set is impossible. */ -static bool facts_are_impossible(const struct opt_facts *facts) { +static bool df_is_bottom(const struct df_domain *value) { for (int i = 0; i < RANGE_TYPES; ++i) { - if (range_is_impossible(&facts->ranges[i])) { + if (range_is_bottom(&value->ranges[i])) { return true; } } for (int i = 0; i < PRED_TYPES; ++i) { - if (facts->preds[i] == PRED_IMPOSSIBLE) { + if (value->preds[i] == PRED_BOTTOM) { return true; } } - if (!facts->types || !facts->xtypes) { + if (!value->types || !value->xtypes) { return true; } return false; } -/** Set some facts to be impossible. */ -static void set_facts_impossible(struct opt_facts *facts) { +/** Initialize some data flow value. */ +static void df_init_top(struct df_domain *value) { + for (int i = 0; i < PRED_TYPES; ++i) { + value->preds[i] = PRED_TOP; + } + for (int i = 0; i < RANGE_TYPES; ++i) { - set_range_impossible(&facts->ranges[i]); + range_init_top(&value->ranges[i]); } + value->types = ~0; + value->xtypes = ~0; +} + +/** Compute the union of two fact sets. */ +static void df_join(struct df_domain *dest, const struct df_domain *src) { for (int i = 0; i < PRED_TYPES; ++i) { - facts->preds[i] = PRED_IMPOSSIBLE; + pred_join(&dest->preds[i], src->preds[i]); } - facts->types = 0; - facts->xtypes = 0; + for (int i = 0; i < RANGE_TYPES; ++i) { + range_join(&dest->ranges[i], &src->ranges[i]); + } + + dest->types |= src->types; + dest->xtypes |= src->xtypes; } /** * Optimizer state. */ -struct opt_state { +struct bfs_opt { /** The context we're optimizing. */ const struct bfs_ctx *ctx; - /** Data flow facts before this expression is evaluated. */ - struct opt_facts facts; - /** Data flow facts after this expression returns true. */ - struct opt_facts facts_when_true; - /** Data flow facts after this expression returns false. */ - struct opt_facts facts_when_false; - /** Data flow facts before any side-effecting expressions are evaluated. */ - struct opt_facts *facts_when_impure; + /** Data flow state before this expression is evaluated. */ + struct df_domain before; + /** Data flow state after this expression returns true. */ + struct df_domain after_true; + /** Data flow state after this expression returns false. */ + struct df_domain after_false; + /** Data flow state before any side-effecting expressions are evaluated. */ + struct df_domain *impure; }; +/** Constrain the value of a predicate. */ +static void opt_constrain_pred(struct bfs_opt *opt, enum pred_type type, bool value) { + constrain_pred(&opt->after_true.preds[type], value); + constrain_pred(&opt->after_false.preds[type], !value); +} + /** Log an optimization. */ attr(printf(3, 4)) -static bool opt_debug(const struct opt_state *state, int level, const char *format, ...) { - bfs_assert(state->ctx->optlevel >= level); +static bool opt_debug(const struct bfs_opt *opt, int level, const char *format, ...) { + bfs_assert(opt->ctx->optlevel >= level); - if (bfs_debug(state->ctx, DEBUG_OPT, "${cyn}-O%d${rs}: ", level)) { + if (bfs_debug(opt->ctx, DEBUG_OPT, "${cyn}-O%d${rs}: ", level)) { va_list args; va_start(args, format); - cvfprintf(state->ctx->cerr, format, args); + cvfprintf(opt->ctx->cerr, format, args); va_end(args); return true; } else { @@ -322,11 +320,11 @@ static bool opt_debug(const struct opt_state *state, int level, const char *form /** Warn about an expression. */ attr(printf(3, 4)) -static void opt_warning(const struct opt_state *state, const struct bfs_expr *expr, const char *format, ...) { - if (bfs_expr_warning(state->ctx, expr)) { +static void opt_warning(const struct bfs_opt *opt, const struct bfs_expr *expr, const char *format, ...) { + if (bfs_expr_warning(opt->ctx, expr)) { va_list args; va_start(args, format); - bfs_vwarning(state->ctx, format, args); + bfs_vwarning(opt->ctx, format, args); va_end(args); } } @@ -365,15 +363,15 @@ static struct bfs_expr *negate_expr(struct bfs_expr *rhs, char **argv) { return expr; } -static struct bfs_expr *optimize_not_expr(const struct opt_state *state, struct bfs_expr *expr); -static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct bfs_expr *expr); -static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct bfs_expr *expr); +static struct bfs_expr *optimize_not_expr(const struct bfs_opt *opt, struct bfs_expr *expr); +static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_expr *expr); +static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_expr *expr); /** * Apply De Morgan's laws. */ -static struct bfs_expr *de_morgan(const struct opt_state *state, struct bfs_expr *expr, char **argv) { - bool debug = opt_debug(state, 1, "De Morgan's laws: %pe ", expr); +static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *expr, char **argv) { + bool debug = opt_debug(opt, 1, "De Morgan's laws: %pe ", expr); struct bfs_expr *parent = negate_expr(expr, argv); if (!parent) { @@ -403,14 +401,14 @@ static struct bfs_expr *de_morgan(const struct opt_state *state, struct bfs_expr } if (debug) { - cfprintf(state->ctx->cerr, "<==> %pe\n", parent); + cfprintf(opt->ctx->cerr, "<==> %pe\n", parent); } if (expr->lhs->eval_fn == eval_not) { - expr->lhs = optimize_not_expr(state, expr->lhs); + expr->lhs = optimize_not_expr(opt, expr->lhs); } if (expr->rhs->eval_fn == eval_not) { - expr->rhs = optimize_not_expr(state, expr->rhs); + expr->rhs = optimize_not_expr(opt, expr->rhs); } if (!expr->lhs || !expr->rhs) { bfs_expr_free(parent); @@ -418,9 +416,9 @@ static struct bfs_expr *de_morgan(const struct opt_state *state, struct bfs_expr } if (expr->eval_fn == eval_and) { - expr = optimize_and_expr(state, expr); + expr = optimize_and_expr(opt, expr); } else { - expr = optimize_or_expr(state, expr); + expr = optimize_or_expr(opt, expr); } if (has_parent) { parent->rhs = expr; @@ -433,38 +431,38 @@ static struct bfs_expr *de_morgan(const struct opt_state *state, struct bfs_expr } if (has_parent) { - parent = optimize_not_expr(state, parent); + parent = optimize_not_expr(opt, parent); } return parent; } /** Optimize an expression recursively. */ -static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct bfs_expr *expr); +static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr); /** * Optimize a negation. */ -static struct bfs_expr *optimize_not_expr(const struct opt_state *state, struct bfs_expr *expr) { +static struct bfs_expr *optimize_not_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { bfs_assert(expr->eval_fn == eval_not); struct bfs_expr *rhs = expr->rhs; - int optlevel = state->ctx->optlevel; + int optlevel = opt->ctx->optlevel; if (optlevel >= 1) { if (rhs->eval_fn == eval_true || rhs->eval_fn == eval_false) { struct bfs_expr *ret = opt_const(rhs->eval_fn == eval_false); - opt_debug(state, 1, "constant propagation: %pe <==> %pe\n", expr, ret); + opt_debug(opt, 1, "constant propagation: %pe <==> %pe\n", expr, ret); bfs_expr_free(expr); return ret; } else if (rhs->eval_fn == eval_not) { - opt_debug(state, 1, "double negation: %pe <==> %pe\n", expr, rhs->rhs); + opt_debug(opt, 1, "double negation: %pe <==> %pe\n", expr, rhs->rhs); return extract_child_expr(expr, &rhs->rhs); } else if (bfs_expr_never_returns(rhs)) { - opt_debug(state, 1, "reachability: %pe <==> %pe\n", expr, rhs); + opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, rhs); return extract_child_expr(expr, &expr->rhs); } else if ((rhs->eval_fn == eval_and || rhs->eval_fn == eval_or) && (rhs->lhs->eval_fn == eval_not || rhs->rhs->eval_fn == eval_not)) { - return de_morgan(state, expr, expr->argv); + return de_morgan(opt, expr, expr->argv); } } @@ -478,17 +476,17 @@ static struct bfs_expr *optimize_not_expr(const struct opt_state *state, struct } /** Optimize a negation recursively. */ -static struct bfs_expr *optimize_not_expr_recursive(struct opt_state *state, struct bfs_expr *expr) { - struct opt_state rhs_state = *state; +static struct bfs_expr *optimize_not_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { + struct bfs_opt rhs_state = *opt; expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { goto fail; } - state->facts_when_true = rhs_state.facts_when_false; - state->facts_when_false = rhs_state.facts_when_true; + opt->after_true = rhs_state.after_false; + opt->after_false = rhs_state.after_true; - return optimize_not_expr(state, expr); + return optimize_not_expr(opt, expr); fail: bfs_expr_free(expr); @@ -496,27 +494,27 @@ fail: } /** Optimize a conjunction. */ -static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct bfs_expr *expr) { +static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { bfs_assert(expr->eval_fn == eval_and); struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; - const struct bfs_ctx *ctx = state->ctx; + const struct bfs_ctx *ctx = opt->ctx; int optlevel = ctx->optlevel; if (optlevel >= 1) { if (lhs->eval_fn == eval_true) { - opt_debug(state, 1, "conjunction elimination: %pe <==> %pe\n", expr, rhs); + opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, rhs); return extract_child_expr(expr, &expr->rhs); } else if (rhs->eval_fn == eval_true) { - opt_debug(state, 1, "conjunction elimination: %pe <==> %pe\n", expr, lhs); + opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, lhs); return extract_child_expr(expr, &expr->lhs); } else if (lhs->always_false) { - opt_debug(state, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); - opt_warning(state, expr->rhs, "This expression is unreachable.\n\n"); + opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); + opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); return extract_child_expr(expr, &expr->lhs); } else if (lhs->always_true && rhs->eval_fn == eval_false) { - bool debug = opt_debug(state, 1, "strength reduction: %pe <==> ", expr); + bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); struct bfs_expr *ret = extract_child_expr(expr, &expr->lhs); ret = negate_expr(ret, &fake_not_arg); if (debug && ret) { @@ -524,11 +522,11 @@ static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct } return ret; } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_false) { - opt_debug(state, 2, "purity: %pe <==> %pe\n", expr, rhs); - opt_warning(state, expr->lhs, "The result of this expression is ignored.\n\n"); + opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); + opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); return extract_child_expr(expr, &expr->rhs); } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { - return de_morgan(state, expr, expr->lhs->argv); + return de_morgan(opt, expr, expr->lhs->argv); } } @@ -542,24 +540,25 @@ static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct } /** Optimize a conjunction recursively. */ -static struct bfs_expr *optimize_and_expr_recursive(struct opt_state *state, struct bfs_expr *expr) { - struct opt_state lhs_state = *state; +static struct bfs_expr *optimize_and_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { + struct bfs_opt lhs_state = *opt; expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); if (!expr->lhs) { goto fail; } - struct opt_state rhs_state = *state; - rhs_state.facts = lhs_state.facts_when_true; + struct bfs_opt rhs_state = *opt; + rhs_state.before = lhs_state.after_true; expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { goto fail; } - state->facts_when_true = rhs_state.facts_when_true; - facts_union(&state->facts_when_false, &lhs_state.facts_when_false, &rhs_state.facts_when_false); + opt->after_true = rhs_state.after_true; + opt->after_false = lhs_state.after_false; + df_join(&opt->after_false, &rhs_state.after_false); - return optimize_and_expr(state, expr); + return optimize_and_expr(opt, expr); fail: bfs_expr_free(expr); @@ -567,27 +566,27 @@ fail: } /** Optimize a disjunction. */ -static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct bfs_expr *expr) { +static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { bfs_assert(expr->eval_fn == eval_or); struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; - const struct bfs_ctx *ctx = state->ctx; + const struct bfs_ctx *ctx = opt->ctx; int optlevel = ctx->optlevel; if (optlevel >= 1) { if (lhs->always_true) { - opt_debug(state, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); - opt_warning(state, expr->rhs, "This expression is unreachable.\n\n"); + opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); + opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); return extract_child_expr(expr, &expr->lhs); } else if (lhs->eval_fn == eval_false) { - opt_debug(state, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, rhs); + opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, rhs); return extract_child_expr(expr, &expr->rhs); } else if (rhs->eval_fn == eval_false) { - opt_debug(state, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, lhs); + opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, lhs); return extract_child_expr(expr, &expr->lhs); } else if (lhs->always_false && rhs->eval_fn == eval_true) { - bool debug = opt_debug(state, 1, "strength reduction: %pe <==> ", expr); + bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); struct bfs_expr *ret = extract_child_expr(expr, &expr->lhs); ret = negate_expr(ret, &fake_not_arg); if (debug && ret) { @@ -595,11 +594,11 @@ static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct b } return ret; } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_true) { - opt_debug(state, 2, "purity: %pe <==> %pe\n", expr, rhs); - opt_warning(state, expr->lhs, "The result of this expression is ignored.\n\n"); + opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); + opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); return extract_child_expr(expr, &expr->rhs); } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { - return de_morgan(state, expr, expr->lhs->argv); + return de_morgan(opt, expr, expr->lhs->argv); } } @@ -613,24 +612,25 @@ static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct b } /** Optimize a disjunction recursively. */ -static struct bfs_expr *optimize_or_expr_recursive(struct opt_state *state, struct bfs_expr *expr) { - struct opt_state lhs_state = *state; +static struct bfs_expr *optimize_or_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { + struct bfs_opt lhs_state = *opt; expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); if (!expr->lhs) { goto fail; } - struct opt_state rhs_state = *state; - rhs_state.facts = lhs_state.facts_when_false; + struct bfs_opt rhs_state = *opt; + rhs_state.before = lhs_state.after_false; expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { goto fail; } - facts_union(&state->facts_when_true, &lhs_state.facts_when_true, &rhs_state.facts_when_true); - state->facts_when_false = rhs_state.facts_when_false; + opt->after_false = rhs_state.after_false; + opt->after_true = lhs_state.after_true; + df_join(&opt->after_true, &rhs_state.after_true); - return optimize_or_expr(state, expr); + return optimize_or_expr(opt, expr); fail: bfs_expr_free(expr); @@ -638,20 +638,20 @@ fail: } /** Optimize an expression in an ignored-result context. */ -static struct bfs_expr *ignore_result(const struct opt_state *state, struct bfs_expr *expr) { - int optlevel = state->ctx->optlevel; +static struct bfs_expr *ignore_result(const struct bfs_opt *opt, struct bfs_expr *expr) { + int optlevel = opt->ctx->optlevel; if (optlevel >= 1) { while (true) { if (expr->eval_fn == eval_not) { - opt_debug(state, 1, "ignored result: %pe --> %pe\n", expr, expr->rhs); - opt_warning(state, expr, "The result of this expression is ignored.\n\n"); + opt_debug(opt, 1, "ignored result: %pe --> %pe\n", expr, expr->rhs); + opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); expr = extract_child_expr(expr, &expr->rhs); } else if (optlevel >= 2 && (expr->eval_fn == eval_and || expr->eval_fn == eval_or || expr->eval_fn == eval_comma) && expr->rhs->pure) { - opt_debug(state, 2, "ignored result: %pe --> %pe\n", expr, expr->lhs); - opt_warning(state, expr->rhs, "The result of this expression is ignored.\n\n"); + opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, expr->lhs); + opt_warning(opt, expr->rhs, "The result of this expression is ignored.\n\n"); expr = extract_child_expr(expr, &expr->lhs); } else { break; @@ -660,8 +660,8 @@ static struct bfs_expr *ignore_result(const struct opt_state *state, struct bfs_ if (optlevel >= 2 && expr->pure && expr->eval_fn != eval_false) { struct bfs_expr *ret = opt_const(false); - opt_debug(state, 2, "ignored result: %pe --> %pe\n", expr, ret); - opt_warning(state, expr, "The result of this expression is ignored.\n\n"); + opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, ret); + opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); bfs_expr_free(expr); return ret; } @@ -671,27 +671,27 @@ static struct bfs_expr *ignore_result(const struct opt_state *state, struct bfs_ } /** Optimize a comma expression. */ -static struct bfs_expr *optimize_comma_expr(const struct opt_state *state, struct bfs_expr *expr) { +static struct bfs_expr *optimize_comma_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { bfs_assert(expr->eval_fn == eval_comma); struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; - int optlevel = state->ctx->optlevel; + int optlevel = opt->ctx->optlevel; if (optlevel >= 1) { - lhs = expr->lhs = ignore_result(state, lhs); + lhs = expr->lhs = ignore_result(opt, lhs); if (bfs_expr_never_returns(lhs)) { - opt_debug(state, 1, "reachability: %pe <==> %pe\n", expr, lhs); - opt_warning(state, expr->rhs, "This expression is unreachable.\n\n"); + opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, lhs); + opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); return extract_child_expr(expr, &expr->lhs); } else if ((lhs->always_true && rhs->eval_fn == eval_true) || (lhs->always_false && rhs->eval_fn == eval_false)) { - opt_debug(state, 1, "redundancy elimination: %pe <==> %pe\n", expr, lhs); + opt_debug(opt, 1, "redundancy elimination: %pe <==> %pe\n", expr, lhs); return extract_child_expr(expr, &expr->lhs); } else if (optlevel >= 2 && lhs->pure) { - opt_debug(state, 2, "purity: %pe <==> %pe\n", expr, rhs); - opt_warning(state, expr->lhs, "The result of this expression is ignored.\n\n"); + opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); + opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); return extract_child_expr(expr, &expr->rhs); } } @@ -706,76 +706,72 @@ static struct bfs_expr *optimize_comma_expr(const struct opt_state *state, struc } /** Optimize a comma expression recursively. */ -static struct bfs_expr *optimize_comma_expr_recursive(struct opt_state *state, struct bfs_expr *expr) { - struct opt_state lhs_state = *state; +static struct bfs_expr *optimize_comma_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { + struct bfs_opt lhs_state = *opt; expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); if (!expr->lhs) { goto fail; } - struct opt_state rhs_state = *state; - facts_union(&rhs_state.facts, &lhs_state.facts_when_true, &lhs_state.facts_when_false); + struct bfs_opt rhs_state = *opt; + rhs_state.before = lhs_state.after_true; + df_join(&rhs_state.before, &lhs_state.after_false); + expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { goto fail; } - return optimize_comma_expr(state, expr); + return optimize_comma_expr(opt, expr); fail: bfs_expr_free(expr); return NULL; } -/** Infer data flow facts about a predicate. */ -static void infer_pred_facts(struct opt_state *state, enum pred_type pred) { - constrain_pred(&state->facts_when_true.preds[pred], true); - constrain_pred(&state->facts_when_false.preds[pred], false); -} - -/** Infer data flow facts about an icmp-style ([+-]N) expression. */ -static void infer_icmp_facts(struct opt_state *state, const struct bfs_expr *expr, enum range_type type) { - struct range *range_when_true = &state->facts_when_true.ranges[type]; - struct range *range_when_false = &state->facts_when_false.ranges[type]; +/** Optimize an icmp-style ([+-]N) expression. */ +static void optimize_icmp(struct bfs_opt *opt, const struct bfs_expr *expr, enum range_type type) { + struct df_range *true_range = &opt->after_true.ranges[type]; + struct df_range *false_range = &opt->after_false.ranges[type]; long long value = expr->num; switch (expr->int_cmp) { case BFS_INT_EQUAL: - constrain_min(range_when_true, value); - constrain_max(range_when_true, value); - range_remove(range_when_false, value); + constrain_min(true_range, value); + constrain_max(true_range, value); + range_remove(false_range, value); break; case BFS_INT_LESS: - constrain_min(range_when_false, value); - constrain_max(range_when_true, value); - range_remove(range_when_true, value); + constrain_min(false_range, value); + constrain_max(true_range, value); + range_remove(true_range, value); break; case BFS_INT_GREATER: - constrain_max(range_when_false, value); - constrain_min(range_when_true, value); - range_remove(range_when_true, value); + constrain_max(false_range, value); + constrain_min(true_range, value); + range_remove(true_range, value); break; } } /** Optimize -{execut,read,writ}able. */ -static struct bfs_expr *optimize_access(struct opt_state *state, struct bfs_expr *expr) { +static struct bfs_expr *optimize_access(struct bfs_opt *opt, struct bfs_expr *expr) { expr->probability = 1.0; if (expr->num & R_OK) { - infer_pred_facts(state, READABLE_PRED); + opt_constrain_pred(opt, READABLE_PRED, true); expr->probability *= 0.99; } if (expr->num & W_OK) { - infer_pred_facts(state, WRITABLE_PRED); + opt_constrain_pred(opt, WRITABLE_PRED, true); expr->probability *= 0.8; } if (expr->num & X_OK) { - infer_pred_facts(state, EXECUTABLE_PRED); + opt_constrain_pred(opt, EXECUTABLE_PRED, true); expr->probability *= 0.2; } @@ -783,8 +779,8 @@ static struct bfs_expr *optimize_access(struct opt_state *state, struct bfs_expr } /** Optimize -empty. */ -static struct bfs_expr *optimize_empty(struct opt_state *state, struct bfs_expr *expr) { - if (state->ctx->optlevel >= 4) { +static struct bfs_expr *optimize_empty(struct bfs_opt *opt, struct bfs_expr *expr) { + if (opt->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 @@ -795,7 +791,7 @@ static struct bfs_expr *optimize_empty(struct opt_state *state, struct bfs_expr } /** Optimize -{exec,ok}{,dir}. */ -static struct bfs_expr *optimize_exec(struct opt_state *state, struct bfs_expr *expr) { +static struct bfs_expr *optimize_exec(struct bfs_opt *opt, struct bfs_expr *expr) { if (expr->exec->flags & BFS_EXEC_MULTI) { expr->always_true = true; } else { @@ -806,7 +802,7 @@ static struct bfs_expr *optimize_exec(struct opt_state *state, struct bfs_expr * } /** Optimize -name/-lname/-path. */ -static struct bfs_expr *optimize_fnmatch(struct opt_state *state, struct bfs_expr *expr) { +static struct bfs_expr *optimize_fnmatch(struct bfs_opt *opt, struct bfs_expr *expr) { if (strchr(expr->argv[1], '*')) { expr->probability = 0.5; } else { @@ -817,13 +813,13 @@ static struct bfs_expr *optimize_fnmatch(struct opt_state *state, struct bfs_exp } /** Optimize -gid. */ -static struct bfs_expr *optimize_gid(struct opt_state *state, struct bfs_expr *expr) { - struct range *range = &state->facts_when_true.ranges[GID_RANGE]; +static struct bfs_expr *optimize_gid(struct bfs_opt *opt, struct bfs_expr *expr) { + struct df_range *range = &opt->after_true.ranges[GID_RANGE]; if (range->min == range->max) { gid_t gid = range->min; - bool nogroup = !bfs_getgrgid(state->ctx->groups, gid); + bool nogroup = !bfs_getgrgid(opt->ctx->groups, gid); if (errno == 0) { - constrain_pred(&state->facts_when_true.preds[NOGROUP_PRED], nogroup); + opt_constrain_pred(opt, NOGROUP_PRED, nogroup); } } @@ -831,8 +827,8 @@ static struct bfs_expr *optimize_gid(struct opt_state *state, struct bfs_expr *e } /** Optimize -inum. */ -static struct bfs_expr *optimize_inum(struct opt_state *state, struct bfs_expr *expr) { - struct range *range = &state->facts_when_true.ranges[INUM_RANGE]; +static struct bfs_expr *optimize_inum(struct bfs_opt *opt, struct bfs_expr *expr) { + struct df_range *range = &opt->after_true.ranges[INUM_RANGE]; if (range->min == range->max) { expr->probability = 0.01; } else { @@ -843,8 +839,8 @@ static struct bfs_expr *optimize_inum(struct opt_state *state, struct bfs_expr * } /** Optimize -links. */ -static struct bfs_expr *optimize_links(struct opt_state *state, struct bfs_expr *expr) { - struct range *range = &state->facts_when_true.ranges[LINKS_RANGE]; +static struct bfs_expr *optimize_links(struct bfs_opt *opt, struct bfs_expr *expr) { + struct df_range *range = &opt->after_true.ranges[LINKS_RANGE]; if (1 >= range->min && 1 <= range->max) { expr->probability = 0.99; } else { @@ -855,13 +851,13 @@ static struct bfs_expr *optimize_links(struct opt_state *state, struct bfs_expr } /** Optimize -uid. */ -static struct bfs_expr *optimize_uid(struct opt_state *state, struct bfs_expr *expr) { - struct range *range = &state->facts_when_true.ranges[UID_RANGE]; +static struct bfs_expr *optimize_uid(struct bfs_opt *opt, struct bfs_expr *expr) { + struct df_range *range = &opt->after_true.ranges[UID_RANGE]; if (range->min == range->max) { uid_t uid = range->min; - bool nouser = !bfs_getpwuid(state->ctx->users, uid); + bool nouser = !bfs_getpwuid(opt->ctx->users, uid); if (errno == 0) { - constrain_pred(&state->facts_when_true.preds[NOUSER_PRED], nouser); + opt_constrain_pred(opt, NOUSER_PRED, nouser); } } @@ -869,16 +865,16 @@ static struct bfs_expr *optimize_uid(struct opt_state *state, struct bfs_expr *e } /** Optimize -samefile. */ -static struct bfs_expr *optimize_samefile(struct opt_state *state, struct bfs_expr *expr) { - struct range *range_when_true = &state->facts_when_true.ranges[INUM_RANGE]; - constrain_min(range_when_true, expr->ino); - constrain_max(range_when_true, expr->ino); +static struct bfs_expr *optimize_samefile(struct bfs_opt *opt, struct bfs_expr *expr) { + struct df_range *range = &opt->after_true.ranges[INUM_RANGE]; + constrain_min(range, expr->ino); + constrain_max(range, expr->ino); return expr; } /** Optimize -size. */ -static struct bfs_expr *optimize_size(struct opt_state *state, struct bfs_expr *expr) { - struct range *range = &state->facts_when_true.ranges[SIZE_RANGE]; +static struct bfs_expr *optimize_size(struct bfs_opt *opt, struct bfs_expr *expr) { + struct df_range *range = &opt->after_true.ranges[SIZE_RANGE]; if (range->min == range->max) { expr->probability = 0.01; } else { @@ -923,9 +919,9 @@ static void estimate_type_probability(struct bfs_expr *expr) { } /** Optimize -type. */ -static struct bfs_expr *optimize_type(struct opt_state *state, struct bfs_expr *expr) { - state->facts_when_true.types &= expr->num; - state->facts_when_false.types &= ~expr->num; +static struct bfs_expr *optimize_type(struct bfs_opt *opt, struct bfs_expr *expr) { + opt->after_true.types &= expr->num; + opt->after_false.types &= ~expr->num; estimate_type_probability(expr); @@ -933,16 +929,16 @@ static struct bfs_expr *optimize_type(struct opt_state *state, struct bfs_expr * } /** Optimize -xtype. */ -static struct bfs_expr *optimize_xtype(struct opt_state *state, struct bfs_expr *expr) { - if (state->ctx->optlevel >= 4) { +static struct bfs_expr *optimize_xtype(struct bfs_opt *opt, struct bfs_expr *expr) { + if (opt->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 expr->pure = true; } - state->facts_when_true.xtypes &= expr->num; - state->facts_when_false.xtypes &= ~expr->num; + opt->after_true.xtypes &= expr->num; + opt->after_false.xtypes &= ~expr->num; estimate_type_probability(expr); @@ -1115,7 +1111,7 @@ static const struct { }; /** Signature for custom optimizer functions. */ -typedef struct bfs_expr *bfs_opt_fn(struct opt_state *state, struct bfs_expr *expr); +typedef struct bfs_expr *bfs_opt_fn(struct bfs_opt *opt, struct bfs_expr *expr); /** Table of custom optimizer functions. */ static const struct { @@ -1150,7 +1146,7 @@ static const struct { /** * Look up the appropriate optimizer for an expression and call it. */ -static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs_expr *expr) { +static struct bfs_expr *optimize_expr_lookup(struct bfs_opt *opt, struct bfs_expr *expr) { for (size_t i = 0; i < countof(opt_pure); ++i) { if (opt_pure[i] == expr->eval_fn) { expr->pure = true; @@ -1189,42 +1185,42 @@ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs for (size_t i = 0; i < countof(opt_preds); ++i) { if (opt_preds[i].eval_fn == expr->eval_fn) { - infer_pred_facts(state, opt_preds[i].pred); + opt_constrain_pred(opt, opt_preds[i].pred, true); break; } } for (size_t i = 0; i < countof(opt_ranges); ++i) { if (opt_ranges[i].eval_fn == expr->eval_fn) { - infer_icmp_facts(state, expr, opt_ranges[i].range); + optimize_icmp(opt, expr, opt_ranges[i].range); break; } } for (size_t i = 0; i < countof(opt_fns); ++i) { if (opt_fns[i].eval_fn == expr->eval_fn) { - return opt_fns[i].opt_fn(state, expr); + return opt_fns[i].opt_fn(opt, expr); } } return expr; } -static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct bfs_expr *expr) { - int optlevel = state->ctx->optlevel; +static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { + int optlevel = opt->ctx->optlevel; - state->facts_when_true = state->facts; - state->facts_when_false = state->facts; + opt->after_true = opt->before; + opt->after_false = opt->before; - if (optlevel >= 2 && facts_are_impossible(&state->facts)) { + if (optlevel >= 2 && df_is_bottom(&opt->before)) { struct bfs_expr *ret = opt_const(false); - opt_debug(state, 2, "reachability: %pe --> %pe\n", expr, ret); - opt_warning(state, expr, "This expression is unreachable.\n\n"); + opt_debug(opt, 2, "reachability: %pe --> %pe\n", expr, ret); + opt_warning(opt, expr, "This expression is unreachable.\n\n"); bfs_expr_free(expr); return ret; } - expr = optimize_expr_lookup(state, expr); + expr = optimize_expr_lookup(opt, expr); if (!expr) { return NULL; } @@ -1243,38 +1239,38 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct } } } else if (!expr->pure) { - facts_union(state->facts_when_impure, state->facts_when_impure, &state->facts); + df_join(opt->impure, &opt->before); } if (expr->always_true) { expr->probability = 1.0; - set_facts_impossible(&state->facts_when_false); + df_init_bottom(&opt->after_false); } if (expr->always_false) { expr->probability = 0.0; - set_facts_impossible(&state->facts_when_true); + df_init_bottom(&opt->after_true); } if (optlevel < 2 || expr->eval_fn == eval_true || expr->eval_fn == eval_false) { return expr; } - if (facts_are_impossible(&state->facts_when_true)) { + if (df_is_bottom(&opt->after_true)) { if (expr->pure) { struct bfs_expr *ret = opt_const(false); - opt_warning(state, expr, "This expression is always false.\n\n"); - opt_debug(state, 2, "data flow: %pe --> %pe\n", expr, ret); + opt_warning(opt, expr, "This expression is always false.\n\n"); + opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); bfs_expr_free(expr); return ret; } else { expr->always_false = true; expr->probability = 0.0; } - } else if (facts_are_impossible(&state->facts_when_false)) { + } else if (df_is_bottom(&opt->after_false)) { if (expr->pure) { struct bfs_expr *ret = opt_const(true); - opt_debug(state, 2, "data flow: %pe --> %pe\n", expr, ret); - opt_warning(state, expr, "This expression is always true.\n\n"); + opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); + opt_warning(opt, expr, "This expression is always true.\n\n"); bfs_expr_free(expr); return ret; } else { @@ -1287,14 +1283,14 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct } /** Swap the children of a binary expression if it would reduce the cost. */ -static bool reorder_expr(const struct opt_state *state, struct bfs_expr *expr, float swapped_cost) { +static bool reorder_expr(const struct bfs_opt *opt, struct bfs_expr *expr, float swapped_cost) { if (swapped_cost < expr->cost) { - bool debug = opt_debug(state, 3, "cost: %pe <==> ", expr); + bool debug = opt_debug(opt, 3, "cost: %pe <==> ", expr); struct bfs_expr *lhs = expr->lhs; expr->lhs = expr->rhs; expr->rhs = lhs; if (debug) { - cfprintf(state->ctx->cerr, "%pe (~${ylw}%g${rs} --> ~${ylw}%g${rs})\n", expr, expr->cost, swapped_cost); + cfprintf(opt->ctx->cerr, "%pe (~${ylw}%g${rs} --> ~${ylw}%g${rs})\n", expr, expr->cost, swapped_cost); } expr->cost = swapped_cost; return true; @@ -1311,7 +1307,7 @@ static bool reorder_expr(const struct opt_state *state, struct bfs_expr *expr, f * @return * Whether any subexpression was reordered. */ -static bool reorder_expr_recursive(const struct opt_state *state, struct bfs_expr *expr) { +static bool reorder_expr_recursive(const struct bfs_opt *opt, struct bfs_expr *expr) { if (!bfs_expr_is_parent(expr)) { return false; } @@ -1321,17 +1317,17 @@ static bool reorder_expr_recursive(const struct opt_state *state, struct bfs_exp bool ret = false; if (lhs) { - ret |= reorder_expr_recursive(state, lhs); + ret |= reorder_expr_recursive(opt, lhs); } if (rhs) { - ret |= reorder_expr_recursive(state, rhs); + ret |= reorder_expr_recursive(opt, rhs); } if (expr->eval_fn == eval_and || expr->eval_fn == eval_or) { if (lhs->pure && rhs->pure) { float rhs_prob = expr->eval_fn == eval_and ? rhs->probability : 1.0 - rhs->probability; float swapped_cost = rhs->cost + rhs_prob * lhs->cost; - ret |= reorder_expr(state, expr, swapped_cost); + ret |= reorder_expr(opt, expr, swapped_cost); } } @@ -1341,18 +1337,18 @@ static bool reorder_expr_recursive(const struct opt_state *state, struct bfs_exp /** * Optimize a top-level expression. */ -static struct bfs_expr *optimize_expr(struct opt_state *state, struct bfs_expr *expr) { - struct opt_facts saved_impure = *state->facts_when_impure; +static struct bfs_expr *optimize_expr(struct bfs_opt *opt, struct bfs_expr *expr) { + struct df_domain saved_impure = *opt->impure; - expr = optimize_expr_recursive(state, expr); + expr = optimize_expr_recursive(opt, expr); if (!expr) { return NULL; } - if (state->ctx->optlevel >= 3 && reorder_expr_recursive(state, expr)) { + if (opt->ctx->optlevel >= 3 && reorder_expr_recursive(opt, expr)) { // Re-do optimizations to account for the new ordering - *state->facts_when_impure = saved_impure; - expr = optimize_expr_recursive(state, expr); + *opt->impure = saved_impure; + expr = optimize_expr_recursive(opt, expr); if (!expr) { return NULL; } @@ -1364,41 +1360,41 @@ static struct bfs_expr *optimize_expr(struct opt_state *state, struct bfs_expr * 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 df_domain impure; + df_init_bottom(&impure); - struct opt_state state = { + struct bfs_opt opt = { .ctx = ctx, - .facts_when_impure = &facts_when_impure, + .impure = &impure, }; - facts_init(&state.facts); + df_init_top(&opt.before); - ctx->exclude = optimize_expr(&state, ctx->exclude); + ctx->exclude = optimize_expr(&opt, ctx->exclude); if (!ctx->exclude) { return -1; } // Only non-excluded files are evaluated - state.facts = state.facts_when_false; + opt.before = opt.after_false; - struct range *depth = &state.facts.ranges[DEPTH_RANGE]; + struct df_range *depth = &opt.before.ranges[DEPTH_RANGE]; constrain_min(depth, ctx->mindepth); constrain_max(depth, ctx->maxdepth); - ctx->expr = optimize_expr(&state, ctx->expr); + ctx->expr = optimize_expr(&opt, ctx->expr); if (!ctx->expr) { return -1; } - ctx->expr = ignore_result(&state, ctx->expr); + ctx->expr = ignore_result(&opt, ctx->expr); - if (facts_are_impossible(&facts_when_impure)) { + if (df_is_bottom(&impure)) { bfs_warning(ctx, "This command won't do anything.\n\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; + const struct df_range *impure_depth = &impure.ranges[DEPTH_RANGE]; + long long mindepth = impure_depth->min; + long long maxdepth = impure_depth->max; int optlevel = ctx->optlevel; @@ -1407,7 +1403,7 @@ int bfs_optimize(struct bfs_ctx *ctx) { mindepth = INT_MAX; } ctx->mindepth = mindepth; - opt_debug(&state, 2, "data flow: mindepth --> %d\n", ctx->mindepth); + opt_debug(&opt, 2, "data flow: mindepth --> %d\n", ctx->mindepth); } if (optlevel >= 4 && maxdepth < ctx->maxdepth) { @@ -1415,7 +1411,7 @@ int bfs_optimize(struct bfs_ctx *ctx) { maxdepth = INT_MIN; } ctx->maxdepth = maxdepth; - opt_debug(&state, 4, "data flow: maxdepth --> %d\n", ctx->maxdepth); + opt_debug(&opt, 4, "data flow: maxdepth --> %d\n", ctx->maxdepth); } return 0; -- cgit v1.2.3 From 70092ae5c8f83a99fbc98dc8e2ca2eaab676a5a8 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 20 Dec 2023 20:18:35 -0500 Subject: expr: Arena-allocate expressions --- src/ctx.c | 11 +- src/ctx.h | 6 ++ src/expr.c | 22 ++-- src/expr.h | 19 +++- src/opt.c | 113 +++++++------------- src/parse.c | 340 +++++++++++++++++++++++------------------------------------- 6 files changed, 210 insertions(+), 301 deletions(-) (limited to 'src/opt.c') diff --git a/src/ctx.c b/src/ctx.c index f5dc465..a3c5140 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -22,6 +22,9 @@ struct bfs_ctx *bfs_ctx_new(void) { return NULL; } + SLIST_INIT(&ctx->expr_list); + ARENA_INIT(&ctx->expr_arena, struct bfs_expr); + ctx->maxdepth = INT_MAX; ctx->flags = BFTW_RECOVER; ctx->strategy = BFTW_BFS; @@ -202,9 +205,6 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { CFILE *cout = ctx->cout; CFILE *cerr = ctx->cerr; - bfs_expr_free(ctx->exclude); - bfs_expr_free(ctx->expr); - bfs_mtab_free(ctx->mtab); bfs_groups_free(ctx->groups); @@ -241,6 +241,11 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { free_colors(ctx->colors); + for_slist (struct bfs_expr, expr, &ctx->expr_list) { + bfs_expr_clear(expr); + } + arena_destroy(&ctx->expr_arena); + for (size_t i = 0; i < ctx->npaths; ++i) { free((char *)ctx->paths[i]); } diff --git a/src/ctx.h b/src/ctx.h index 75891da..aa91f2c 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -8,9 +8,11 @@ #ifndef BFS_CTX_H #define BFS_CTX_H +#include "alloc.h" #include "bftw.h" #include "config.h" #include "diag.h" +#include "expr.h" #include "trie.h" #include #include @@ -34,6 +36,10 @@ struct bfs_ctx { struct bfs_expr *expr; /** An expression for files to filter out. */ struct bfs_expr *exclude; + /** A list of allocated expressions. */ + struct bfs_exprs expr_list; + /** bfs_expr arena. */ + struct arena expr_arena; /** -mindepth option. */ int mindepth; diff --git a/src/expr.c b/src/expr.c index 380038c..2002fc7 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3,24 +3,27 @@ #include "expr.h" #include "alloc.h" +#include "ctx.h" #include "eval.h" #include "exec.h" #include "printf.h" #include "xregex.h" #include #include +#include -struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { - struct bfs_expr *expr = ZALLOC(struct bfs_expr); +struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval_fn, size_t argc, char **argv) { + struct bfs_expr *expr = arena_alloc(&ctx->expr_arena); if (!expr) { - perror("zalloc()"); return NULL; } + memset(expr, 0, sizeof(*expr)); expr->eval_fn = eval_fn; expr->argc = argc; expr->argv = argv; expr->probability = 0.5; + SLIST_PREPEND(&ctx->expr_list, expr); return expr; } @@ -36,21 +39,12 @@ bool bfs_expr_never_returns(const struct bfs_expr *expr) { return expr->always_true && expr->always_false; } -void bfs_expr_free(struct bfs_expr *expr) { - if (!expr) { - return; - } - - if (bfs_expr_is_parent(expr)) { - bfs_expr_free(expr->rhs); - bfs_expr_free(expr->lhs); - } else if (expr->eval_fn == eval_exec) { +void bfs_expr_clear(struct bfs_expr *expr) { + if (expr->eval_fn == eval_exec) { bfs_exec_free(expr->exec); } else if (expr->eval_fn == eval_fprintf) { bfs_printf_free(expr->printf); } else if (expr->eval_fn == eval_regex) { bfs_regfree(expr->regex); } - - free(expr); } diff --git a/src/expr.h b/src/expr.h index 95118b9..290f1f8 100644 --- a/src/expr.h +++ b/src/expr.h @@ -74,10 +74,21 @@ enum bfs_size_unit { BFS_PB, }; +/** + * A linked list of expressions. + */ +struct bfs_exprs { + struct bfs_expr *head; + struct bfs_expr **tail; +}; + /** * A command line expression. */ struct bfs_expr { + /** The next allocated expression. */ + struct bfs_expr *next; + /** The function that evaluates this expression. */ bfs_eval_fn *eval_fn; @@ -199,10 +210,12 @@ struct bfs_expr { }; }; +struct bfs_ctx; + /** * Create a new expression. */ -struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval, size_t argc, char **argv); +struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval, size_t argc, char **argv); /** * @return Whether the expression has child expressions. @@ -220,8 +233,8 @@ bool bfs_expr_never_returns(const struct bfs_expr *expr); bool bfs_expr_cmp(const struct bfs_expr *expr, long long n); /** - * Free an expression tree. + * Free any resources owned by an expression. */ -void bfs_expr_free(struct bfs_expr *expr); +void bfs_expr_clear(struct bfs_expr *expr); #endif // BFS_EXPR_H diff --git a/src/opt.c b/src/opt.c index a989670..1dc985a 100644 --- a/src/opt.c +++ b/src/opt.c @@ -284,7 +284,7 @@ static void df_join(struct df_domain *dest, const struct df_domain *src) { */ struct bfs_opt { /** The context we're optimizing. */ - const struct bfs_ctx *ctx; + struct bfs_ctx *ctx; /** Data flow state before this expression is evaluated. */ struct df_domain before; @@ -330,31 +330,22 @@ static void opt_warning(const struct bfs_opt *opt, const struct bfs_expr *expr, } /** Create a constant expression. */ -static struct bfs_expr *opt_const(bool value) { +static struct bfs_expr *opt_const(const struct bfs_opt *opt, bool value) { static bfs_eval_fn *fns[] = {eval_false, eval_true}; static char *fake_args[] = {"-false", "-true"}; - return bfs_expr_new(fns[value], 1, &fake_args[value]); -} - -/** Extract a child expression, freeing the outer expression. */ -static struct bfs_expr *extract_child_expr(struct bfs_expr *expr, struct bfs_expr **child) { - struct bfs_expr *ret = *child; - *child = NULL; - bfs_expr_free(expr); - return ret; + return bfs_expr_new(opt->ctx, fns[value], 1, &fake_args[value]); } /** * Negate an expression. */ -static struct bfs_expr *negate_expr(struct bfs_expr *rhs, char **argv) { +static struct bfs_expr *negate_expr(const struct bfs_opt *opt, struct bfs_expr *rhs, char **argv) { if (rhs->eval_fn == eval_not) { - return extract_child_expr(rhs, &rhs->rhs); + return rhs->rhs; } - struct bfs_expr *expr = bfs_expr_new(eval_not, 1, argv); + struct bfs_expr *expr = bfs_expr_new(opt->ctx, eval_not, 1, argv); if (!expr) { - bfs_expr_free(rhs); return NULL; } @@ -373,7 +364,7 @@ static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_e static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *expr, char **argv) { bool debug = opt_debug(opt, 1, "De Morgan's laws: %pe ", expr); - struct bfs_expr *parent = negate_expr(expr, argv); + struct bfs_expr *parent = negate_expr(opt, expr, argv); if (!parent) { return NULL; } @@ -393,10 +384,9 @@ static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *ex expr->argv = &fake_and_arg; } - expr->lhs = negate_expr(expr->lhs, argv); - expr->rhs = negate_expr(expr->rhs, argv); + expr->lhs = negate_expr(opt, expr->lhs, argv); + expr->rhs = negate_expr(opt, expr->rhs, argv); if (!expr->lhs || !expr->rhs) { - bfs_expr_free(parent); return NULL; } @@ -411,7 +401,6 @@ static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *ex expr->rhs = optimize_not_expr(opt, expr->rhs); } if (!expr->lhs || !expr->rhs) { - bfs_expr_free(parent); return NULL; } @@ -426,7 +415,6 @@ static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *ex parent = expr; } if (!expr) { - bfs_expr_free(parent); return NULL; } @@ -450,16 +438,15 @@ static struct bfs_expr *optimize_not_expr(const struct bfs_opt *opt, struct bfs_ int optlevel = opt->ctx->optlevel; if (optlevel >= 1) { if (rhs->eval_fn == eval_true || rhs->eval_fn == eval_false) { - struct bfs_expr *ret = opt_const(rhs->eval_fn == eval_false); + struct bfs_expr *ret = opt_const(opt, rhs->eval_fn == eval_false); opt_debug(opt, 1, "constant propagation: %pe <==> %pe\n", expr, ret); - bfs_expr_free(expr); return ret; } else if (rhs->eval_fn == eval_not) { opt_debug(opt, 1, "double negation: %pe <==> %pe\n", expr, rhs->rhs); - return extract_child_expr(expr, &rhs->rhs); + return rhs->rhs; } else if (bfs_expr_never_returns(rhs)) { opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, rhs); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if ((rhs->eval_fn == eval_and || rhs->eval_fn == eval_or) && (rhs->lhs->eval_fn == eval_not || rhs->rhs->eval_fn == eval_not)) { return de_morgan(opt, expr, expr->argv); @@ -480,17 +467,13 @@ static struct bfs_expr *optimize_not_expr_recursive(struct bfs_opt *opt, struct struct bfs_opt rhs_state = *opt; expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { - goto fail; + return NULL; } opt->after_true = rhs_state.after_false; opt->after_false = rhs_state.after_true; return optimize_not_expr(opt, expr); - -fail: - bfs_expr_free(expr); - return NULL; } /** Optimize a conjunction. */ @@ -505,18 +488,18 @@ static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_ if (optlevel >= 1) { if (lhs->eval_fn == eval_true) { opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, rhs); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if (rhs->eval_fn == eval_true) { opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, lhs); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (lhs->always_false) { opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (lhs->always_true && rhs->eval_fn == eval_false) { bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); - struct bfs_expr *ret = extract_child_expr(expr, &expr->lhs); - ret = negate_expr(ret, &fake_not_arg); + struct bfs_expr *ret = expr->lhs; + ret = negate_expr(opt, ret, &fake_not_arg); if (debug && ret) { cfprintf(ctx->cerr, "%pe\n", ret); } @@ -524,7 +507,7 @@ static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_ } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_false) { opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { return de_morgan(opt, expr, expr->lhs->argv); } @@ -544,14 +527,14 @@ static struct bfs_expr *optimize_and_expr_recursive(struct bfs_opt *opt, struct struct bfs_opt lhs_state = *opt; expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); if (!expr->lhs) { - goto fail; + return NULL; } struct bfs_opt rhs_state = *opt; rhs_state.before = lhs_state.after_true; expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { - goto fail; + return NULL; } opt->after_true = rhs_state.after_true; @@ -559,10 +542,6 @@ static struct bfs_expr *optimize_and_expr_recursive(struct bfs_opt *opt, struct df_join(&opt->after_false, &rhs_state.after_false); return optimize_and_expr(opt, expr); - -fail: - bfs_expr_free(expr); - return NULL; } /** Optimize a disjunction. */ @@ -578,17 +557,17 @@ static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_e if (lhs->always_true) { opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (lhs->eval_fn == eval_false) { opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, rhs); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if (rhs->eval_fn == eval_false) { opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, lhs); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (lhs->always_false && rhs->eval_fn == eval_true) { bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); - struct bfs_expr *ret = extract_child_expr(expr, &expr->lhs); - ret = negate_expr(ret, &fake_not_arg); + struct bfs_expr *ret = expr->lhs; + ret = negate_expr(opt, ret, &fake_not_arg); if (debug && ret) { cfprintf(ctx->cerr, "%pe\n", ret); } @@ -596,7 +575,7 @@ static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_e } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_true) { opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { return de_morgan(opt, expr, expr->lhs->argv); } @@ -616,14 +595,14 @@ static struct bfs_expr *optimize_or_expr_recursive(struct bfs_opt *opt, struct b struct bfs_opt lhs_state = *opt; expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); if (!expr->lhs) { - goto fail; + return NULL; } struct bfs_opt rhs_state = *opt; rhs_state.before = lhs_state.after_false; expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { - goto fail; + return NULL; } opt->after_false = rhs_state.after_false; @@ -631,10 +610,6 @@ static struct bfs_expr *optimize_or_expr_recursive(struct bfs_opt *opt, struct b df_join(&opt->after_true, &rhs_state.after_true); return optimize_or_expr(opt, expr); - -fail: - bfs_expr_free(expr); - return NULL; } /** Optimize an expression in an ignored-result context. */ @@ -646,23 +621,22 @@ static struct bfs_expr *ignore_result(const struct bfs_opt *opt, struct bfs_expr if (expr->eval_fn == eval_not) { opt_debug(opt, 1, "ignored result: %pe --> %pe\n", expr, expr->rhs); opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); - expr = extract_child_expr(expr, &expr->rhs); + expr = expr->rhs; } else if (optlevel >= 2 && (expr->eval_fn == eval_and || expr->eval_fn == eval_or || expr->eval_fn == eval_comma) && expr->rhs->pure) { opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, expr->lhs); opt_warning(opt, expr->rhs, "The result of this expression is ignored.\n\n"); - expr = extract_child_expr(expr, &expr->lhs); + expr = expr->lhs; } else { break; } } if (optlevel >= 2 && expr->pure && expr->eval_fn != eval_false) { - struct bfs_expr *ret = opt_const(false); + struct bfs_expr *ret = opt_const(opt, false); opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, ret); opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); - bfs_expr_free(expr); return ret; } } @@ -684,15 +658,15 @@ static struct bfs_expr *optimize_comma_expr(const struct bfs_opt *opt, struct bf if (bfs_expr_never_returns(lhs)) { opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, lhs); opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if ((lhs->always_true && rhs->eval_fn == eval_true) || (lhs->always_false && rhs->eval_fn == eval_false)) { opt_debug(opt, 1, "redundancy elimination: %pe <==> %pe\n", expr, lhs); - return extract_child_expr(expr, &expr->lhs); + return expr->lhs; } else if (optlevel >= 2 && lhs->pure) { opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return extract_child_expr(expr, &expr->rhs); + return expr->rhs; } } @@ -710,7 +684,7 @@ static struct bfs_expr *optimize_comma_expr_recursive(struct bfs_opt *opt, struc struct bfs_opt lhs_state = *opt; expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); if (!expr->lhs) { - goto fail; + return NULL; } struct bfs_opt rhs_state = *opt; @@ -719,14 +693,10 @@ static struct bfs_expr *optimize_comma_expr_recursive(struct bfs_opt *opt, struc expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); if (!expr->rhs) { - goto fail; + return NULL; } return optimize_comma_expr(opt, expr); - -fail: - bfs_expr_free(expr); - return NULL; } /** Optimize an icmp-style ([+-]N) expression. */ @@ -1213,10 +1183,9 @@ static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_ opt->after_false = opt->before; if (optlevel >= 2 && df_is_bottom(&opt->before)) { - struct bfs_expr *ret = opt_const(false); + struct bfs_expr *ret = opt_const(opt, false); opt_debug(opt, 2, "reachability: %pe --> %pe\n", expr, ret); opt_warning(opt, expr, "This expression is unreachable.\n\n"); - bfs_expr_free(expr); return ret; } @@ -1257,10 +1226,9 @@ static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_ if (df_is_bottom(&opt->after_true)) { if (expr->pure) { - struct bfs_expr *ret = opt_const(false); + struct bfs_expr *ret = opt_const(opt, false); opt_warning(opt, expr, "This expression is always false.\n\n"); opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); - bfs_expr_free(expr); return ret; } else { expr->always_false = true; @@ -1268,10 +1236,9 @@ static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_ } } else if (df_is_bottom(&opt->after_false)) { if (expr->pure) { - struct bfs_expr *ret = opt_const(true); + struct bfs_expr *ret = opt_const(opt, true); opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); opt_warning(opt, expr, "This expression is always true.\n\n"); - bfs_expr_free(expr); return ret; } else { expr->always_true = true; diff --git a/src/parse.c b/src/parse.c index 18a1064..17fe8ad 100644 --- a/src/parse.c +++ b/src/parse.c @@ -54,50 +54,6 @@ static char *fake_or_arg = "-o"; static char *fake_print_arg = "-print"; static char *fake_true_arg = "-true"; -/** - * Create a new unary expression. - */ -static struct bfs_expr *new_unary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *rhs, char **argv) { - struct bfs_expr *expr = bfs_expr_new(eval_fn, 1, argv); - if (!expr) { - bfs_expr_free(rhs); - return NULL; - } - - expr->lhs = NULL; - expr->rhs = rhs; - bfs_assert(bfs_expr_is_parent(expr)); - - expr->persistent_fds = rhs->persistent_fds; - expr->ephemeral_fds = rhs->ephemeral_fds; - return expr; -} - -/** - * Create a new binary expression. - */ -static struct bfs_expr *new_binary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *lhs, struct bfs_expr *rhs, char **argv) { - struct bfs_expr *expr = bfs_expr_new(eval_fn, 1, argv); - if (!expr) { - bfs_expr_free(rhs); - bfs_expr_free(lhs); - return NULL; - } - - expr->lhs = lhs; - expr->rhs = rhs; - bfs_assert(bfs_expr_is_parent(expr)); - - expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; - if (lhs->ephemeral_fds > rhs->ephemeral_fds) { - expr->ephemeral_fds = lhs->ephemeral_fds; - } else { - expr->ephemeral_fds = rhs->ephemeral_fds; - } - - return expr; -} - /** * Color use flags. */ @@ -343,6 +299,58 @@ static bool parse_expr_warning(const struct bfs_parser *parser, const struct bfs return ret; } +/** + * Allocate a new expression. + */ +static struct bfs_expr *parse_new_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc, char **argv) { + struct bfs_expr *expr = bfs_expr_new(parser->ctx, eval_fn, argc, argv); + if (!expr) { + parse_perror(parser, "bfs_expr_new()"); + } + return expr; +} + +/** + * Create a new unary expression. + */ +static struct bfs_expr *new_unary_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, struct bfs_expr *rhs, char **argv) { + struct bfs_expr *expr = parse_new_expr(parser, eval_fn, 1, argv); + if (!expr) { + return NULL; + } + + expr->lhs = NULL; + expr->rhs = rhs; + bfs_assert(bfs_expr_is_parent(expr)); + + expr->persistent_fds = rhs->persistent_fds; + expr->ephemeral_fds = rhs->ephemeral_fds; + return expr; +} + +/** + * Create a new binary expression. + */ +static struct bfs_expr *new_binary_expr(const struct bfs_parser *parser, bfs_eval_fn *eval_fn, struct bfs_expr *lhs, struct bfs_expr *rhs, char **argv) { + struct bfs_expr *expr = parse_new_expr(parser, eval_fn, 1, argv); + if (!expr) { + return NULL; + } + + expr->lhs = lhs; + expr->rhs = rhs; + bfs_assert(bfs_expr_is_parent(expr)); + + expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; + if (lhs->ephemeral_fds > rhs->ephemeral_fds) { + expr->ephemeral_fds = lhs->ephemeral_fds; + } else { + expr->ephemeral_fds = rhs->ephemeral_fds; + } + + return expr; +} + /** * Fill in a "-print"-type expression. */ @@ -645,7 +653,7 @@ static bool looks_like_icmp(const char *str) { */ static struct bfs_expr *parse_flag(struct bfs_parser *parser, size_t argc) { char **argv = parser_advance(parser, T_FLAG, argc); - return bfs_expr_new(eval_true, argc, argv); + return parse_new_expr(parser, eval_true, argc, argv); } /** @@ -674,7 +682,7 @@ static struct bfs_expr *parse_unary_flag(struct bfs_parser *parser) { */ static struct bfs_expr *parse_option(struct bfs_parser *parser, size_t argc) { char **argv = parser_advance(parser, T_OPTION, argc); - return bfs_expr_new(eval_true, argc, argv); + return parse_new_expr(parser, eval_true, argc, argv); } /** @@ -703,7 +711,7 @@ static struct bfs_expr *parse_unary_option(struct bfs_parser *parser) { */ static struct bfs_expr *parse_test(struct bfs_parser *parser, bfs_eval_fn *eval_fn, size_t argc) { char **argv = parser_advance(parser, T_TEST, argc); - return bfs_expr_new(eval_fn, argc, argv); + return parse_new_expr(parser, eval_fn, argc, argv); } /** @@ -742,7 +750,7 @@ static struct bfs_expr *parse_action(struct bfs_parser *parser, bfs_eval_fn *eva parser->implicit_print = false; } - return bfs_expr_new(eval_fn, argc, argv); + return parse_new_expr(parser, eval_fn, argc, argv); } /** @@ -771,7 +779,7 @@ static struct bfs_expr *parse_unary_action(struct bfs_parser *parser, bfs_eval_f */ static int parse_exclude(struct bfs_parser *parser, struct bfs_expr *expr) { struct bfs_ctx *ctx = parser->ctx; - ctx->exclude = new_binary_expr(eval_or, ctx->exclude, expr, &fake_or_arg); + ctx->exclude = new_binary_expr(parser, eval_or, ctx->exclude, expr, &fake_or_arg); if (ctx->exclude) { return 0; } else { @@ -789,7 +797,6 @@ static struct bfs_expr *parse_test_icmp(struct bfs_parser *parser, bfs_eval_fn * } if (!parse_icmp(parser, expr, 0)) { - bfs_expr_free(expr); return NULL; } @@ -848,7 +855,6 @@ static struct bfs_expr *parse_debug(struct bfs_parser *parser, int arg1, int arg if (parse_debug_flag(flag, len, "help")) { debug_help(ctx->cout); parser->just_info = true; - bfs_expr_free(expr); return NULL; } else if (parse_debug_flag(flag, len, "all")) { ctx->debug = DEBUG_ALL; @@ -896,7 +902,6 @@ static struct bfs_expr *parse_optlevel(struct bfs_parser *parser, int arg1, int if (strcmp(expr->argv[0], "-Ofast") == 0) { *optlevel = 4; } else if (!parse_int(parser, expr->argv, expr->argv[0] + 2, optlevel, IF_INT | IF_UNSIGNED)) { - bfs_expr_free(expr); return NULL; } @@ -963,16 +968,12 @@ static struct bfs_expr *parse_newer(struct bfs_parser *parser, int field, int ar struct bfs_stat sb; if (stat_arg(parser, &expr->argv[1], &sb) != 0) { - goto fail; + return NULL; } expr->reftime = sb.mtime; expr->stat_field = field; return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1004,7 +1005,7 @@ static struct bfs_expr *parse_time(struct bfs_parser *parser, int field, int arg const char *tail = parse_icmp(parser, expr, IF_PARTIAL_OK); if (!tail) { - goto fail; + return NULL; } if (!*tail) { @@ -1033,7 +1034,7 @@ static struct bfs_expr *parse_time(struct bfs_parser *parser, int field, int arg break; default: parse_expr_error(parser, expr, "Unknown time unit ${bld}%c${rs}.\n", *tail); - goto fail; + return NULL; } expr->num += time; @@ -1044,20 +1045,16 @@ static struct bfs_expr *parse_time(struct bfs_parser *parser, int field, int arg tail = parse_int(parser, &expr->argv[1], tail, &time, IF_PARTIAL_OK | IF_LONG_LONG | IF_UNSIGNED); if (!tail) { - goto fail; + return NULL; } if (!*tail) { parse_expr_error(parser, expr, "Missing time unit.\n"); - goto fail; + return NULL; } } expr->time_unit = BFS_SECONDS; return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1087,7 +1084,6 @@ static struct bfs_expr *parse_color(struct bfs_parser *parser, int color, int ar if (color) { if (!colors) { parse_expr_error(parser, expr, "Error parsing $$LS_COLORS: %s.\n", xstrerror(ctx->colors_error)); - bfs_expr_free(expr); return NULL; } @@ -1182,7 +1178,6 @@ static struct bfs_expr *parse_depth_limit(struct bfs_parser *parser, int is_min, int *depth = is_min ? &ctx->mindepth : &ctx->maxdepth; char **arg = &expr->argv[1]; if (!parse_int(parser, arg, *arg, depth, IF_INT | IF_UNSIGNED)) { - bfs_expr_free(expr); return NULL; } @@ -1235,7 +1230,7 @@ static struct bfs_expr *parse_exec(struct bfs_parser *parser, int flags, int arg } else { parse_perror(parser, "strndup()"); } - goto fail; + return NULL; } path = strchr(path, ':'); @@ -1257,10 +1252,6 @@ static struct bfs_expr *parse_exec(struct bfs_parser *parser, int flags, int arg } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1292,7 +1283,6 @@ static struct bfs_expr *parse_f(struct bfs_parser *parser, int arg1, int arg2) { } if (parse_root(parser, expr->argv[1]) != 0) { - bfs_expr_free(expr); return NULL; } @@ -1318,7 +1308,7 @@ static struct bfs_expr *parse_files0_from(struct bfs_parser *parser, int arg1, i } if (!file) { parse_expr_error(parser, expr, "%m.\n"); - goto fail; + return NULL; } while (true) { @@ -1348,10 +1338,9 @@ static struct bfs_expr *parse_files0_from(struct bfs_parser *parser, int arg1, i return expr; fail: - if (file && file != stdin) { + if (file != stdin) { fclose(file); } - bfs_expr_free(expr); return NULL; } @@ -1385,7 +1374,6 @@ static struct bfs_expr *parse_flags(struct bfs_parser *parser, int arg1, int arg } else { parse_expr_error(parser, expr, "Invalid flags.\n"); } - bfs_expr_free(expr); return NULL; } @@ -1398,18 +1386,14 @@ static struct bfs_expr *parse_flags(struct bfs_parser *parser, int arg1, int arg static struct bfs_expr *parse_fls(struct bfs_parser *parser, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(parser, eval_fls); if (!expr) { - goto fail; + return NULL; } if (expr_open(parser, expr, expr->argv[1]) != 0) { - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1417,16 +1401,15 @@ fail: */ static struct bfs_expr *parse_fprint(struct bfs_parser *parser, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(parser, eval_fprint); - if (expr) { - if (expr_open(parser, expr, expr->argv[1]) != 0) { - goto fail; - } + if (!expr) { + return NULL; } - return expr; -fail: - bfs_expr_free(expr); - return NULL; + if (expr_open(parser, expr, expr->argv[1]) != 0) { + return NULL; + } + + return expr; } /** @@ -1434,16 +1417,15 @@ fail: */ static struct bfs_expr *parse_fprint0(struct bfs_parser *parser, int arg1, int arg2) { struct bfs_expr *expr = parse_unary_action(parser, eval_fprint0); - if (expr) { - if (expr_open(parser, expr, expr->argv[1]) != 0) { - goto fail; - } + if (!expr) { + return NULL; } - return expr; -fail: - bfs_expr_free(expr); - return NULL; + if (expr_open(parser, expr, expr->argv[1]) != 0) { + return NULL; + } + + return expr; } /** @@ -1470,18 +1452,14 @@ static struct bfs_expr *parse_fprintf(struct bfs_parser *parser, int arg1, int a } if (expr_open(parser, expr, file) != 0) { - goto fail; + return NULL; } if (bfs_printf_parse(parser->ctx, expr, format) != 0) { - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1495,7 +1473,6 @@ static struct bfs_expr *parse_fstype(struct bfs_parser *parser, int arg1, int ar if (!bfs_ctx_mtab(parser->ctx)) { parse_expr_error(parser, expr, "Couldn't parse the mount table: %m.\n"); - bfs_expr_free(expr); return NULL; } @@ -1517,21 +1494,17 @@ static struct bfs_expr *parse_group(struct bfs_parser *parser, int arg1, int arg expr->int_cmp = BFS_INT_EQUAL; } else if (looks_like_icmp(expr->argv[1])) { if (!parse_icmp(parser, expr, 0)) { - goto fail; + return NULL; } } else if (errno) { parse_expr_error(parser, expr, "%m.\n"); - goto fail; + return NULL; } else { parse_expr_error(parser, expr, "No such group.\n"); - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1564,21 +1537,17 @@ static struct bfs_expr *parse_user(struct bfs_parser *parser, int arg1, int arg2 expr->int_cmp = BFS_INT_EQUAL; } else if (looks_like_icmp(expr->argv[1])) { if (!parse_icmp(parser, expr, 0)) { - goto fail; + return NULL; } } else if (errno) { parse_expr_error(parser, expr, "%m.\n"); - goto fail; + return NULL; } else { parse_expr_error(parser, expr, "No such user.\n"); - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1614,13 +1583,11 @@ static struct bfs_expr *parse_jobs(struct bfs_parser *parser, int arg1, int arg2 unsigned int n; if (!parse_int(parser, expr->argv, expr->argv[0] + 2, &n, IF_INT | IF_UNSIGNED)) { - bfs_expr_free(expr); return NULL; } if (n == 0) { parse_expr_error(parser, expr, "${bld}0${rs} is not enough threads.\n"); - bfs_expr_free(expr); return NULL; } @@ -1680,7 +1647,6 @@ static struct bfs_expr *parse_fnmatch(const struct bfs_parser *parser, struct bf expr->fnm_flags = FNM_CASEFOLD; #else parse_expr_error(parser, expr, "Missing platform support.\n"); - bfs_expr_free(expr); return NULL; #endif } else { @@ -1811,7 +1777,7 @@ static struct bfs_expr *parse_newerxy(struct bfs_parser *parser, int arg1, int a struct bfs_expr *expr = parse_unary_test(parser, eval_newer); if (!expr) { - goto fail; + return NULL; } expr->stat_field = parse_newerxy_field(arg[6]); @@ -1819,12 +1785,12 @@ static struct bfs_expr *parse_newerxy(struct bfs_parser *parser, int arg1, int a parse_expr_error(parser, expr, "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n", arg[6]); - goto fail; + return NULL; } if (arg[7] == 't') { if (parse_reftime(parser, expr) != 0) { - goto fail; + return NULL; } } else { enum bfs_stat_field field = parse_newerxy_field(arg[7]); @@ -1832,28 +1798,24 @@ static struct bfs_expr *parse_newerxy(struct bfs_parser *parser, int arg1, int a parse_expr_error(parser, expr, "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n", arg[7]); - goto fail; + return NULL; } struct bfs_stat sb; if (stat_arg(parser, &expr->argv[1], &sb) != 0) { - goto fail; + return NULL; } const struct timespec *reftime = bfs_stat_time(&sb, field); if (!reftime) { parse_expr_error(parser, expr, "Couldn't get file %s.\n", bfs_stat_field_name(field)); - goto fail; + return NULL; } expr->reftime = *reftime; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -1872,7 +1834,7 @@ static struct bfs_expr *parse_nogroup(struct bfs_parser *parser, int arg1, int a * Parse -nohidden. */ static struct bfs_expr *parse_nohidden(struct bfs_parser *parser, int arg1, int arg2) { - struct bfs_expr *hidden = bfs_expr_new(eval_hidden, 1, &fake_hidden_arg); + struct bfs_expr *hidden = parse_new_expr(parser, eval_hidden, 1, &fake_hidden_arg); if (!hidden) { return NULL; } @@ -2158,14 +2120,10 @@ static struct bfs_expr *parse_perm(struct bfs_parser *parser, int field, int arg } if (parse_mode(parser, mode, expr) != 0) { - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -2202,7 +2160,6 @@ static struct bfs_expr *parse_printf(struct bfs_parser *parser, int arg1, int ar init_print_expr(parser, expr); if (bfs_printf_parse(parser->ctx, expr, expr->argv[1]) != 0) { - bfs_expr_free(expr); return NULL; } @@ -2241,31 +2198,26 @@ static struct bfs_expr *parse_quit(struct bfs_parser *parser, int arg1, int arg2 static struct bfs_expr *parse_regex(struct bfs_parser *parser, int flags, int arg2) { struct bfs_expr *expr = parse_unary_test(parser, eval_regex); if (!expr) { - goto fail; + return NULL; } if (bfs_regcomp(&expr->regex, expr->argv[1], parser->regex_type, flags) != 0) { - if (!expr->regex) { + if (expr->regex) { + char *str = bfs_regerror(expr->regex); + if (str) { + parse_expr_error(parser, expr, "%s.\n", str); + free(str); + } else { + parse_perror(parser, "bfs_regerror()"); + } + } else { parse_perror(parser, "bfs_regcomp()"); - goto fail; } - char *str = bfs_regerror(expr->regex); - if (!str) { - parse_perror(parser, "bfs_regerror()"); - goto fail; - } - - parse_expr_error(parser, expr, "%s.\n", str); - free(str); - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -2324,8 +2276,6 @@ list_types: cfprintf(cfile, " ${bld}grep${rs}: Like ${grn}grep${rs}\n"); #endif cfprintf(cfile, " ${bld}sed${rs}: Like ${grn}sed${rs} (same as ${bld}posix-basic${rs})\n"); - - bfs_expr_free(expr); return NULL; } @@ -2348,7 +2298,6 @@ static struct bfs_expr *parse_samefile(struct bfs_parser *parser, int arg1, int struct bfs_stat sb; if (stat_arg(parser, &expr->argv[1], &sb) != 0) { - bfs_expr_free(expr); return NULL; } @@ -2396,8 +2345,6 @@ list_strategies: cfprintf(cfile, " ${bld}dfs${rs}: depth-first search\n"); cfprintf(cfile, " ${bld}ids${rs}: iterative deepening search\n"); cfprintf(cfile, " ${bld}eds${rs}: exponential deepening search\n"); - - bfs_expr_free(expr); return NULL; } @@ -2411,15 +2358,11 @@ static struct bfs_expr *parse_since(struct bfs_parser *parser, int field, int ar } if (parse_reftime(parser, expr) != 0) { - goto fail; + return NULL; } expr->stat_field = field; return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -2433,7 +2376,7 @@ static struct bfs_expr *parse_size(struct bfs_parser *parser, int arg1, int arg2 const char *unit = parse_icmp(parser, expr, IF_PARTIAL_OK); if (!unit) { - goto fail; + return NULL; } if (strlen(unit) > 1) { @@ -2468,16 +2411,12 @@ static struct bfs_expr *parse_size(struct bfs_parser *parser, int arg1, int arg2 break; default: - goto bad_unit; + bad_unit: + parse_expr_error(parser, expr, "Expected a size unit (one of ${bld}cwbkMGTP${rs}); found ${err}%pq${rs}.\n", unit); + return NULL; } return expr; - -bad_unit: - parse_expr_error(parser, expr, "Expected a size unit (one of ${bld}cwbkMGTP${rs}); found ${err}%pq${rs}.\n", unit); -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -2543,11 +2482,11 @@ static struct bfs_expr *parse_type(struct bfs_parser *parser, int x, int arg2) { case '\0': parse_expr_error(parser, expr, "Expected a type flag.\n"); - goto fail; + return NULL; default: parse_expr_error(parser, expr, "Unknown type flag ${err}%c${rs}; expected one of [${bld}bcdpflsD${rs}].\n", *c); - goto fail; + return NULL; } ++c; @@ -2558,15 +2497,11 @@ static struct bfs_expr *parse_type(struct bfs_parser *parser, int x, int arg2) { continue; } else { parse_expr_error(parser, expr, "Types must be comma-separated.\n"); - goto fail; + return NULL; } } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } /** @@ -3244,14 +3179,12 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { } if (skip_paths(parser) != 0) { - bfs_expr_free(expr); return NULL; } arg = parser->argv[0]; if (!arg || strcmp(arg, ")") != 0) { parse_argv_error(parser, parser->last_arg, 1, "Expected a ${red})${rs}.\n"); - bfs_expr_free(expr); return NULL; } @@ -3277,7 +3210,7 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { return NULL; } - return bfs_expr_new(eval_true, parser->argv - argv, argv); + return parse_new_expr(parser, eval_true, parser->argv - argv, argv); } else if (strcmp(arg, "!") == 0 || strcmp(arg, "-not") == 0) { char **argv = parser_advance(parser, T_OPERATOR, 1); @@ -3286,7 +3219,7 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { return NULL; } - return new_unary_expr(eval_not, factor, argv); + return new_unary_expr(parser, eval_not, factor, argv); } else { return parse_primary(parser); } @@ -3303,7 +3236,6 @@ static struct bfs_expr *parse_term(struct bfs_parser *parser) { while (term) { if (skip_paths(parser) != 0) { - bfs_expr_free(term); return NULL; } @@ -3326,11 +3258,10 @@ static struct bfs_expr *parse_term(struct bfs_parser *parser) { struct bfs_expr *lhs = term; struct bfs_expr *rhs = parse_factor(parser); if (!rhs) { - bfs_expr_free(lhs); return NULL; } - term = new_binary_expr(eval_and, lhs, rhs, argv); + term = new_binary_expr(parser, eval_and, lhs, rhs, argv); } return term; @@ -3346,7 +3277,6 @@ static struct bfs_expr *parse_clause(struct bfs_parser *parser) { while (clause) { if (skip_paths(parser) != 0) { - bfs_expr_free(clause); return NULL; } @@ -3364,11 +3294,10 @@ static struct bfs_expr *parse_clause(struct bfs_parser *parser) { struct bfs_expr *lhs = clause; struct bfs_expr *rhs = parse_term(parser); if (!rhs) { - bfs_expr_free(lhs); return NULL; } - clause = new_binary_expr(eval_or, lhs, rhs, argv); + clause = new_binary_expr(parser, eval_or, lhs, rhs, argv); } return clause; @@ -3383,7 +3312,6 @@ static struct bfs_expr *parse_expr(struct bfs_parser *parser) { while (expr) { if (skip_paths(parser) != 0) { - bfs_expr_free(expr); return NULL; } @@ -3401,11 +3329,10 @@ static struct bfs_expr *parse_expr(struct bfs_parser *parser) { struct bfs_expr *lhs = expr; struct bfs_expr *rhs = parse_clause(parser); if (!rhs) { - bfs_expr_free(lhs); return NULL; } - expr = new_binary_expr(eval_comma, lhs, rhs, argv); + expr = new_binary_expr(parser, eval_comma, lhs, rhs, argv); } return expr; @@ -3423,7 +3350,7 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { if (parser->argv[0]) { expr = parse_expr(parser); } else { - expr = bfs_expr_new(eval_true, 1, &fake_true_arg); + expr = parse_new_expr(parser, eval_true, 1, &fake_true_arg); } if (!expr) { return NULL; @@ -3431,19 +3358,19 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { if (parser->argv[0]) { parse_error(parser, "Unexpected argument.\n"); - goto fail; + return NULL; } if (parser->implicit_print) { - struct bfs_expr *print = bfs_expr_new(eval_fprint, 1, &fake_print_arg); + struct bfs_expr *print = parse_new_expr(parser, eval_fprint, 1, &fake_print_arg); if (!print) { - goto fail; + return NULL; } init_print_expr(parser, print); - expr = new_binary_expr(eval_and, expr, print, &fake_and_arg); + expr = new_binary_expr(parser, eval_and, expr, print, &fake_and_arg); if (!expr) { - goto fail; + return NULL; } } @@ -3461,7 +3388,7 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { if (parser->interactive) { bfs_warning(parser->ctx, "Do you want to continue? "); if (ynprompt() == 0) { - goto fail; + return NULL; } } @@ -3472,14 +3399,10 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { parse_conflict_error(parser, parser->ok_expr->argv, parser->ok_expr->argc, parser->files0_stdin_arg, 2, "${blu}%s${rs} conflicts with ${blu}%s${rs} ${bld}%s${rs}.\n", parser->ok_expr->argv[0], parser->files0_stdin_arg[0], parser->files0_stdin_arg[1]); - goto fail; + return NULL; } return expr; - -fail: - bfs_expr_free(expr); - return NULL; } static const char *bftw_strategy_name(enum bftw_strategy strategy) { @@ -3637,7 +3560,7 @@ static void dump_costs(const struct bfs_ctx *ctx) { struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { struct bfs_ctx *ctx = bfs_ctx_new(); if (!ctx) { - perror("bfs_new_ctx()"); + perror("bfs_ctx_new()"); goto fail; } @@ -3715,7 +3638,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { .now = ctx->now, }; - ctx->exclude = bfs_expr_new(eval_false, 1, &fake_false_arg); + ctx->exclude = parse_new_expr(&parser, eval_false, 1, &fake_false_arg); if (!ctx->exclude) { goto fail; } @@ -3734,6 +3657,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { } if (bfs_optimize(ctx) != 0) { + bfs_perror(ctx, "bfs_optimize()"); goto fail; } -- cgit v1.2.3 From 4a36bb92a5bbdc41965a6d2c6eae6cdca5983474 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 7 Jan 2024 12:19:17 -0500 Subject: expr: Make expressions variadic Rather than only unary/binary expressions, we now support an arbitrary number of children. The optimizer has been re-written almost completely and now supports optimal reordering of longer expression chains, rather than just arm-swapping. Fixes #85. --- src/color.c | 39 +- src/ctx.c | 2 +- src/diag.c | 5 +- src/eval.c | 55 +- src/expr.c | 37 +- src/expr.h | 28 +- src/opt.c | 2468 +++++++++++++++++++++++++++++++++++++++-------------------- src/parse.c | 73 +- 8 files changed, 1768 insertions(+), 939 deletions(-) (limited to 'src/opt.c') diff --git a/src/color.c b/src/color.c index 5247cbf..1f10c04 100644 --- a/src/color.c +++ b/src/color.c @@ -1109,7 +1109,11 @@ attr(printf(2, 3)) static int cbuff(CFILE *cfile, const char *format, ...); /** Dump a parsed expression tree, for debugging. */ -static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { +static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose, int depth) { + if (depth >= 2) { + return dstrcat(&cfile->buffer, "(...)"); + } + if (!expr) { return dstrcat(&cfile->buffer, "(null)"); } @@ -1118,13 +1122,7 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { return -1; } - const struct bfs_expr *lhs = NULL; - const struct bfs_expr *rhs = NULL; - if (bfs_expr_is_parent(expr)) { - lhs = expr->lhs; - rhs = expr->rhs; - if (cbuff(cfile, "${red}%pq${rs}", expr->argv[0]) < 0) { return -1; } @@ -1152,21 +1150,20 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { } } - if (lhs) { - if (dstrcat(&cfile->buffer, " ") != 0) { - return -1; - } - if (print_expr(cfile, lhs, verbose) != 0) { - return -1; - } - } - - if (rhs) { + int count = 0; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { if (dstrcat(&cfile->buffer, " ") != 0) { return -1; } - if (print_expr(cfile, rhs, verbose) != 0) { - return -1; + if (++count >= 3) { + if (dstrcat(&cfile->buffer, "...") != 0) { + return -1; + } + break; + } else { + if (print_expr(cfile, child, verbose, depth + 1) != 0) { + return -1; + } } } @@ -1276,12 +1273,12 @@ static int cvbuff(CFILE *cfile, const char *format, va_list args) { break; case 'e': - if (print_expr(cfile, va_arg(args, const struct bfs_expr *), false) != 0) { + if (print_expr(cfile, va_arg(args, const struct bfs_expr *), false, 0) != 0) { return -1; } break; case 'E': - if (print_expr(cfile, va_arg(args, const struct bfs_expr *), true) != 0) { + if (print_expr(cfile, va_arg(args, const struct bfs_expr *), true, 0) != 0) { return -1; } break; diff --git a/src/ctx.c b/src/ctx.c index a3c5140..1f3e72e 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -241,7 +241,7 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { free_colors(ctx->colors); - for_slist (struct bfs_expr, expr, &ctx->expr_list) { + for_slist (struct bfs_expr, expr, &ctx->expr_list, freelist) { bfs_expr_clear(expr); } arena_destroy(&ctx->expr_arena); diff --git a/src/diag.c b/src/diag.c index bf2343d..efa7ebd 100644 --- a/src/diag.c +++ b/src/diag.c @@ -158,9 +158,8 @@ static bool highlight_expr_recursive(const struct bfs_ctx *ctx, const struct bfs } } - if (bfs_expr_is_parent(expr)) { - ret |= highlight_expr_recursive(ctx, expr->lhs, args); - ret |= highlight_expr_recursive(ctx, expr->rhs, args); + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + ret |= highlight_expr_recursive(ctx, child, args); } return ret; diff --git a/src/eval.c b/src/eval.c index 372fcf5..49c4aff 100644 --- a/src/eval.c +++ b/src/eval.c @@ -372,11 +372,10 @@ static int eval_exec_finish(const struct bfs_expr *expr, const struct bfs_ctx *c } ret = -1; } - } else if (bfs_expr_is_parent(expr)) { - if (expr->lhs && eval_exec_finish(expr->lhs, ctx) != 0) { - ret = -1; - } - if (expr->rhs && eval_exec_finish(expr->rhs, ctx) != 0) { + } + + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (eval_exec_finish(child, ctx) != 0) { ret = -1; } } @@ -1045,50 +1044,48 @@ static bool eval_expr(struct bfs_expr *expr, struct bfs_eval *state) { * Evaluate a negation. */ bool eval_not(const struct bfs_expr *expr, struct bfs_eval *state) { - return !eval_expr(expr->rhs, state); + return !eval_expr(bfs_expr_children(expr), state); } /** * Evaluate a conjunction. */ bool eval_and(const struct bfs_expr *expr, struct bfs_eval *state) { - if (!eval_expr(expr->lhs, state)) { - return false; - } - - if (state->quit) { - return false; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (!eval_expr(child, state) || state->quit) { + return false; + } } - return eval_expr(expr->rhs, state); + return true; } /** * Evaluate a disjunction. */ bool eval_or(const struct bfs_expr *expr, struct bfs_eval *state) { - if (eval_expr(expr->lhs, state)) { - return true; - } - - if (state->quit) { - return false; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (eval_expr(child, state) || state->quit) { + return true; + } } - return eval_expr(expr->rhs, state); + return false; } /** * Evaluate the comma operator. */ bool eval_comma(const struct bfs_expr *expr, struct bfs_eval *state) { - eval_expr(expr->lhs, state); - - if (state->quit) { - return false; + bool ret; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + ret = eval_expr(child, state); + if (state->quit) { + break; + } } - return eval_expr(expr->rhs, state); + return ret; } /** Update the status bar. */ @@ -1571,12 +1568,8 @@ static bool eval_must_buffer(const struct bfs_expr *expr) { return true; } - if (bfs_expr_is_parent(expr)) { - if (expr->lhs && eval_must_buffer(expr->lhs)) { - return true; - } - - if (expr->rhs && eval_must_buffer(expr->rhs)) { + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (eval_must_buffer(child)) { return true; } } diff --git a/src/expr.c b/src/expr.c index 2002fc7..3e0033f 100644 --- a/src/expr.c +++ b/src/expr.c @@ -23,7 +23,12 @@ struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval_fn, size_t expr->argc = argc; expr->argv = argv; expr->probability = 0.5; - SLIST_PREPEND(&ctx->expr_list, expr); + SLIST_PREPEND(&ctx->expr_list, expr, freelist); + + if (bfs_expr_is_parent(expr)) { + SLIST_INIT(&expr->children); + } + return expr; } @@ -34,6 +39,36 @@ bool bfs_expr_is_parent(const struct bfs_expr *expr) { || expr->eval_fn == eval_comma; } +struct bfs_expr *bfs_expr_children(const struct bfs_expr *expr) { + if (bfs_expr_is_parent(expr)) { + return expr->children.head; + } else { + return NULL; + } +} + +void bfs_expr_append(struct bfs_expr *expr, struct bfs_expr *child) { + bfs_assert(bfs_expr_is_parent(expr)); + + SLIST_APPEND(&expr->children, child); + + if (!child->pure) { + expr->pure = false; + } + + expr->persistent_fds += child->persistent_fds; + if (expr->ephemeral_fds < child->ephemeral_fds) { + expr->ephemeral_fds = child->ephemeral_fds; + } +} + +void bfs_expr_extend(struct bfs_expr *expr, struct bfs_exprs *children) { + while (!SLIST_EMPTY(children)) { + struct bfs_expr *child = SLIST_POP(children); + bfs_expr_append(expr, child); + } +} + bool bfs_expr_never_returns(const struct bfs_expr *expr) { // Expressions that never return are vacuously both always true and always false return expr->always_true && expr->always_false; diff --git a/src/expr.h b/src/expr.h index 290f1f8..4d607a4 100644 --- a/src/expr.h +++ b/src/expr.h @@ -86,8 +86,10 @@ struct bfs_exprs { * A command line expression. */ struct bfs_expr { - /** The next allocated expression. */ + /** This expression's next sibling, if any. */ struct bfs_expr *next; + /** The next allocated expression. */ + struct { struct bfs_expr *next; } freelist; /** The function that evaluates this expression. */ bfs_eval_fn *eval_fn; @@ -123,12 +125,7 @@ struct bfs_expr { /** Auxilliary data for the evaluation function. */ union { /** Child expressions. */ - struct { - /** The left hand side of the expression. */ - struct bfs_expr *lhs; - /** The right hand side of the expression. */ - struct bfs_expr *rhs; - }; + struct bfs_exprs children; /** Integer comparisons. */ struct { @@ -218,10 +215,25 @@ struct bfs_ctx; struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval, size_t argc, char **argv); /** - * @return Whether the expression has child expressions. + * @return Whether this type of expression has children. */ bool bfs_expr_is_parent(const struct bfs_expr *expr); +/** + * @return The first child of this expression, or NULL if it has none. + */ +struct bfs_expr *bfs_expr_children(const struct bfs_expr *expr); + +/** + * Add a child to an expression. + */ +void bfs_expr_append(struct bfs_expr *expr, struct bfs_expr *child); + +/** + * Add a list of children to an expression. + */ +void bfs_expr_extend(struct bfs_expr *expr, struct bfs_exprs *children); + /** * @return Whether expr is known to always quit. */ diff --git a/src/opt.c b/src/opt.c index 1dc985a..7203c61 100644 --- a/src/opt.c +++ b/src/opt.c @@ -21,11 +21,12 @@ * -bar is likely to return false. * * -O4/-Ofast: aggressive optimizations that may affect correctness in corner - * cases. The main effect is to use impure to determine if any side-effects are - * reachable at all, and skipping the traversal if not. + * cases. The main effect is to use opt->impure to determine if any side- + * effects are reachable at all, skipping the traversal if not. */ #include "opt.h" +#include "bit.h" #include "color.h" #include "config.h" #include "ctx.h" @@ -33,6 +34,7 @@ #include "eval.h" #include "exec.h" #include "expr.h" +#include "list.h" #include "pwcache.h" #include #include @@ -41,9 +43,9 @@ #include #include -static char *fake_and_arg = "-a"; -static char *fake_or_arg = "-o"; -static char *fake_not_arg = "!"; +static char *fake_and_arg = "-and"; +static char *fake_or_arg = "-or"; +static char *fake_not_arg = "-not"; /** * The data flow domain for predicates. @@ -69,6 +71,70 @@ static void pred_join(enum df_pred *dest, enum df_pred src) { *dest |= src; } +/** + * Types of predicates we track. + */ +enum pred_type { + /** -readable */ + READABLE_PRED, + /** -writable */ + WRITABLE_PRED, + /** -executable */ + EXECUTABLE_PRED, + /** -acl */ + ACL_PRED, + /** -capable */ + CAPABLE_PRED, + /** -empty */ + EMPTY_PRED, + /** -hidden */ + HIDDEN_PRED, + /** -nogroup */ + NOGROUP_PRED, + /** -nouser */ + NOUSER_PRED, + /** -sparse */ + SPARSE_PRED, + /** -xattr */ + XATTR_PRED, + /** The number of pred_types. */ + PRED_TYPES, +}; + +/** Get the name of a predicate type. */ +static const char *pred_type_name(enum pred_type type) { + switch (type) { + case READABLE_PRED: + return "-readable"; + case WRITABLE_PRED: + return "-writable"; + case EXECUTABLE_PRED: + return "-executable"; + case ACL_PRED: + return "-acl"; + case CAPABLE_PRED: + return "-capable"; + case EMPTY_PRED: + return "-empty"; + case HIDDEN_PRED: + return "-hidden"; + case NOGROUP_PRED: + return "-nogroup"; + case NOUSER_PRED: + return "-nouser"; + case SPARSE_PRED: + return "-sparse"; + case XATTR_PRED: + return "-xattr"; + + case PRED_TYPES: + break; + } + + bfs_bug("Unknown predicate %d", (int)type); + return "???"; +} + /** * A contrained integer range. */ @@ -97,6 +163,11 @@ static void range_init_top(struct df_range *range) { range->max = LLONG_MAX; } +/** Check for an infinite range. */ +static bool range_is_top(const struct df_range *range) { + return range->min == 0 && range->max == LLONG_MAX; +} + /** Compute the minimum of two values. */ static long long min_value(long long a, long long b) { if (a < b) { @@ -170,35 +241,29 @@ enum range_type { RANGE_TYPES, }; -/** - * Types of predicates we track. - */ -enum pred_type { - /** -readable */ - READABLE_PRED, - /** -writable */ - WRITABLE_PRED, - /** -executable */ - EXECUTABLE_PRED, - /** -acl */ - ACL_PRED, - /** -capable */ - CAPABLE_PRED, - /** -empty */ - EMPTY_PRED, - /** -hidden */ - HIDDEN_PRED, - /** -nogroup */ - NOGROUP_PRED, - /** -nouser */ - NOUSER_PRED, - /** -sparse */ - SPARSE_PRED, - /** -xattr */ - XATTR_PRED, - /** The number of pred_types. */ - PRED_TYPES, -}; +/** Get the name of a range type. */ +static const char *range_type_name(enum range_type type) { + switch (type) { + case DEPTH_RANGE: + return "-depth"; + case GID_RANGE: + return "-gid"; + case INUM_RANGE: + return "-inum"; + case LINKS_RANGE: + return "-links"; + case SIZE_RANGE: + return "-size"; + case UID_RANGE: + return "-uid"; + + case RANGE_TYPES: + break; + } + + bfs_bug("Unknown range %d", (int)type); + return "???"; +} /** * The data flow analysis domain. @@ -210,9 +275,9 @@ struct df_domain { /** The value ranges we track. */ struct df_range ranges[RANGE_TYPES]; - /** Bitmask of possible file types. */ + /** Bitmask of possible -types. */ unsigned int types; - /** Bitmask of possible link target types. */ + /** Bitmask of possible -xtypes. */ unsigned int xtypes; }; @@ -265,6 +330,31 @@ static void df_init_top(struct df_domain *value) { value->xtypes = ~0; } +/** Check for the top element. */ +static bool df_is_top(const struct df_domain *value) { + for (int i = 0; i < PRED_TYPES; ++i) { + if (value->preds[i] != PRED_TOP) { + return false; + } + } + + for (int i = 0; i < RANGE_TYPES; ++i) { + if (!range_is_top(&value->ranges[i])) { + return false; + } + } + + if (value->types != ~0U) { + return false; + } + + if (value->xtypes != ~0U) { + return false; + } + + return true; +} + /** Compute the union of two fact sets. */ static void df_join(struct df_domain *dest, const struct df_domain *src) { for (int i = 0; i < PRED_TYPES; ++i) { @@ -285,6 +375,15 @@ static void df_join(struct df_domain *dest, const struct df_domain *src) { struct bfs_opt { /** The context we're optimizing. */ struct bfs_ctx *ctx; + /** Optimization level (ctx->optlevel). */ + int level; + /** Recursion depth. */ + int depth; + + /** Whether to produce warnings. */ + bool warn; + /** Whether the result of this expression is ignored. */ + bool ignore_result; /** Data flow state before this expression is evaluated. */ struct df_domain before; @@ -296,18 +395,14 @@ struct bfs_opt { struct df_domain *impure; }; -/** Constrain the value of a predicate. */ -static void opt_constrain_pred(struct bfs_opt *opt, enum pred_type type, bool value) { - constrain_pred(&opt->after_true.preds[type], value); - constrain_pred(&opt->after_false.preds[type], !value); -} - /** Log an optimization. */ -attr(printf(3, 4)) -static bool opt_debug(const struct bfs_opt *opt, int level, const char *format, ...) { - bfs_assert(opt->ctx->optlevel >= level); +attr(printf(2, 3)) +static bool opt_debug(struct bfs_opt *opt, const char *format, ...) { + if (bfs_debug_prefix(opt->ctx, DEBUG_OPT)) { + for (int i = 0; i < opt->depth; ++i) { + cfprintf(opt->ctx->cerr, "│ "); + } - if (bfs_debug(opt->ctx, DEBUG_OPT, "${cyn}-O%d${rs}: ", level)) { va_list args; va_start(args, format); cvfprintf(opt->ctx->cerr, format, args); @@ -318,450 +413,577 @@ static bool opt_debug(const struct bfs_opt *opt, int level, const char *format, } } -/** Warn about an expression. */ -attr(printf(3, 4)) -static void opt_warning(const struct bfs_opt *opt, const struct bfs_expr *expr, const char *format, ...) { - if (bfs_expr_warning(opt->ctx, expr)) { +/** Log a recursive call. */ +attr(printf(2, 3)) +static bool opt_enter(struct bfs_opt *opt, const char *format, ...) { + int depth = opt->depth; + if (depth > 0) { + --opt->depth; + } + + bool debug = opt_debug(opt, "%s", depth > 0 ? "├─╮ " : ""); + if (debug) { va_list args; va_start(args, format); - bfs_vwarning(opt->ctx, format, args); + cvfprintf(opt->ctx->cerr, format, args); va_end(args); } -} -/** Create a constant expression. */ -static struct bfs_expr *opt_const(const struct bfs_opt *opt, bool value) { - static bfs_eval_fn *fns[] = {eval_false, eval_true}; - static char *fake_args[] = {"-false", "-true"}; - return bfs_expr_new(opt->ctx, fns[value], 1, &fake_args[value]); + opt->depth = depth + 1; + return debug; } -/** - * Negate an expression. - */ -static struct bfs_expr *negate_expr(const struct bfs_opt *opt, struct bfs_expr *rhs, char **argv) { - if (rhs->eval_fn == eval_not) { - return rhs->rhs; - } +/** Log a recursive return. */ +attr(printf(2, 3)) +static bool opt_leave(struct bfs_opt *opt, const char *format, ...) { + bool debug = false; + int depth = opt->depth; - struct bfs_expr *expr = bfs_expr_new(opt->ctx, eval_not, 1, argv); - if (!expr) { - return NULL; + if (format) { + if (depth > 1) { + opt->depth -= 2; + } + + debug = opt_debug(opt, "%s", depth > 1 ? "├─╯ " : ""); + if (debug) { + va_list args; + va_start(args, format); + cvfprintf(opt->ctx->cerr, format, args); + va_end(args); + } } - expr->lhs = NULL; - expr->rhs = rhs; - return expr; + opt->depth = depth - 1; + return debug; } -static struct bfs_expr *optimize_not_expr(const struct bfs_opt *opt, struct bfs_expr *expr); -static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_expr *expr); -static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_expr *expr); - -/** - * Apply De Morgan's laws. - */ -static struct bfs_expr *de_morgan(const struct bfs_opt *opt, struct bfs_expr *expr, char **argv) { - bool debug = opt_debug(opt, 1, "De Morgan's laws: %pe ", expr); - - struct bfs_expr *parent = negate_expr(opt, expr, argv); - if (!parent) { - return NULL; +/** Log a shallow visit. */ +attr(printf(2, 3)) +static bool opt_visit(struct bfs_opt *opt, const char *format, ...) { + int depth = opt->depth; + if (depth > 0) { + --opt->depth; } - bool has_parent = true; - if (parent->eval_fn != eval_not) { - expr = parent; - has_parent = false; + bool debug = opt_debug(opt, "%s", depth > 0 ? "├─◯ " : ""); + if (debug) { + va_list args; + va_start(args, format); + cvfprintf(opt->ctx->cerr, format, args); + va_end(args); } - bfs_assert(expr->eval_fn == eval_and || expr->eval_fn == eval_or); - if (expr->eval_fn == eval_and) { - expr->eval_fn = eval_or; - expr->argv = &fake_or_arg; - } else { - expr->eval_fn = eval_and; - expr->argv = &fake_and_arg; - } + opt->depth = depth; + return debug; +} - expr->lhs = negate_expr(opt, expr->lhs, argv); - expr->rhs = negate_expr(opt, expr->rhs, argv); - if (!expr->lhs || !expr->rhs) { - return NULL; +/** Log the deletion of an expression. */ +attr(printf(2, 3)) +static bool opt_delete(struct bfs_opt *opt, const char *format, ...) { + int depth = opt->depth; + + if (depth > 0) { + --opt->depth; } + bool debug = opt_debug(opt, "%s", depth > 0 ? "├─✘ " : ""); if (debug) { - cfprintf(opt->ctx->cerr, "<==> %pe\n", parent); + va_list args; + va_start(args, format); + cvfprintf(opt->ctx->cerr, format, args); + va_end(args); } - if (expr->lhs->eval_fn == eval_not) { - expr->lhs = optimize_not_expr(opt, expr->lhs); - } - if (expr->rhs->eval_fn == eval_not) { - expr->rhs = optimize_not_expr(opt, expr->rhs); - } - if (!expr->lhs || !expr->rhs) { - return NULL; - } + opt->depth = depth; + return debug; +} - if (expr->eval_fn == eval_and) { - expr = optimize_and_expr(opt, expr); - } else { - expr = optimize_or_expr(opt, expr); +typedef bool dump_fn(struct bfs_opt *opt, const char *format, ...); + +/** Print a df_pred. */ +static void pred_dump(dump_fn *dump, struct bfs_opt *opt, const struct df_domain *value, enum pred_type type) { + dump(opt, "${blu}%s${rs}: ", pred_type_name(type)); + + FILE *file = opt->ctx->cerr->file; + switch (value->preds[type]) { + case PRED_BOTTOM: + fprintf(file, "⊥\n"); + break; + case PRED_TOP: + fprintf(file, "⊤\n"); + break; + case PRED_TRUE: + fprintf(file, "true\n"); + break; + case PRED_FALSE: + fprintf(file, "false\n"); + break; } - if (has_parent) { - parent->rhs = expr; +} + +/** Print a df_range. */ +static void range_dump(dump_fn *dump, struct bfs_opt *opt, const struct df_domain *value, enum range_type type) { + dump(opt, "${blu}%s${rs}: ", range_type_name(type)); + + FILE *file = opt->ctx->cerr->file; + const struct df_range *range = &value->ranges[type]; + if (range_is_bottom(range)) { + fprintf(file, "⊥\n"); + } else if (range_is_top(range)) { + fprintf(file, "⊤\n"); + } else if (range->min == range->max) { + fprintf(file, "%lld\n", range->min); } else { - parent = expr; - } - if (!expr) { - return NULL; + if (range->min == LLONG_MIN) { + fprintf(file, "(-∞, "); + } else { + fprintf(file, "[%lld, ", range->min); + } + if (range->max == LLONG_MAX) { + fprintf(file, "∞)\n"); + } else { + fprintf(file, "%lld]\n", range->max); + } } +} - if (has_parent) { - parent = optimize_not_expr(opt, parent); +/** Print a set of types. */ +static void types_dump(dump_fn *dump, struct bfs_opt *opt, const char *name, unsigned int types) { + dump(opt, "${blu}%s${rs}: ", name); + + FILE *file = opt->ctx->cerr->file; + if (types == 0) { + fprintf(file, " ⊥\n"); + } else if (types == ~0U) { + fprintf(file, " ⊤\n"); + } else if (count_ones(types) < count_ones(~types)) { + fprintf(file, " 0x%X\n", types); + } else { + fprintf(file, "~0x%X\n", ~types); } - return parent; } -/** Optimize an expression recursively. */ -static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr); +/** Calculate the number of lines of df_dump() output. */ +static int df_dump_lines(const struct df_domain *value) { + int lines = 0; -/** - * Optimize a negation. - */ -static struct bfs_expr *optimize_not_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { - bfs_assert(expr->eval_fn == eval_not); - - struct bfs_expr *rhs = expr->rhs; - - int optlevel = opt->ctx->optlevel; - if (optlevel >= 1) { - if (rhs->eval_fn == eval_true || rhs->eval_fn == eval_false) { - struct bfs_expr *ret = opt_const(opt, rhs->eval_fn == eval_false); - opt_debug(opt, 1, "constant propagation: %pe <==> %pe\n", expr, ret); - return ret; - } else if (rhs->eval_fn == eval_not) { - opt_debug(opt, 1, "double negation: %pe <==> %pe\n", expr, rhs->rhs); - return rhs->rhs; - } else if (bfs_expr_never_returns(rhs)) { - opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, rhs); - return expr->rhs; - } else if ((rhs->eval_fn == eval_and || rhs->eval_fn == eval_or) - && (rhs->lhs->eval_fn == eval_not || rhs->rhs->eval_fn == eval_not)) { - return de_morgan(opt, expr, expr->argv); - } + for (int i = 0; i < PRED_TYPES; ++i) { + lines += value->preds[i] != PRED_TOP; } - expr->pure = rhs->pure; - expr->always_true = rhs->always_false; - expr->always_false = rhs->always_true; - expr->cost = rhs->cost; - expr->probability = 1.0 - rhs->probability; + for (int i = 0; i < RANGE_TYPES; ++i) { + lines += !range_is_top(&value->ranges[i]); + } - return expr; + lines += value->types != ~0U; + lines += value->xtypes != ~0U; + + return lines; } -/** Optimize a negation recursively. */ -static struct bfs_expr *optimize_not_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - struct bfs_opt rhs_state = *opt; - expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); - if (!expr->rhs) { - return NULL; +/** Get the right debugging function for a df_dump() line. */ +static dump_fn *df_dump_line(int lines, int *line) { + ++*line; + + if (lines == 1) { + return opt_visit; + } else if (*line == 1) { + return opt_enter; + } else if (*line == lines) { + return opt_leave; + } else { + return opt_debug; } +} - opt->after_true = rhs_state.after_false; - opt->after_false = rhs_state.after_true; - - return optimize_not_expr(opt, expr); -} - -/** Optimize a conjunction. */ -static struct bfs_expr *optimize_and_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { - bfs_assert(expr->eval_fn == eval_and); - - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; - - const struct bfs_ctx *ctx = opt->ctx; - int optlevel = ctx->optlevel; - if (optlevel >= 1) { - if (lhs->eval_fn == eval_true) { - opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, rhs); - return expr->rhs; - } else if (rhs->eval_fn == eval_true) { - opt_debug(opt, 1, "conjunction elimination: %pe <==> %pe\n", expr, lhs); - return expr->lhs; - } else if (lhs->always_false) { - opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); - opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return expr->lhs; - } else if (lhs->always_true && rhs->eval_fn == eval_false) { - bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); - struct bfs_expr *ret = expr->lhs; - ret = negate_expr(opt, ret, &fake_not_arg); - if (debug && ret) { - cfprintf(ctx->cerr, "%pe\n", ret); - } - return ret; - } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_false) { - opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); - opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return expr->rhs; - } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { - return de_morgan(opt, expr, expr->lhs->argv); - } +/** Print a data flow value. */ +static void df_dump(struct bfs_opt *opt, const char *str, const struct df_domain *value) { + if (df_is_bottom(value)) { + opt_debug(opt, "%s: ⊥\n", str); + return; + } else if (df_is_top(value)) { + opt_debug(opt, "%s: ⊤\n", str); + return; } - expr->pure = lhs->pure && rhs->pure; - expr->always_true = lhs->always_true && rhs->always_true; - expr->always_false = lhs->always_false || rhs->always_false; - expr->cost = lhs->cost + lhs->probability * rhs->cost; - expr->probability = lhs->probability * rhs->probability; + if (!opt_debug(opt, "%s:\n", str)) { + return; + } - return expr; -} + int lines = df_dump_lines(value); + int line = 0; -/** Optimize a conjunction recursively. */ -static struct bfs_expr *optimize_and_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - struct bfs_opt lhs_state = *opt; - expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); - if (!expr->lhs) { - return NULL; + for (int i = 0; i < PRED_TYPES; ++i) { + if (value->preds[i] != PRED_TOP) { + pred_dump(df_dump_line(lines, &line), opt, value, i); + } } - struct bfs_opt rhs_state = *opt; - rhs_state.before = lhs_state.after_true; - expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); - if (!expr->rhs) { - return NULL; + for (int i = 0; i < RANGE_TYPES; ++i) { + if (!range_is_top(&value->ranges[i])) { + range_dump(df_dump_line(lines, &line), opt, value, i); + } } - opt->after_true = rhs_state.after_true; - opt->after_false = lhs_state.after_false; - df_join(&opt->after_false, &rhs_state.after_false); - - return optimize_and_expr(opt, expr); -} - -/** Optimize a disjunction. */ -static struct bfs_expr *optimize_or_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { - bfs_assert(expr->eval_fn == eval_or); - - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; - - const struct bfs_ctx *ctx = opt->ctx; - int optlevel = ctx->optlevel; - if (optlevel >= 1) { - if (lhs->always_true) { - opt_debug(opt, 1, "short-circuit: %pe <==> %pe\n", expr, lhs); - opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return expr->lhs; - } else if (lhs->eval_fn == eval_false) { - opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, rhs); - return expr->rhs; - } else if (rhs->eval_fn == eval_false) { - opt_debug(opt, 1, "disjunctive syllogism: %pe <==> %pe\n", expr, lhs); - return expr->lhs; - } else if (lhs->always_false && rhs->eval_fn == eval_true) { - bool debug = opt_debug(opt, 1, "strength reduction: %pe <==> ", expr); - struct bfs_expr *ret = expr->lhs; - ret = negate_expr(opt, ret, &fake_not_arg); - if (debug && ret) { - cfprintf(ctx->cerr, "%pe\n", ret); - } - return ret; - } else if (optlevel >= 2 && lhs->pure && rhs->eval_fn == eval_true) { - opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); - opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return expr->rhs; - } else if (lhs->eval_fn == eval_not && rhs->eval_fn == eval_not) { - return de_morgan(opt, expr, expr->lhs->argv); - } + if (value->types != ~0U) { + types_dump(df_dump_line(lines, &line), opt, "-type", value->types); } - expr->pure = lhs->pure && rhs->pure; - expr->always_true = lhs->always_true || rhs->always_true; - expr->always_false = lhs->always_false && rhs->always_false; - expr->cost = lhs->cost + (1 - lhs->probability) * rhs->cost; - expr->probability = lhs->probability + rhs->probability - lhs->probability * rhs->probability; + if (value->xtypes != ~0U) { + types_dump(df_dump_line(lines, &line), opt, "-xtype", value->xtypes); + } +} - return expr; +/** Check if an expression is constant. */ +static bool is_const(const struct bfs_expr *expr) { + return expr->eval_fn == eval_true || expr->eval_fn == eval_false; } -/** Optimize a disjunction recursively. */ -static struct bfs_expr *optimize_or_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - struct bfs_opt lhs_state = *opt; - expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); - if (!expr->lhs) { - return NULL; +/** Warn about an expression. */ +attr(printf(3, 4)) +static void opt_warning(const struct bfs_opt *opt, const struct bfs_expr *expr, const char *format, ...) { + if (!opt->warn) { + return; } - struct bfs_opt rhs_state = *opt; - rhs_state.before = lhs_state.after_false; - expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); - if (!expr->rhs) { - return NULL; + if (bfs_expr_is_parent(expr) || is_const(expr)) { + return; } - opt->after_false = rhs_state.after_false; - opt->after_true = lhs_state.after_true; - df_join(&opt->after_true, &rhs_state.after_true); - - return optimize_or_expr(opt, expr); + if (bfs_expr_warning(opt->ctx, expr)) { + va_list args; + va_start(args, format); + bfs_vwarning(opt->ctx, format, args); + va_end(args); + } } -/** Optimize an expression in an ignored-result context. */ -static struct bfs_expr *ignore_result(const struct bfs_opt *opt, struct bfs_expr *expr) { - int optlevel = opt->ctx->optlevel; +/** Remove and return an expression's children. */ +static void foster_children(struct bfs_expr *expr, struct bfs_exprs *children) { + bfs_assert(bfs_expr_is_parent(expr)); - if (optlevel >= 1) { - while (true) { - if (expr->eval_fn == eval_not) { - opt_debug(opt, 1, "ignored result: %pe --> %pe\n", expr, expr->rhs); - opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); - expr = expr->rhs; - } else if (optlevel >= 2 - && (expr->eval_fn == eval_and || expr->eval_fn == eval_or || expr->eval_fn == eval_comma) - && expr->rhs->pure) { - opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, expr->lhs); - opt_warning(opt, expr->rhs, "The result of this expression is ignored.\n\n"); - expr = expr->lhs; - } else { - break; - } - } + SLIST_INIT(children); + SLIST_EXTEND(children, &expr->children); - if (optlevel >= 2 && expr->pure && expr->eval_fn != eval_false) { - struct bfs_expr *ret = opt_const(opt, false); - opt_debug(opt, 2, "ignored result: %pe --> %pe\n", expr, ret); - opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); - return ret; - } - } + expr->persistent_fds = 0; + expr->ephemeral_fds = 0; + expr->pure = true; +} - return expr; +/** Return an expression's only child. */ +static struct bfs_expr *only_child(struct bfs_expr *expr) { + bfs_assert(bfs_expr_is_parent(expr)); + struct bfs_expr *child = bfs_expr_children(expr); + bfs_assert(child && !child->next); + return child; } -/** Optimize a comma expression. */ -static struct bfs_expr *optimize_comma_expr(const struct bfs_opt *opt, struct bfs_expr *expr) { - bfs_assert(expr->eval_fn == eval_comma); +/** Foster an expression's only child. */ +static struct bfs_expr *foster_only_child(struct bfs_expr *expr) { + struct bfs_expr *child = only_child(expr); + struct bfs_exprs children; + foster_children(expr, &children); + return child; +} - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; - - int optlevel = opt->ctx->optlevel; - if (optlevel >= 1) { - lhs = expr->lhs = ignore_result(opt, lhs); - - if (bfs_expr_never_returns(lhs)) { - opt_debug(opt, 1, "reachability: %pe <==> %pe\n", expr, lhs); - opt_warning(opt, expr->rhs, "This expression is unreachable.\n\n"); - return expr->lhs; - } else if ((lhs->always_true && rhs->eval_fn == eval_true) - || (lhs->always_false && rhs->eval_fn == eval_false)) { - opt_debug(opt, 1, "redundancy elimination: %pe <==> %pe\n", expr, lhs); - return expr->lhs; - } else if (optlevel >= 2 && lhs->pure) { - opt_debug(opt, 2, "purity: %pe <==> %pe\n", expr, rhs); - opt_warning(opt, expr->lhs, "The result of this expression is ignored.\n\n"); - return expr->rhs; +/** An expression visitor. */ +struct visitor; + +/** An expression-visiting function. */ +typedef struct bfs_expr *visit_fn(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor); + +/** An entry in a visitor lookup table. */ +struct visitor_table { + /** The evaluation function to match on. */ + bfs_eval_fn *eval_fn; + /** The visitor function. */ + visit_fn *visit; +}; + +/** Look up a visitor in a table. */ +static visit_fn *look_up_visitor(const struct bfs_expr *expr, const struct visitor_table table[]) { + for (size_t i = 0; table[i].eval_fn; ++i) { + if (expr->eval_fn == table[i].eval_fn) { + return table[i].visit; } } - expr->pure = lhs->pure && rhs->pure; - expr->always_true = bfs_expr_never_returns(lhs) || rhs->always_true; - expr->always_false = bfs_expr_never_returns(lhs) || rhs->always_false; - expr->cost = lhs->cost + rhs->cost; - expr->probability = rhs->probability; - - return expr; + return NULL; } -/** Optimize a comma expression recursively. */ -static struct bfs_expr *optimize_comma_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - struct bfs_opt lhs_state = *opt; - expr->lhs = optimize_expr_recursive(&lhs_state, expr->lhs); - if (!expr->lhs) { - return NULL; - } +struct visitor { + /** The name of this visitor. */ + const char *name; + + /** A function to call before visiting children. */ + visit_fn *enter; + /** The default visitor. */ + visit_fn *visit; + /** A function to call after visiting children. */ + visit_fn *leave; + + /** A visitor lookup table. */ + struct visitor_table table[]; +}; - struct bfs_opt rhs_state = *opt; - rhs_state.before = lhs_state.after_true; - df_join(&rhs_state.before, &lhs_state.after_false); +/** Recursive visitor implementation. */ +static struct bfs_expr *visit_deep(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor); - expr->rhs = optimize_expr_recursive(&rhs_state, expr->rhs); - if (!expr->rhs) { +/** Visit a negation. */ +static struct bfs_expr *visit_not(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *rhs = foster_only_child(expr); + + struct bfs_opt nested = *opt; + rhs = visit_deep(&nested, rhs, visitor); + if (!rhs) { return NULL; } - return optimize_comma_expr(opt, expr); + opt->after_true = nested.after_false; + opt->after_false = nested.after_true; + + bfs_expr_append(expr, rhs); + return expr; } -/** Optimize an icmp-style ([+-]N) expression. */ -static void optimize_icmp(struct bfs_opt *opt, const struct bfs_expr *expr, enum range_type type) { - struct df_range *true_range = &opt->after_true.ranges[type]; - struct df_range *false_range = &opt->after_false.ranges[type]; - long long value = expr->num; +/** Visit a conjunction. */ +static struct bfs_expr *visit_and(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); - switch (expr->int_cmp) { - case BFS_INT_EQUAL: - constrain_min(true_range, value); - constrain_max(true_range, value); - range_remove(false_range, value); - break; + // Base case (-and) == (-true) + df_init_bottom(&opt->after_false); + struct bfs_opt nested = *opt; - case BFS_INT_LESS: - constrain_min(false_range, value); - constrain_max(true_range, value); - range_remove(true_range, value); - break; + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); - case BFS_INT_GREATER: - constrain_max(false_range, value); - constrain_min(true_range, value); - range_remove(true_range, value); - break; - } -} + if (SLIST_EMPTY(&children)) { + nested.ignore_result = opt->ignore_result; + } else { + nested.ignore_result = false; + } -/** Optimize -{execut,read,writ}able. */ -static struct bfs_expr *optimize_access(struct bfs_opt *opt, struct bfs_expr *expr) { - expr->probability = 1.0; + child = visit_deep(&nested, child, visitor); + if (!child) { + return NULL; + } - if (expr->num & R_OK) { - opt_constrain_pred(opt, READABLE_PRED, true); - expr->probability *= 0.99; - } + df_join(&opt->after_false, &nested.after_false); + nested.before = nested.after_true; - if (expr->num & W_OK) { - opt_constrain_pred(opt, WRITABLE_PRED, true); - expr->probability *= 0.8; + bfs_expr_append(expr, child); } - if (expr->num & X_OK) { - opt_constrain_pred(opt, EXECUTABLE_PRED, true); - expr->probability *= 0.2; - } + opt->after_true = nested.after_true; return expr; } -/** Optimize -empty. */ -static struct bfs_expr *optimize_empty(struct bfs_opt *opt, struct bfs_expr *expr) { - if (opt->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 - expr->pure = true; - } +/** Visit a disjunction. */ +static struct bfs_expr *visit_or(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); - return expr; -} + // Base case (-or) == (-false) + df_init_bottom(&opt->after_true); + struct bfs_opt nested = *opt; -/** Optimize -{exec,ok}{,dir}. */ -static struct bfs_expr *optimize_exec(struct bfs_opt *opt, struct bfs_expr *expr) { + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); + + if (SLIST_EMPTY(&children)) { + nested.ignore_result = opt->ignore_result; + } else { + nested.ignore_result = false; + } + + child = visit_deep(&nested, child, visitor); + if (!child) { + return NULL; + } + + df_join(&opt->after_true, &nested.after_true); + nested.before = nested.after_false; + + bfs_expr_append(expr, child); + } + + opt->after_false = nested.after_false; + + return expr; +} + +/** Visit a comma expression. */ +static struct bfs_expr *visit_comma(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_opt nested = *opt; + + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); + + if (SLIST_EMPTY(&children)) { + nested.ignore_result = opt->ignore_result; + } else { + nested.ignore_result = true; + } + + child = visit_deep(&nested, child, visitor); + if (!child) { + return NULL; + } + + nested.before = nested.after_true; + df_join(&nested.before, &nested.after_false); + + bfs_expr_append(expr, child); + } + + opt->after_true = nested.after_true; + opt->after_false = nested.after_false; + + return expr; +} + +/** Default enter() function. */ +static struct bfs_expr *visit_enter(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + opt_enter(opt, "%pe\n", expr); + opt->after_true = opt->before; + opt->after_false = opt->before; + return expr; +} + +/** Default leave() function. */ +static struct bfs_expr *visit_leave(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + opt_leave(opt, "%pe\n", expr); + return expr; +} + +static struct bfs_expr *visit_deep(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + bool entered = false; + + visit_fn *enter = visitor->enter ? visitor->enter : visit_enter; + visit_fn *leave = visitor->leave ? visitor->leave : visit_leave; + + static const struct visitor_table table[] = { + {eval_not, visit_not}, + {eval_and, visit_and}, + {eval_or, visit_or}, + {eval_comma, visit_comma}, + {NULL, NULL}, + }; + visit_fn *recursive = look_up_visitor(expr, table); + if (recursive) { + if (!entered) { + expr = enter(opt, expr, visitor); + if (!expr) { + return NULL; + } + entered = true; + } + + expr = recursive(opt, expr, visitor); + if (!expr) { + return NULL; + } + } + + visit_fn *general = visitor->visit; + if (general) { + if (!entered) { + expr = enter(opt, expr, visitor); + if (!expr) { + return NULL; + } + entered = true; + } + + expr = general(opt, expr, visitor); + if (!expr) { + return NULL; + } + } + + visit_fn *specific = look_up_visitor(expr, visitor->table); + if (specific) { + if (!entered) { + expr = enter(opt, expr, visitor); + if (!expr) { + return NULL; + } + entered = true; + } + + expr = specific(opt, expr, visitor); + if (!expr) { + return NULL; + } + } + + if (entered) { + expr = leave(opt, expr, visitor); + } else { + opt_visit(opt, "%pe\n", expr); + } + + return expr; +} + +/** Visit an expression recursively. */ +static struct bfs_expr *visit(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + opt_enter(opt, "%s()\n", visitor->name); + expr = visit_deep(opt, expr, visitor); + opt_leave(opt, "\n"); + return expr; +} + +/** Visit an expression non-recursively. */ +static struct bfs_expr *visit_shallow(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + visit_fn *general = visitor->visit; + if (expr && general) { + expr = general(opt, expr, visitor); + } + + visit_fn *specific = look_up_visitor(expr, visitor->table); + if (expr && specific) { + expr = specific(opt, expr, visitor); + } + + return expr; +} + +/** Annotate -{execut,read,writ}able. */ +static struct bfs_expr *annotate_access(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + expr->probability = 1.0; + if (expr->num & R_OK) { + expr->probability *= 0.99; + } + if (expr->num & W_OK) { + expr->probability *= 0.8; + } + if (expr->num & X_OK) { + expr->probability *= 0.2; + } + + return expr; +} + +/** Annotate -empty. */ +static struct bfs_expr *annotate_empty(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (opt->level >= 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 + expr->pure = true; + } + + return expr; +} + +/** Annotate -exec. */ +static struct bfs_expr *annotate_exec(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { if (expr->exec->flags & BFS_EXEC_MULTI) { expr->always_true = true; } else { @@ -771,33 +993,650 @@ static struct bfs_expr *optimize_exec(struct bfs_opt *opt, struct bfs_expr *expr return expr; } -/** Optimize -name/-lname/-path. */ -static struct bfs_expr *optimize_fnmatch(struct bfs_opt *opt, struct bfs_expr *expr) { - if (strchr(expr->argv[1], '*')) { +/** Annotate -name/-lname/-path. */ +static struct bfs_expr *annotate_fnmatch(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (expr->literal) { + expr->probability = 0.1; + } else { expr->probability = 0.5; + } + + return expr; +} + +/** Estimate probability for -x?type. */ +static void estimate_type_probability(struct bfs_expr *expr) { + unsigned int types = expr->num; + + expr->probability = 0.0; + if (types & (1 << BFS_BLK)) { + expr->probability += 0.00000721183; + } + if (types & (1 << BFS_CHR)) { + expr->probability += 0.0000499855; + } + if (types & (1 << BFS_DIR)) { + expr->probability += 0.114475; + } + if (types & (1 << BFS_DOOR)) { + expr->probability += 0.000001; + } + if (types & (1 << BFS_FIFO)) { + expr->probability += 0.00000248684; + } + if (types & (1 << BFS_REG)) { + expr->probability += 0.859772; + } + if (types & (1 << BFS_LNK)) { + expr->probability += 0.0256816; + } + if (types & (1 << BFS_SOCK)) { + expr->probability += 0.0000116881; + } + if (types & (1 << BFS_WHT)) { + expr->probability += 0.000001; + } +} + +/** Annotate -type. */ +static struct bfs_expr *annotate_type(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + estimate_type_probability(expr); + return expr; +} + +/** Annotate -xtype. */ +static struct bfs_expr *annotate_xtype(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (opt->level >= 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 + expr->pure = true; + } + + estimate_type_probability(expr); + return expr; +} + +/** Annotate a negation. */ +static struct bfs_expr *annotate_not(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *rhs = only_child(expr); + expr->pure = rhs->pure; + expr->always_true = rhs->always_false; + expr->always_false = rhs->always_true; + expr->cost = rhs->cost; + expr->probability = 1.0 - rhs->probability; + return expr; +} + +/** Annotate a conjunction. */ +static struct bfs_expr *annotate_and(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + expr->pure = true; + expr->always_true = true; + expr->always_false = false; + expr->cost = 0.0; + expr->probability = 1.0; + + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + expr->pure &= child->pure; + expr->always_true &= child->always_true; + expr->always_false |= child->always_false; + expr->cost += expr->probability * child->cost; + expr->probability *= child->probability; + } + + return expr; +} + +/** Annotate a disjunction. */ +static struct bfs_expr *annotate_or(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + expr->pure = true; + expr->always_true = false; + expr->always_false = true; + expr->cost = 0.0; + + float false_prob = 1.0; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + expr->pure &= child->pure; + expr->always_true |= child->always_true; + expr->always_false &= child->always_false; + expr->cost += false_prob * child->cost; + false_prob *= (1.0 - child->probability); + } + expr->probability = 1.0 - false_prob; + + return expr; +} + +/** Annotate a comma expression. */ +static struct bfs_expr *annotate_comma(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + expr->pure = true; + expr->cost = 0.0; + + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + expr->pure &= child->pure; + expr->always_true = child->always_true; + expr->always_false = child->always_false; + expr->cost += child->cost; + expr->probability = child->probability; + } + + return expr; +} + +/** Annotate an arbitrary expression. */ +static struct bfs_expr *annotate_visit(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + /** Table of pure expressions. */ + static bfs_eval_fn *const pure[] = { + eval_access, + eval_acl, + eval_capable, + eval_depth, + eval_false, + eval_flags, + eval_fstype, + eval_gid, + eval_hidden, + eval_inum, + eval_links, + eval_lname, + eval_name, + eval_newer, + eval_nogroup, + eval_nouser, + eval_path, + eval_perm, + eval_regex, + eval_samefile, + eval_size, + eval_sparse, + eval_time, + eval_true, + eval_type, + eval_uid, + eval_used, + eval_xattr, + eval_xattrname, + }; + + expr->pure = false; + for (size_t i = 0; i < countof(pure); ++i) { + if (expr->eval_fn == pure[i]) { + expr->pure = true; + break; + } + } + + /** Table of always-true expressions. */ + static bfs_eval_fn *const always_true[] = { + eval_fls, + eval_fprint, + eval_fprint0, + eval_fprintf, + eval_fprintx, + eval_prune, + eval_true, + // Non-returning + eval_exit, + eval_quit, + }; + + expr->always_true = false; + for (size_t i = 0; i < countof(always_true); ++i) { + if (expr->eval_fn == always_true[i]) { + expr->always_true = true; + break; + } + } + + /** Table of always-false expressions. */ + static bfs_eval_fn *const always_false[] = { + eval_false, + // Non-returning + eval_exit, + eval_quit, + }; + + expr->always_false = false; + for (size_t i = 0; i < countof(always_false); ++i) { + if (expr->eval_fn == always_false[i]) { + expr->always_false = true; + break; + } + } + +#define FAST_COST 40.0 +#define FNMATCH_COST 400.0 +#define STAT_COST 1000.0 +#define PRINT_COST 20000.0 + + /** Table of expression costs. */ + static const struct { + bfs_eval_fn *eval_fn; + float cost; + } costs[] = { + {eval_access, STAT_COST}, + {eval_acl, STAT_COST}, + {eval_capable, STAT_COST}, + {eval_empty, 2 * STAT_COST}, // readdir() is worse than stat() + {eval_fls, PRINT_COST}, + {eval_fprint, PRINT_COST}, + {eval_fprint0, PRINT_COST}, + {eval_fprintf, PRINT_COST}, + {eval_fprintx, PRINT_COST}, + {eval_fstype, STAT_COST}, + {eval_gid, STAT_COST}, + {eval_inum, STAT_COST}, + {eval_links, STAT_COST}, + {eval_lname, FNMATCH_COST}, + {eval_name, FNMATCH_COST}, + {eval_newer, STAT_COST}, + {eval_nogroup, STAT_COST}, + {eval_nouser, STAT_COST}, + {eval_path, FNMATCH_COST}, + {eval_perm, STAT_COST}, + {eval_samefile, STAT_COST}, + {eval_size, STAT_COST}, + {eval_sparse, STAT_COST}, + {eval_time, STAT_COST}, + {eval_uid, STAT_COST}, + {eval_used, STAT_COST}, + {eval_xattr, STAT_COST}, + {eval_xattrname, STAT_COST}, + }; + + expr->cost = FAST_COST; + for (size_t i = 0; i < countof(costs); ++i) { + if (expr->eval_fn == costs[i].eval_fn) { + expr->cost = costs[i].cost; + break; + } + } + + /** Table of expression probabilities. */ + static const struct { + /** The evaluation function with this cost. */ + bfs_eval_fn *eval_fn; + /** The matching probability. */ + float probability; + } probs[] = { + {eval_acl, 0.00002}, + {eval_capable, 0.000002}, + {eval_empty, 0.01}, + {eval_false, 0.0}, + {eval_hidden, 0.01}, + {eval_nogroup, 0.01}, + {eval_nouser, 0.01}, + {eval_samefile, 0.01}, + {eval_true, 1.0}, + {eval_xattr, 0.01}, + {eval_xattrname, 0.01}, + }; + + expr->probability = 0.5; + for (size_t i = 0; i < countof(probs); ++i) { + if (expr->eval_fn == probs[i].eval_fn) { + expr->probability = probs[i].probability; + break; + } + } + + return expr; +} + +/** + * Annotating visitor. + */ +static const struct visitor annotate = { + .name = "annotate", + .visit = annotate_visit, + .table = { + {eval_access, annotate_access}, + {eval_empty, annotate_empty}, + {eval_exec, annotate_exec}, + {eval_lname, annotate_fnmatch}, + {eval_name, annotate_fnmatch}, + {eval_path, annotate_fnmatch}, + {eval_type, annotate_type}, + {eval_xtype, annotate_xtype}, + + {eval_not, annotate_not}, + {eval_and, annotate_and}, + {eval_or, annotate_or}, + {eval_comma, annotate_comma}, + + {NULL, NULL}, + }, +}; + +/** Create a constant expression. */ +static struct bfs_expr *opt_const(struct bfs_opt *opt, bool value) { + static bfs_eval_fn *const fns[] = {eval_false, eval_true}; + static char *fake_args[] = {"-false", "-true"}; + + struct bfs_expr *expr = bfs_expr_new(opt->ctx, fns[value], 1, &fake_args[value]); + return visit_shallow(opt, expr, &annotate); +} + +/** Negate an expression, keeping it canonical. */ +static struct bfs_expr *negate_expr(struct bfs_opt *opt, struct bfs_expr *expr, char **argv) { + if (expr->eval_fn == eval_not) { + return only_child(expr); + } else if (expr->eval_fn == eval_true) { + return opt_const(opt, false); + } else if (expr->eval_fn == eval_false) { + return opt_const(opt, true); + } + + struct bfs_expr *ret = bfs_expr_new(opt->ctx, eval_not, 1, argv); + if (!ret) { + return NULL; + } + + bfs_expr_append(ret, expr); + return visit_shallow(opt, ret, &annotate); +} + +/** Sink negations into a conjunction/disjunction using De Morgan's laws. */ +static struct bfs_expr *sink_not_andor(struct bfs_opt *opt, struct bfs_expr *expr) { + opt_debug(opt, "De Morgan's laws\n"); + + char **argv = expr->argv; + expr = only_child(expr); + opt_enter(opt, "%pe\n", expr); + + if (expr->eval_fn == eval_and) { + expr->eval_fn = eval_or; + expr->argv = &fake_or_arg; } else { - expr->probability = 0.1; + bfs_assert(expr->eval_fn == eval_or); + expr->eval_fn = eval_and; + expr->argv = &fake_and_arg; + } + + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + opt_enter(opt, "%pe\n", child); + + child = negate_expr(opt, child, argv); + if (!child) { + return NULL; + } + + opt_leave(opt, "%pe\n", child); + bfs_expr_append(expr, child); + } + + opt_leave(opt, "%pe\n", expr); + return visit_shallow(opt, expr, &annotate); +} + +/** Sink a negation into a comma expression. */ +static struct bfs_expr *sink_not_comma(struct bfs_opt *opt, struct bfs_expr *expr) { + bfs_assert(expr->eval_fn == eval_comma); + + opt_enter(opt, "%pe\n", expr); + + char **argv = expr->argv; + expr = only_child(expr); + + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + if (SLIST_EMPTY(&children)) { + opt_enter(opt, "%pe\n", child); + opt_debug(opt, "sink\n"); + + child = negate_expr(opt, child, argv); + if (!child) { + return NULL; + } + + opt_leave(opt, "%pe\n", child); + } else { + opt_visit(opt, "%pe\n", child); + } + + bfs_expr_append(expr, child); + } + + opt_leave(opt, "%pe\n", expr); + return visit_shallow(opt, expr, &annotate); +} + +/** Canonicalize a negation. */ +static struct bfs_expr *canonicalize_not(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *rhs = only_child(expr); + + if (rhs->eval_fn == eval_not) { + opt_debug(opt, "double negation\n"); + rhs = only_child(expr); + return only_child(rhs); + } else if (rhs->eval_fn == eval_and || rhs->eval_fn == eval_or) { + return sink_not_andor(opt, expr); + } else if (rhs->eval_fn == eval_comma) { + return sink_not_comma(opt, expr); + } else if (is_const(rhs)) { + opt_debug(opt, "constant propagation\n"); + return opt_const(opt, rhs->eval_fn == eval_false); + } else { + return expr; + } +} + +/** Canonicalize an associative operator. */ +static struct bfs_expr *canonicalize_assoc(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_exprs flat; + SLIST_INIT(&flat); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + if (child->eval_fn == expr->eval_fn) { + struct bfs_expr *head = SLIST_HEAD(&child->children); + struct bfs_expr *tail = SLIST_TAIL(&child->children); + + if (!head) { + opt_delete(opt, "%pe [empty]\n", child); + } else { + opt_enter(opt, "%pe\n", child); + opt_debug(opt, "associativity\n"); + if (head == tail) { + opt_leave(opt, "%pe\n", head); + } else if (head->next == tail) { + opt_leave(opt, "%pe %pe\n", head, tail); + } else { + opt_leave(opt, "%pe ... %pe\n", head, tail); + } + } + + SLIST_EXTEND(&flat, &child->children); + } else { + opt_visit(opt, "%pe\n", child); + SLIST_APPEND(&flat, child); + } + } + + bfs_expr_extend(expr, &flat); + + return visit_shallow(opt, expr, &annotate); +} + +/** + * Canonicalizing visitor. + */ +static const struct visitor canonicalize = { + .name = "canonicalize", + .table = { + {eval_not, canonicalize_not}, + {eval_and, canonicalize_assoc}, + {eval_or, canonicalize_assoc}, + {eval_comma, canonicalize_assoc}, + {NULL, NULL}, + }, +}; + +/** Calculate the cost of an ordered pair of expressions. */ +static float expr_cost(const struct bfs_expr *parent, const struct bfs_expr *lhs, const struct bfs_expr *rhs) { + // https://cs.stackexchange.com/a/66921/21004 + float prob = lhs->probability; + if (parent->eval_fn == eval_or) { + prob = 1.0 - prob; + } + return lhs->cost + prob * rhs->cost; +} + +/** Sort a block of expressions. */ +static void sort_exprs(struct bfs_opt *opt, struct bfs_expr *parent, struct bfs_exprs *exprs) { + if (!exprs->head || !exprs->head->next) { + return; + } + + struct bfs_exprs left, right; + SLIST_INIT(&left); + SLIST_INIT(&right); + + // Split + for (struct bfs_expr *hare = exprs->head; hare && (hare = hare->next); hare = hare->next) { + struct bfs_expr *tortoise = SLIST_POP(exprs); + SLIST_APPEND(&left, tortoise); + } + SLIST_EXTEND(&right, exprs); + + // Recurse + sort_exprs(opt, parent, &left); + sort_exprs(opt, parent, &right); + + // Merge + while (!SLIST_EMPTY(&left) && !SLIST_EMPTY(&right)) { + struct bfs_expr *lhs = left.head; + struct bfs_expr *rhs = right.head; + + float cost = expr_cost(parent, lhs, rhs); + float swapped = expr_cost(parent, rhs, lhs); + + if (cost <= swapped) { + SLIST_POP(&left); + SLIST_APPEND(exprs, lhs); + } else { + opt_enter(opt, "%pe %pe [${ylw}%g${rs}]\n", lhs, rhs, cost); + SLIST_POP(&right); + SLIST_APPEND(exprs, rhs); + opt_leave(opt, "%pe %pe [${ylw}%g${rs}]\n", rhs, lhs, swapped); + } + } + SLIST_EXTEND(exprs, &left); + SLIST_EXTEND(exprs, &right); +} + +/** Reorder children to reduce cost. */ +static struct bfs_expr *reorder_andor(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); + + // Split into blocks of consecutive pure/impure expressions, and sort + // the pure blocks + struct bfs_exprs pure; + SLIST_INIT(&pure); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + if (child->pure) { + SLIST_APPEND(&pure, child); + } else { + sort_exprs(opt, expr, &pure); + bfs_expr_extend(expr, &pure); + bfs_expr_append(expr, child); + } + } + sort_exprs(opt, expr, &pure); + bfs_expr_extend(expr, &pure); + + return visit_shallow(opt, expr, &annotate); +} + +/** + * Reordering visitor. + */ +static const struct visitor reorder = { + .name = "reorder", + .table = { + {eval_and, reorder_andor}, + {eval_or, reorder_andor}, + {NULL, NULL}, + }, +}; + +/** Transfer function for simple predicates. */ +static void data_flow_pred(struct bfs_opt *opt, enum pred_type pred, bool value) { + constrain_pred(&opt->after_true.preds[pred], value); + constrain_pred(&opt->after_false.preds[pred], !value); +} + +/** Transfer function for icmp-style ([+-]N) expressions. */ +static void data_flow_icmp(struct bfs_opt *opt, const struct bfs_expr *expr, enum range_type type) { + struct df_range *true_range = &opt->after_true.ranges[type]; + struct df_range *false_range = &opt->after_false.ranges[type]; + long long value = expr->num; + + switch (expr->int_cmp) { + case BFS_INT_EQUAL: + constrain_min(true_range, value); + constrain_max(true_range, value); + range_remove(false_range, value); + break; + + case BFS_INT_LESS: + constrain_min(false_range, value); + constrain_max(true_range, value); + range_remove(true_range, value); + break; + + case BFS_INT_GREATER: + constrain_max(false_range, value); + constrain_min(true_range, value); + range_remove(true_range, value); + break; + } +} + +/** Transfer function for -{execut,read,writ}able. */ +static struct bfs_expr *data_flow_access(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (expr->num & R_OK) { + data_flow_pred(opt, READABLE_PRED, true); + } + if (expr->num & W_OK) { + data_flow_pred(opt, WRITABLE_PRED, true); + } + if (expr->num & X_OK) { + data_flow_pred(opt, EXECUTABLE_PRED, true); } return expr; } -/** Optimize -gid. */ -static struct bfs_expr *optimize_gid(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -gid. */ +static struct bfs_expr *data_flow_gid(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { struct df_range *range = &opt->after_true.ranges[GID_RANGE]; if (range->min == range->max) { gid_t gid = range->min; bool nogroup = !bfs_getgrgid(opt->ctx->groups, gid); if (errno == 0) { - opt_constrain_pred(opt, NOGROUP_PRED, nogroup); + data_flow_pred(opt, NOGROUP_PRED, nogroup); } } return expr; } -/** Optimize -inum. */ -static struct bfs_expr *optimize_inum(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -inum. */ +static struct bfs_expr *data_flow_inum(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { struct df_range *range = &opt->after_true.ranges[INUM_RANGE]; if (range->min == range->max) { expr->probability = 0.01; @@ -808,8 +1647,8 @@ static struct bfs_expr *optimize_inum(struct bfs_opt *opt, struct bfs_expr *expr return expr; } -/** Optimize -links. */ -static struct bfs_expr *optimize_links(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -links. */ +static struct bfs_expr *data_flow_links(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { struct df_range *range = &opt->after_true.ranges[LINKS_RANGE]; if (1 >= range->min && 1 <= range->max) { expr->probability = 0.99; @@ -820,30 +1659,20 @@ static struct bfs_expr *optimize_links(struct bfs_opt *opt, struct bfs_expr *exp return expr; } -/** Optimize -uid. */ -static struct bfs_expr *optimize_uid(struct bfs_opt *opt, struct bfs_expr *expr) { - struct df_range *range = &opt->after_true.ranges[UID_RANGE]; - if (range->min == range->max) { - uid_t uid = range->min; - bool nouser = !bfs_getpwuid(opt->ctx->users, uid); - if (errno == 0) { - opt_constrain_pred(opt, NOUSER_PRED, nouser); - } - } +/** Transfer function for -samefile. */ +static struct bfs_expr *data_flow_samefile(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct df_range *true_range = &opt->after_true.ranges[INUM_RANGE]; + constrain_min(true_range, expr->ino); + constrain_max(true_range, expr->ino); - return expr; -} + struct df_range *false_range = &opt->after_false.ranges[INUM_RANGE]; + range_remove(false_range, expr->ino); -/** Optimize -samefile. */ -static struct bfs_expr *optimize_samefile(struct bfs_opt *opt, struct bfs_expr *expr) { - struct df_range *range = &opt->after_true.ranges[INUM_RANGE]; - constrain_min(range, expr->ino); - constrain_max(range, expr->ino); return expr; } -/** Optimize -size. */ -static struct bfs_expr *optimize_size(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -size. */ +static struct bfs_expr *data_flow_size(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { struct df_range *range = &opt->after_true.ranges[SIZE_RANGE]; if (range->min == range->max) { expr->probability = 0.01; @@ -854,473 +1683,449 @@ static struct bfs_expr *optimize_size(struct bfs_opt *opt, struct bfs_expr *expr return expr; } -/** Estimate probability for -x?type. */ -static void estimate_type_probability(struct bfs_expr *expr) { - unsigned int types = expr->num; - - expr->probability = 0.0; - if (types & (1 << BFS_BLK)) { - expr->probability += 0.00000721183; - } - if (types & (1 << BFS_CHR)) { - expr->probability += 0.0000499855; - } - if (types & (1 << BFS_DIR)) { - expr->probability += 0.114475; - } - if (types & (1 << BFS_DOOR)) { - expr->probability += 0.000001; - } - if (types & (1 << BFS_FIFO)) { - expr->probability += 0.00000248684; - } - if (types & (1 << BFS_REG)) { - expr->probability += 0.859772; - } - if (types & (1 << BFS_LNK)) { - expr->probability += 0.0256816; - } - if (types & (1 << BFS_SOCK)) { - expr->probability += 0.0000116881; - } - if (types & (1 << BFS_WHT)) { - expr->probability += 0.000001; - } -} - -/** Optimize -type. */ -static struct bfs_expr *optimize_type(struct bfs_opt *opt, struct bfs_expr *expr) { +/** Transfer function for -type. */ +static struct bfs_expr *data_flow_type(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { opt->after_true.types &= expr->num; opt->after_false.types &= ~expr->num; - - estimate_type_probability(expr); - return expr; } -/** Optimize -xtype. */ -static struct bfs_expr *optimize_xtype(struct bfs_opt *opt, struct bfs_expr *expr) { - if (opt->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 - expr->pure = true; +/** Transfer function for -uid. */ +static struct bfs_expr *data_flow_uid(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct df_range *range = &opt->after_true.ranges[UID_RANGE]; + if (range->min == range->max) { + uid_t uid = range->min; + bool nouser = !bfs_getpwuid(opt->ctx->users, uid); + if (errno == 0) { + data_flow_pred(opt, NOUSER_PRED, nouser); + } } + return expr; +} + +/** Transfer function for -xtype. */ +static struct bfs_expr *data_flow_xtype(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { opt->after_true.xtypes &= expr->num; opt->after_false.xtypes &= ~expr->num; - - estimate_type_probability(expr); - return expr; } -/** - * Table of pure expressions. - */ -static bfs_eval_fn *const opt_pure[] = { - eval_access, - eval_acl, - eval_capable, - eval_depth, - eval_false, - eval_flags, - eval_fstype, - eval_gid, - eval_hidden, - eval_inum, - eval_links, - eval_lname, - eval_name, - eval_newer, - eval_nogroup, - eval_nouser, - eval_path, - eval_perm, - eval_regex, - eval_samefile, - eval_size, - eval_sparse, - eval_time, - eval_true, - eval_type, - eval_uid, - eval_used, - eval_xattr, - eval_xattrname, -}; - -/** - * Table of always-true expressions. - */ -static bfs_eval_fn *const opt_always_true[] = { - eval_fls, - eval_fprint, - eval_fprint0, - eval_fprintf, - eval_fprintx, - eval_prune, - eval_true, - - // Non-returning - eval_exit, - eval_quit, -}; - -/** - * Table of always-false expressions. - */ -static bfs_eval_fn *const opt_always_false[] = { - eval_false, - - // Non-returning - eval_exit, - eval_quit, -}; - -#define FAST_COST 40.0 -#define FNMATCH_COST 400.0 -#define STAT_COST 1000.0 -#define PRINT_COST 20000.0 - -/** - * Table of expression costs. - */ -static const struct { - /** The evaluation function with this cost. */ - bfs_eval_fn *eval_fn; - /** The matching cost. */ - float cost; -} opt_costs[] = { - {eval_access, STAT_COST}, - {eval_acl, STAT_COST}, - {eval_capable, STAT_COST}, - {eval_empty, 2 * STAT_COST}, // readdir() is worse than stat() - {eval_fls, PRINT_COST}, - {eval_fprint, PRINT_COST}, - {eval_fprint0, PRINT_COST}, - {eval_fprintf, PRINT_COST}, - {eval_fprintx, PRINT_COST}, - {eval_fstype, STAT_COST}, - {eval_gid, STAT_COST}, - {eval_inum, STAT_COST}, - {eval_links, STAT_COST}, - {eval_lname, FNMATCH_COST}, - {eval_name, FNMATCH_COST}, - {eval_newer, STAT_COST}, - {eval_nogroup, STAT_COST}, - {eval_nouser, STAT_COST}, - {eval_path, FNMATCH_COST}, - {eval_perm, STAT_COST}, - {eval_samefile, STAT_COST}, - {eval_size, STAT_COST}, - {eval_sparse, STAT_COST}, - {eval_time, STAT_COST}, - {eval_uid, STAT_COST}, - {eval_used, STAT_COST}, - {eval_xattr, STAT_COST}, - {eval_xattrname, STAT_COST}, -}; - -/** - * Table of expression probabilities. - */ -static const struct { - /** The evaluation function with this cost. */ - bfs_eval_fn *eval_fn; - /** The matching probability. */ - float probability; -} opt_probs[] = { - {eval_acl, 0.00002}, - {eval_capable, 0.000002}, - {eval_empty, 0.01}, - {eval_false, 0.0}, - {eval_hidden, 0.01}, - {eval_nogroup, 0.01}, - {eval_nouser, 0.01}, - {eval_samefile, 0.01}, - {eval_true, 1.0}, - {eval_xattr, 0.01}, - {eval_xattrname, 0.01}, -}; +/** Data flow visitor entry. */ +static struct bfs_expr *data_flow_enter(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + visit_enter(opt, expr, visitor); -/** - * Table of simple predicates. - */ -static const struct { - /** The evaluation function this optimizer applies to. */ - bfs_eval_fn *eval_fn; - /** The corresponding predicate. */ - enum pred_type pred; -} opt_preds[] = { - {eval_acl, ACL_PRED}, - {eval_capable, CAPABLE_PRED}, - {eval_empty, EMPTY_PRED}, - {eval_hidden, HIDDEN_PRED}, - {eval_nogroup, NOGROUP_PRED}, - {eval_nouser, NOUSER_PRED}, - {eval_sparse, SPARSE_PRED}, - {eval_xattr, XATTR_PRED}, -}; + df_dump(opt, "before", &opt->before); -/** - * Table of simple range comparisons. - */ -static const struct { - /** The evaluation function this optimizer applies to. */ - bfs_eval_fn *eval_fn; - /** The corresponding range. */ - enum range_type range; -} opt_ranges[] = { - {eval_depth, DEPTH_RANGE}, - {eval_gid, GID_RANGE}, - {eval_inum, INUM_RANGE}, - {eval_links, LINKS_RANGE}, - {eval_size, SIZE_RANGE}, - {eval_uid, UID_RANGE}, -}; + if (!bfs_expr_is_parent(expr) && !expr->pure) { + df_join(opt->impure, &opt->before); + df_dump(opt, "impure", opt->impure); + } -/** Signature for custom optimizer functions. */ -typedef struct bfs_expr *bfs_opt_fn(struct bfs_opt *opt, struct bfs_expr *expr); + return expr; +} -/** Table of custom optimizer functions. */ -static const struct { - /** The evaluation function this optimizer applies to. */ - bfs_eval_fn *eval_fn; - /** The corresponding optimizer function. */ - bfs_opt_fn *opt_fn; -} opt_fns[] = { - // Primaries - {eval_access, optimize_access}, - {eval_empty, optimize_empty}, - {eval_exec, optimize_exec}, - {eval_gid, optimize_gid}, - {eval_inum, optimize_inum}, - {eval_links, optimize_links}, - {eval_lname, optimize_fnmatch}, - {eval_name, optimize_fnmatch}, - {eval_path, optimize_fnmatch}, - {eval_samefile, optimize_samefile}, - {eval_size, optimize_size}, - {eval_type, optimize_type}, - {eval_uid, optimize_uid}, - {eval_xtype, optimize_xtype}, - - // Operators - {eval_and, optimize_and_expr_recursive}, - {eval_comma, optimize_comma_expr_recursive}, - {eval_not, optimize_not_expr_recursive}, - {eval_or, optimize_or_expr_recursive}, -}; +/** Data flow visitor exit. */ +static struct bfs_expr *data_flow_leave(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (expr->always_true) { + expr->probability = 1.0; + df_init_bottom(&opt->after_false); + } -/** - * Look up the appropriate optimizer for an expression and call it. - */ -static struct bfs_expr *optimize_expr_lookup(struct bfs_opt *opt, struct bfs_expr *expr) { - for (size_t i = 0; i < countof(opt_pure); ++i) { - if (opt_pure[i] == expr->eval_fn) { - expr->pure = true; - break; - } + if (expr->always_false) { + expr->probability = 0.0; + df_init_bottom(&opt->after_true); } - for (size_t i = 0; i < countof(opt_always_true); ++i) { - if (opt_always_true[i] == expr->eval_fn) { + df_dump(opt, "after true", &opt->after_true); + df_dump(opt, "after false", &opt->after_false); + + if (df_is_bottom(&opt->after_false)) { + if (!expr->pure) { expr->always_true = true; - break; + expr->probability = 0.0; + } else if (expr->eval_fn != eval_true) { + opt_warning(opt, expr, "This expression is always true.\n\n"); + opt_debug(opt, "pure, always true\n"); + expr = opt_const(opt, true); + if (!expr) { + return NULL; + } } } - for (size_t i = 0; i < countof(opt_always_false); ++i) { - if (opt_always_false[i] == expr->eval_fn) { + if (df_is_bottom(&opt->after_true)) { + if (!expr->pure) { expr->always_false = true; - break; + expr->probability = 0.0; + } else if (expr->eval_fn != eval_false) { + opt_warning(opt, expr, "This expression is always false.\n\n"); + opt_debug(opt, "pure, always false\n"); + expr = opt_const(opt, false); + if (!expr) { + return NULL; + } } } - expr->cost = FAST_COST; - for (size_t i = 0; i < countof(opt_costs); ++i) { - if (opt_costs[i].eval_fn == expr->eval_fn) { - expr->cost = opt_costs[i].cost; - break; + return visit_leave(opt, expr, visitor); +} + +/** Data flow visitor function. */ +static struct bfs_expr *data_flow_visit(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (opt->ignore_result && expr->pure) { + opt_debug(opt, "ignored result\n"); + opt_warning(opt, expr, "The result of this expression is ignored.\n\n"); + expr = opt_const(opt, false); + if (!expr) { + return NULL; } } - for (size_t i = 0; i < countof(opt_probs); ++i) { - if (opt_probs[i].eval_fn == expr->eval_fn) { - expr->probability = opt_probs[i].probability; - break; + if (df_is_bottom(&opt->before)) { + opt_debug(opt, "unreachable\n"); + opt_warning(opt, expr, "This expression is unreachable.\n\n"); + expr = opt_const(opt, false); + if (!expr) { + return NULL; } } - for (size_t i = 0; i < countof(opt_preds); ++i) { - if (opt_preds[i].eval_fn == expr->eval_fn) { - opt_constrain_pred(opt, opt_preds[i].pred, true); + /** Table of simple predicates. */ + static const struct { + bfs_eval_fn *eval_fn; + enum pred_type pred; + } preds[] = { + {eval_acl, ACL_PRED}, + {eval_capable, CAPABLE_PRED}, + {eval_empty, EMPTY_PRED}, + {eval_hidden, HIDDEN_PRED}, + {eval_nogroup, NOGROUP_PRED}, + {eval_nouser, NOUSER_PRED}, + {eval_sparse, SPARSE_PRED}, + {eval_xattr, XATTR_PRED}, + }; + + for (size_t i = 0; i < countof(preds); ++i) { + if (preds[i].eval_fn == expr->eval_fn) { + data_flow_pred(opt, preds[i].pred, true); break; } } - for (size_t i = 0; i < countof(opt_ranges); ++i) { - if (opt_ranges[i].eval_fn == expr->eval_fn) { - optimize_icmp(opt, expr, opt_ranges[i].range); + /** Table of simple range comparisons. */ + static const struct { + bfs_eval_fn *eval_fn; + enum range_type range; + } ranges[] = { + {eval_depth, DEPTH_RANGE}, + {eval_gid, GID_RANGE}, + {eval_inum, INUM_RANGE}, + {eval_links, LINKS_RANGE}, + {eval_size, SIZE_RANGE}, + {eval_uid, UID_RANGE}, + }; + + for (size_t i = 0; i < countof(ranges); ++i) { + if (ranges[i].eval_fn == expr->eval_fn) { + data_flow_icmp(opt, expr, ranges[i].range); break; } } - for (size_t i = 0; i < countof(opt_fns); ++i) { - if (opt_fns[i].eval_fn == expr->eval_fn) { - return opt_fns[i].opt_fn(opt, expr); - } + return expr; +} + +/** + * Data flow visitor. + */ +static const struct visitor data_flow = { + .name = "data_flow", + .enter = data_flow_enter, + .visit = data_flow_visit, + .leave = data_flow_leave, + .table = { + {eval_access, data_flow_access}, + {eval_gid, data_flow_gid}, + {eval_inum, data_flow_inum}, + {eval_links, data_flow_links}, + {eval_samefile, data_flow_samefile}, + {eval_size, data_flow_size}, + {eval_type, data_flow_type}, + {eval_uid, data_flow_uid}, + {eval_xtype, data_flow_xtype}, + {NULL, NULL}, + }, +}; + +/** Simplify a negation. */ +static struct bfs_expr *simplify_not(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + if (opt->ignore_result) { + opt_debug(opt, "ignored result\n"); + expr = only_child(expr); } return expr; } -static struct bfs_expr *optimize_expr_recursive(struct bfs_opt *opt, struct bfs_expr *expr) { - int optlevel = opt->ctx->optlevel; +/** Lift negations out of a conjunction/disjunction using De Morgan's laws. */ +static struct bfs_expr *lift_andor_not(struct bfs_opt *opt, struct bfs_expr *expr) { + // Only lift negations if it would reduce the number of (-not) expressions + size_t added = 0, removed = 0; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (child->eval_fn == eval_not) { + ++removed; + } else { + ++added; + } + } + if (added >= removed) { + return visit_shallow(opt, expr, &annotate); + } - opt->after_true = opt->before; - opt->after_false = opt->before; + opt_debug(opt, "De Morgan's laws\n"); - if (optlevel >= 2 && df_is_bottom(&opt->before)) { - struct bfs_expr *ret = opt_const(opt, false); - opt_debug(opt, 2, "reachability: %pe --> %pe\n", expr, ret); - opt_warning(opt, expr, "This expression is unreachable.\n\n"); - return ret; + if (expr->eval_fn == eval_and) { + expr->eval_fn = eval_or; + expr->argv = &fake_or_arg; + } else { + bfs_assert(expr->eval_fn == eval_or); + expr->eval_fn = eval_and; + expr->argv = &fake_and_arg; + } + + struct bfs_exprs children; + foster_children(expr, &children); + + struct bfs_expr *child; + while ((child = SLIST_POP(&children))) { + opt_enter(opt, "%pe\n", child); + + child = negate_expr(opt, child, &fake_not_arg); + if (!child) { + return NULL; + } + + opt_leave(opt, "%pe\n", child); + bfs_expr_append(expr, child); } - expr = optimize_expr_lookup(opt, expr); - if (!expr) { + expr = visit_shallow(opt, expr, &annotate); + return negate_expr(opt, expr, &fake_not_arg); +} + +/** Get the first ignorable expression in a conjunction/disjunction. */ +static struct bfs_expr *first_ignorable(struct bfs_opt *opt, struct bfs_expr *expr) { + if (opt->level < 2 || !opt->ignore_result) { return NULL; } - if (bfs_expr_is_parent(expr)) { - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; - if (rhs) { - expr->persistent_fds = rhs->persistent_fds; - expr->ephemeral_fds = rhs->ephemeral_fds; + struct bfs_expr *ret = NULL; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + if (!child->pure) { + ret = NULL; + } else if (!ret) { + ret = child; + } + } + + return ret; +} + +/** Simplify a conjunction. */ +static struct bfs_expr *simplify_and(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *ignorable = first_ignorable(opt, expr); + bool ignore = false; + + struct bfs_exprs children; + foster_children(expr, &children); + + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); + + if (child == ignorable) { + ignore = true; + } + + if (ignore) { + opt_delete(opt, "%pe [ignored result]\n", child); + opt_warning(opt, child, "The result of this expression is ignored.\n\n"); + continue; + } + + if (child->eval_fn == eval_true) { + opt_delete(opt, "%pe [conjunction elimination]\n", child); + continue; } - if (lhs) { - expr->persistent_fds += lhs->persistent_fds; - if (lhs->ephemeral_fds > expr->ephemeral_fds) { - expr->ephemeral_fds = lhs->ephemeral_fds; + + opt_visit(opt, "%pe\n", child); + bfs_expr_append(expr, child); + + if (child->always_false) { + while ((child = SLIST_POP(&children))) { + opt_delete(opt, "%pe [short-circuit]\n", child); } } - } else if (!expr->pure) { - df_join(opt->impure, &opt->before); } - if (expr->always_true) { - expr->probability = 1.0; - df_init_bottom(&opt->after_false); - } - if (expr->always_false) { - expr->probability = 0.0; - df_init_bottom(&opt->after_true); + struct bfs_expr *child = bfs_expr_children(expr); + if (!child) { + opt_debug(opt, "nullary identity\n"); + return opt_const(opt, true); + } else if (!child->next) { + opt_debug(opt, "unary identity\n"); + return only_child(expr); } - if (optlevel < 2 || expr->eval_fn == eval_true || expr->eval_fn == eval_false) { - return expr; - } + return lift_andor_not(opt, expr); +} - if (df_is_bottom(&opt->after_true)) { - if (expr->pure) { - struct bfs_expr *ret = opt_const(opt, false); - opt_warning(opt, expr, "This expression is always false.\n\n"); - opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); - return ret; - } else { - expr->always_false = true; - expr->probability = 0.0; +/** Simplify a disjunction. */ +static struct bfs_expr *simplify_or(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_expr *ignorable = first_ignorable(opt, expr); + bool ignore = false; + + struct bfs_exprs children; + foster_children(expr, &children); + + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); + + if (child == ignorable) { + ignore = true; } - } else if (df_is_bottom(&opt->after_false)) { - if (expr->pure) { - struct bfs_expr *ret = opt_const(opt, true); - opt_debug(opt, 2, "data flow: %pe --> %pe\n", expr, ret); - opt_warning(opt, expr, "This expression is always true.\n\n"); - return ret; - } else { - expr->always_true = true; - expr->probability = 1.0; + + if (ignore) { + opt_delete(opt, "%pe [ignored result]\n", child); + opt_warning(opt, child, "The result of this expression is ignored.\n\n"); + continue; } - } - return expr; -} + if (child->eval_fn == eval_false) { + opt_delete(opt, "%pe [disjunctive syllogism]\n", child); + continue; + } -/** Swap the children of a binary expression if it would reduce the cost. */ -static bool reorder_expr(const struct bfs_opt *opt, struct bfs_expr *expr, float swapped_cost) { - if (swapped_cost < expr->cost) { - bool debug = opt_debug(opt, 3, "cost: %pe <==> ", expr); - struct bfs_expr *lhs = expr->lhs; - expr->lhs = expr->rhs; - expr->rhs = lhs; - if (debug) { - cfprintf(opt->ctx->cerr, "%pe (~${ylw}%g${rs} --> ~${ylw}%g${rs})\n", expr, expr->cost, swapped_cost); + opt_visit(opt, "%pe\n", child); + bfs_expr_append(expr, child); + + if (child->always_true) { + while ((child = SLIST_POP(&children))) { + opt_delete(opt, "%pe [short-circuit]\n", child); + } } - expr->cost = swapped_cost; - return true; - } else { - return false; } -} -/** - * Recursively reorder sub-expressions to reduce the overall cost. - * - * @param expr - * The expression to optimize. - * @return - * Whether any subexpression was reordered. - */ -static bool reorder_expr_recursive(const struct bfs_opt *opt, struct bfs_expr *expr) { - if (!bfs_expr_is_parent(expr)) { - return false; + struct bfs_expr *child = bfs_expr_children(expr); + if (!child) { + opt_debug(opt, "nullary identity\n"); + return opt_const(opt, false); + } else if (!child->next) { + opt_debug(opt, "unary identity\n"); + return only_child(expr); } - struct bfs_expr *lhs = expr->lhs; - struct bfs_expr *rhs = expr->rhs; + return lift_andor_not(opt, expr); +} - bool ret = false; - if (lhs) { - ret |= reorder_expr_recursive(opt, lhs); - } - if (rhs) { - ret |= reorder_expr_recursive(opt, rhs); - } +/** Simplify a comma expression. */ +static struct bfs_expr *simplify_comma(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + struct bfs_exprs children; + foster_children(expr, &children); + + while (!SLIST_EMPTY(&children)) { + struct bfs_expr *child = SLIST_POP(&children); - if (expr->eval_fn == eval_and || expr->eval_fn == eval_or) { - if (lhs->pure && rhs->pure) { - float rhs_prob = expr->eval_fn == eval_and ? rhs->probability : 1.0 - rhs->probability; - float swapped_cost = rhs->cost + rhs_prob * lhs->cost; - ret |= reorder_expr(opt, expr, swapped_cost); + if (opt->level >= 2 && child->pure && !SLIST_EMPTY(&children)) { + opt_delete(opt, "%pe [ignored result]\n", child); + opt_warning(opt, child, "The result of this expression is ignored.\n\n"); + continue; } + + opt_visit(opt, "%pe\n", child); + bfs_expr_append(expr, child); } - return ret; + struct bfs_expr *child = bfs_expr_children(expr); + if (child && !child->next) { + opt_debug(opt, "unary identity\n"); + return only_child(expr); + } + + return expr; } /** - * Optimize a top-level expression. + * Logical simplification visitor. */ -static struct bfs_expr *optimize_expr(struct bfs_opt *opt, struct bfs_expr *expr) { - struct df_domain saved_impure = *opt->impure; +static const struct visitor simplify = { + .name = "simplify", + .table = { + {eval_not, simplify_not}, + {eval_and, simplify_and}, + {eval_or, simplify_or}, + {eval_comma, simplify_comma}, + {NULL, NULL}, + }, +}; - expr = optimize_expr_recursive(opt, expr); - if (!expr) { - return NULL; - } +/** Optimize an expression. */ +static struct bfs_expr *optimize(struct bfs_opt *opt, struct bfs_expr *expr) { + opt_enter(opt, "pass 0:\n"); + expr = visit(opt, expr, &annotate); + opt_leave(opt, NULL); + + /** Table of optimization passes. */ + static const struct { + /** Minimum optlevel for this pass. */ + int level; + /** The visitor for this pass. */ + const struct visitor *visitor; + } passes[] = { + {1, &canonicalize}, + {3, &reorder}, + {2, &data_flow}, + {1, &simplify}, + }; - if (opt->ctx->optlevel >= 3 && reorder_expr_recursive(opt, expr)) { - // Re-do optimizations to account for the new ordering - *opt->impure = saved_impure; - expr = optimize_expr_recursive(opt, expr); - if (!expr) { - return NULL; + struct df_domain impure; + + for (int i = 0; i < 3; ++i) { + struct bfs_opt nested = *opt; + nested.impure = &impure; + impure = *opt->impure; + + opt_enter(&nested, "pass %d:\n", i + 1); + + for (size_t j = 0; j < countof(passes); ++j) { + if (opt->level < passes[j].level) { + continue; + } + + // Skip reordering the first time through the passes, to + // make warnings more understandable + if (passes[j].visitor == &reorder) { + if (i == 0) { + continue; + } else { + nested.warn = false; + } + } + + expr = visit(&nested, expr, passes[j].visitor); + if (!expr) { + return NULL; + } + } + + opt_leave(&nested, NULL); + + if (!bfs_expr_is_parent(expr)) { + break; } } + *opt->impure = impure; return expr; } @@ -1332,30 +2137,37 @@ int bfs_optimize(struct bfs_ctx *ctx) { struct bfs_opt opt = { .ctx = ctx, + .level = ctx->optlevel, + .depth = 0, + .warn = ctx->warn, + .ignore_result = false, .impure = &impure, }; df_init_top(&opt.before); - ctx->exclude = optimize_expr(&opt, ctx->exclude); + ctx->exclude = optimize(&opt, ctx->exclude); if (!ctx->exclude) { return -1; } // Only non-excluded files are evaluated opt.before = opt.after_false; + opt.ignore_result = true; struct df_range *depth = &opt.before.ranges[DEPTH_RANGE]; - constrain_min(depth, ctx->mindepth); - constrain_max(depth, ctx->maxdepth); + if (ctx->mindepth > 0) { + constrain_min(depth, ctx->mindepth); + } + if (ctx->maxdepth < INT_MAX) { + constrain_max(depth, ctx->maxdepth); + } - ctx->expr = optimize_expr(&opt, ctx->expr); + ctx->expr = optimize(&opt, ctx->expr); if (!ctx->expr) { return -1; } - ctx->expr = ignore_result(&opt, ctx->expr); - - if (df_is_bottom(&impure)) { + if (opt.level >= 2 && df_is_bottom(&impure)) { bfs_warning(ctx, "This command won't do anything.\n\n"); } @@ -1363,23 +2175,27 @@ int bfs_optimize(struct bfs_ctx *ctx) { long long mindepth = impure_depth->min; long long maxdepth = impure_depth->max; - int optlevel = ctx->optlevel; + opt_enter(&opt, "post-process:\n"); - if (optlevel >= 2 && mindepth > ctx->mindepth) { + if (opt.level >= 2 && mindepth > ctx->mindepth) { if (mindepth > INT_MAX) { mindepth = INT_MAX; } + opt_enter(&opt, "${blu}-mindepth${rs} ${bld}%d${rs}\n", ctx->mindepth); ctx->mindepth = mindepth; - opt_debug(&opt, 2, "data flow: mindepth --> %d\n", ctx->mindepth); + opt_leave(&opt, "${blu}-mindepth${rs} ${bld}%d${rs}\n", ctx->mindepth); } - if (optlevel >= 4 && maxdepth < ctx->maxdepth) { + if (opt.level >= 4 && maxdepth < ctx->maxdepth) { if (maxdepth < INT_MIN) { maxdepth = INT_MIN; } + opt_enter(&opt, "${blu}-maxdepth${rs} ${bld}%d${rs}\n", ctx->maxdepth); ctx->maxdepth = maxdepth; - opt_debug(&opt, 4, "data flow: maxdepth --> %d\n", ctx->maxdepth); + opt_leave(&opt, "${blu}-maxdepth${rs} ${bld}%d${rs}\n", ctx->maxdepth); } + opt_leave(&opt, NULL); + return 0; } diff --git a/src/parse.c b/src/parse.c index 17fe8ad..4212196 100644 --- a/src/parse.c +++ b/src/parse.c @@ -21,6 +21,7 @@ #include "exec.h" #include "expr.h" #include "fsade.h" +#include "list.h" #include "opt.h" #include "printf.h" #include "pwcache.h" @@ -47,10 +48,9 @@ #include // Strings printed by -D tree for "fake" expressions -static char *fake_and_arg = "-a"; -static char *fake_false_arg = "-false"; +static char *fake_and_arg = "-and"; static char *fake_hidden_arg = "-hidden"; -static char *fake_or_arg = "-o"; +static char *fake_or_arg = "-or"; static char *fake_print_arg = "-print"; static char *fake_true_arg = "-true"; @@ -319,12 +319,8 @@ static struct bfs_expr *new_unary_expr(const struct bfs_parser *parser, bfs_eval return NULL; } - expr->lhs = NULL; - expr->rhs = rhs; bfs_assert(bfs_expr_is_parent(expr)); - - expr->persistent_fds = rhs->persistent_fds; - expr->ephemeral_fds = rhs->ephemeral_fds; + bfs_expr_append(expr, rhs); return expr; } @@ -337,17 +333,9 @@ static struct bfs_expr *new_binary_expr(const struct bfs_parser *parser, bfs_eva return NULL; } - expr->lhs = lhs; - expr->rhs = rhs; bfs_assert(bfs_expr_is_parent(expr)); - - expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; - if (lhs->ephemeral_fds > rhs->ephemeral_fds) { - expr->ephemeral_fds = lhs->ephemeral_fds; - } else { - expr->ephemeral_fds = rhs->ephemeral_fds; - } - + bfs_expr_append(expr, lhs); + bfs_expr_append(expr, rhs); return expr; } @@ -774,19 +762,6 @@ static struct bfs_expr *parse_unary_action(struct bfs_parser *parser, bfs_eval_f return parse_action(parser, eval_fn, 2); } -/** - * Add an expression to the exclusions. - */ -static int parse_exclude(struct bfs_parser *parser, struct bfs_expr *expr) { - struct bfs_ctx *ctx = parser->ctx; - ctx->exclude = new_binary_expr(parser, eval_or, ctx->exclude, expr, &fake_or_arg); - if (ctx->exclude) { - return 0; - } else { - return -1; - } -} - /** * Parse a test expression with integer data and a comparison flag. */ @@ -1839,10 +1814,7 @@ static struct bfs_expr *parse_nohidden(struct bfs_parser *parser, int arg1, int return NULL; } - if (parse_exclude(parser, hidden) != 0) { - return NULL; - } - + bfs_expr_append(parser->ctx->exclude, hidden); return parse_nullary_option(parser); } @@ -3206,10 +3178,7 @@ static struct bfs_expr *parse_factor(struct bfs_parser *parser) { parser->excluding = false; - if (parse_exclude(parser, factor) != 0) { - return NULL; - } - + bfs_expr_append(parser->ctx->exclude, factor); return parse_new_expr(parser, eval_true, parser->argv - argv, argv); } else if (strcmp(arg, "!") == 0 || strcmp(arg, "-not") == 0) { char **argv = parser_advance(parser, T_OPERATOR, 1); @@ -3428,19 +3397,29 @@ static void dump_expr_multiline(const struct bfs_ctx *ctx, enum debug_flags flag cfprintf(ctx->cerr, " "); } + bool close = true; + if (bfs_expr_is_parent(expr)) { - cfprintf(ctx->cerr, "(${red}%s${rs}\n", expr->argv[0]); - if (expr->lhs) { - dump_expr_multiline(ctx, flag, expr->lhs, indent + 1, 0); + if (SLIST_EMPTY(&expr->children)) { + cfprintf(ctx->cerr, "(${red}%s${rs}", expr->argv[0]); + ++rparens; + } else { + cfprintf(ctx->cerr, "(${red}%s${rs}\n", expr->argv[0]); + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + int parens = child->next ? 0 : rparens + 1; + dump_expr_multiline(ctx, flag, child, indent + 1, parens); + } + close = false; } - dump_expr_multiline(ctx, flag, expr->rhs, indent + 1, rparens + 1); } else { if (flag == DEBUG_RATES) { cfprintf(ctx->cerr, "%pE", expr); } else { cfprintf(ctx->cerr, "%pe", expr); } + } + if (close) { for (int i = 0; i < rparens; ++i) { cfprintf(ctx->cerr, ")"); } @@ -3540,10 +3519,8 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { fputs("\n", stderr); - if (ctx->exclude->eval_fn != eval_false) { - bfs_debug(ctx, flag, "(${red}-exclude${rs}\n"); - dump_expr_multiline(ctx, flag, ctx->exclude, 1, 1); - } + bfs_debug(ctx, flag, "(${red}-exclude${rs}\n"); + dump_expr_multiline(ctx, flag, ctx->exclude, 1, 1); dump_expr_multiline(ctx, flag, ctx->expr, 0, 0); } @@ -3638,7 +3615,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { .now = ctx->now, }; - ctx->exclude = parse_new_expr(&parser, eval_false, 1, &fake_false_arg); + ctx->exclude = parse_new_expr(&parser, eval_or, 1, &fake_or_arg); if (!ctx->exclude) { goto fail; } -- cgit v1.2.3 From a726c7128ab5c6fffeb6844f3d819ada0369e6a8 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 31 Jan 2024 19:51:51 -0500 Subject: opt: Charge eval_flags() for a stat() call --- src/opt.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index 7203c61..28a2255 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1218,6 +1218,7 @@ static struct bfs_expr *annotate_visit(struct bfs_opt *opt, struct bfs_expr *exp {eval_acl, STAT_COST}, {eval_capable, STAT_COST}, {eval_empty, 2 * STAT_COST}, // readdir() is worse than stat() + {eval_flags, STAT_COST}, {eval_fls, PRINT_COST}, {eval_fprint, PRINT_COST}, {eval_fprint0, PRINT_COST}, -- cgit v1.2.3 From 5f0958fa57770e13b8696b8ee6fc87890333d90b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 5 Feb 2024 15:13:38 -0500 Subject: opt: Enable BFTW_STAT when profitable --- src/expr.h | 2 ++ src/opt.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) (limited to 'src/opt.c') diff --git a/src/expr.h b/src/expr.h index 4d607a4..349e052 100644 --- a/src/expr.h +++ b/src/expr.h @@ -110,6 +110,8 @@ struct bfs_expr { bool always_true; /** Whether this expression always evaluates to false. */ bool always_false; + /** Whether this expression uses stat(). */ + bool calls_stat; /** Estimated cost. */ float cost; diff --git a/src/opt.c b/src/opt.c index 28a2255..74145ac 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1004,6 +1004,13 @@ static struct bfs_expr *annotate_fnmatch(struct bfs_opt *opt, struct bfs_expr *e return expr; } +/** Annotate -f?print. */ +static struct bfs_expr *annotate_fprint(struct bfs_opt *opt, struct bfs_expr *expr, const struct visitor *visitor) { + const struct colors *colors = expr->cfile->colors; + expr->calls_stat = colors && colors_need_stat(colors); + return expr; +} + /** Estimate probability for -x?type. */ static void estimate_type_probability(struct bfs_expr *expr) { unsigned int types = expr->num; @@ -1204,6 +1211,38 @@ static struct bfs_expr *annotate_visit(struct bfs_opt *opt, struct bfs_expr *exp } } + /** Table of stat-calling primaries. */ + static bfs_eval_fn *const calls_stat[] = { + eval_empty, + eval_flags, + eval_fls, + eval_fprintf, + eval_fstype, + eval_gid, + eval_inum, + eval_links, + eval_newer, + eval_nogroup, + eval_nouser, + eval_perm, + eval_samefile, + eval_size, + eval_sparse, + eval_time, + eval_uid, + eval_used, + eval_xattr, + eval_xattrname, + }; + + expr->calls_stat = false; + for (size_t i = 0; i < countof(calls_stat); ++i) { + if (expr->eval_fn == calls_stat[i]) { + expr->calls_stat = true; + break; + } + } + #define FAST_COST 40.0 #define FNMATCH_COST 400.0 #define STAT_COST 1000.0 @@ -1294,6 +1333,7 @@ static const struct visitor annotate = { {eval_access, annotate_access}, {eval_empty, annotate_empty}, {eval_exec, annotate_exec}, + {eval_fprint, annotate_fprint}, {eval_lname, annotate_fnmatch}, {eval_name, annotate_fnmatch}, {eval_path, annotate_fnmatch}, @@ -2130,6 +2170,43 @@ static struct bfs_expr *optimize(struct bfs_opt *opt, struct bfs_expr *expr) { return expr; } +/** Estimate the odds of an expression calling stat(). */ +static float expr_stat_odds(struct bfs_expr *expr) { + if (expr->calls_stat) { + return 1.0; + } + + float nostat_odds = 1.0; + float reached_odds = 1.0; + for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) { + float child_odds = expr_stat_odds(child); + nostat_odds *= 1.0 - reached_odds * child_odds; + + if (expr->eval_fn == eval_and) { + reached_odds *= child->probability; + } else if (expr->eval_fn == eval_or) { + reached_odds *= 1.0 - child->probability; + } + } + + return 1.0 - nostat_odds; +} + +/** Estimate the odds of calling stat(). */ +static float estimate_stat_odds(struct bfs_ctx *ctx) { + if (ctx->unique) { + return 1.0; + } + + float nostat_odds = 1.0 - expr_stat_odds(ctx->exclude); + + float reached_odds = 1.0 - ctx->exclude->probability; + float expr_odds = expr_stat_odds(ctx->expr); + nostat_odds *= 1.0 - reached_odds * expr_odds; + + return 1.0 - nostat_odds; +} + int bfs_optimize(struct bfs_ctx *ctx) { bfs_ctx_dump(ctx, DEBUG_OPT); @@ -2196,6 +2273,20 @@ int bfs_optimize(struct bfs_ctx *ctx) { opt_leave(&opt, "${blu}-maxdepth${rs} ${bld}%d${rs}\n", ctx->maxdepth); } + if (opt.level >= 3) { + // bfs_eval() can do lazy stat() calls, but only on one thread. + float lazy_cost = estimate_stat_odds(ctx); + // bftw() can do eager stat() calls in parallel + float eager_cost = 1.0 / ctx->threads; + + if (eager_cost <= lazy_cost) { + opt_enter(&opt, "lazy stat cost: ${ylw}%g${rs}\n", lazy_cost); + ctx->flags |= BFTW_STAT; + opt_leave(&opt, "eager stat cost: ${ylw}%g${rs}\n", eager_cost); + } + + } + opt_leave(&opt, NULL); return 0; -- cgit v1.2.3 From 2c3ef3a06ee1f951f6d68be6d0d3f6a1822b05b7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 11 Mar 2024 09:51:03 -0400 Subject: Re-run include-what-you-use --- src/alloc.h | 1 + src/bfstd.c | 6 +++--- src/bftw.c | 1 + src/color.h | 1 - src/ctx.c | 1 + src/ctx.h | 2 ++ src/diag.c | 2 +- src/dir.h | 4 ++-- src/dstring.c | 2 ++ src/eval.c | 4 ++-- src/expr.c | 4 ++-- src/expr.h | 1 - src/ioq.c | 5 +++-- src/main.c | 1 + src/opt.c | 3 ++- src/parse.c | 3 +-- src/printf.c | 3 ++- src/trie.c | 2 -- src/trie.h | 1 - src/xspawn.c | 1 - src/xtime.c | 1 - tests/alloc.c | 1 + tests/bfstd.c | 3 --- tests/bit.c | 2 +- tests/ioq.c | 2 ++ tests/main.c | 3 --- tests/xtime.c | 4 ++-- tests/xtouch.c | 1 + 28 files changed, 33 insertions(+), 32 deletions(-) (limited to 'src/opt.c') diff --git a/src/alloc.h b/src/alloc.h index 60dd738..ae055bc 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -10,6 +10,7 @@ #include "config.h" #include +#include #include /** Check if a size is properly aligned. */ diff --git a/src/bfstd.c b/src/bfstd.c index ce4aa49..c6c2e7f 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -2,18 +2,19 @@ // SPDX-License-Identifier: 0BSD #include "bfstd.h" -#include "bit.h" #include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" #include "xregex.h" -#include #include #include #include +#include #include #include +#include +#include #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #if BFS_USE_SYS_SYSMACROS_H # include diff --git a/src/bftw.c b/src/bftw.c index 6f52299..50b8b02 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -35,6 +35,7 @@ #include #include #include +#include /** Initialize a bftw_stat cache. */ static void bftw_stat_init(struct bftw_stat *bufs, struct bfs_stat *stat_buf, struct bfs_stat *lstat_buf) { diff --git a/src/color.h b/src/color.h index 85633a4..e3e7973 100644 --- a/src/color.h +++ b/src/color.h @@ -10,7 +10,6 @@ #include "config.h" #include "dstring.h" -#include #include /** diff --git a/src/ctx.c b/src/ctx.c index 6c84f75..f5b28c7 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -6,6 +6,7 @@ #include "color.h" #include "diag.h" #include "expr.h" +#include "list.h" #include "mtab.h" #include "pwcache.h" #include "stat.h" diff --git a/src/ctx.h b/src/ctx.h index aa91f2c..e14db21 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -18,6 +18,8 @@ #include #include +struct CFILE; + /** * The execution context for bfs. */ diff --git a/src/diag.c b/src/diag.c index 656fa89..cb27b92 100644 --- a/src/diag.c +++ b/src/diag.c @@ -11,8 +11,8 @@ #include "expr.h" #include #include +#include #include -#include /** bfs_diagf() implementation. */ attr(printf(2, 0)) diff --git a/src/dir.h b/src/dir.h index b11d454..18d907e 100644 --- a/src/dir.h +++ b/src/dir.h @@ -8,8 +8,6 @@ #ifndef BFS_DIR_H #define BFS_DIR_H -#include "alloc.h" -#include "config.h" #include /** @@ -78,6 +76,8 @@ struct bfs_dirent { */ struct bfs_dir *bfs_allocdir(void); +struct arena; + /** * Initialize an arena for directories. * diff --git a/src/dstring.c b/src/dstring.c index bc18308..10b0fad 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -7,6 +7,8 @@ #include "config.h" #include "diag.h" #include +#include +#include #include #include #include diff --git a/src/eval.c b/src/eval.c index 1711001..9e55964 100644 --- a/src/eval.c +++ b/src/eval.c @@ -25,7 +25,6 @@ #include "stat.h" #include "trie.h" #include "xregex.h" -#include "xtime.h" #include #include #include @@ -36,8 +35,9 @@ #include #include #include +#include #include -#include +#include #include #include #include diff --git a/src/expr.c b/src/expr.c index 3e0033f..5784220 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4,12 +4,12 @@ #include "expr.h" #include "alloc.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "exec.h" +#include "list.h" #include "printf.h" #include "xregex.h" -#include -#include #include struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval_fn, size_t argc, char **argv) { diff --git a/src/expr.h b/src/expr.h index 957b04a..75cb5fd 100644 --- a/src/expr.h +++ b/src/expr.h @@ -12,7 +12,6 @@ #include "config.h" #include "eval.h" #include "stat.h" -#include #include #include diff --git a/src/ioq.c b/src/ioq.c index 00c3b86..37eed7d 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -126,13 +126,14 @@ #include "config.h" #include "diag.h" #include "dir.h" -#include "sanity.h" #include "stat.h" #include "thread.h" -#include #include +#include #include +#include #include +#include #if BFS_USE_LIBURING # include diff --git a/src/main.c b/src/main.c index 16a2576..e120f03 100644 --- a/src/main.c +++ b/src/main.c @@ -48,6 +48,7 @@ #include "bfstd.h" #include "config.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "parse.h" #include diff --git a/src/opt.c b/src/opt.c index 74145ac..76965de 100644 --- a/src/opt.c +++ b/src/opt.c @@ -26,11 +26,13 @@ */ #include "opt.h" +#include "bftw.h" #include "bit.h" #include "color.h" #include "config.h" #include "ctx.h" #include "diag.h" +#include "dir.h" #include "eval.h" #include "exec.h" #include "expr.h" @@ -40,7 +42,6 @@ #include #include #include -#include #include static char *fake_and_arg = "-and"; diff --git a/src/parse.c b/src/parse.c index b26a50f..3b7386d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -42,8 +42,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/src/printf.c b/src/printf.c index 34ed606..487f039 100644 --- a/src/printf.c +++ b/src/printf.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "printf.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" @@ -14,10 +15,10 @@ #include "mtab.h" #include "pwcache.h" #include "stat.h" -#include "xtime.h" #include #include #include +#include #include #include #include diff --git a/src/trie.c b/src/trie.c index bd5300d..1ffb23a 100644 --- a/src/trie.c +++ b/src/trie.c @@ -87,9 +87,7 @@ #include "config.h" #include "diag.h" #include "list.h" -#include #include -#include #include bfs_static_assert(CHAR_WIDTH == 8); diff --git a/src/trie.h b/src/trie.h index 02088f1..4288d76 100644 --- a/src/trie.h +++ b/src/trie.h @@ -5,7 +5,6 @@ #define BFS_TRIE_H #include "alloc.h" -#include "config.h" #include "list.h" #include #include diff --git a/src/xspawn.c b/src/xspawn.c index 8d6108b..6a94d3d 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #if BFS_USE_PATHS_H diff --git a/src/xtime.c b/src/xtime.c index 05f0e1a..5b259ab 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -7,7 +7,6 @@ #include "diag.h" #include #include -#include #include #include #include diff --git a/tests/alloc.c b/tests/alloc.c index 4ce23d4..9f08111 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -3,6 +3,7 @@ #include "tests.h" #include "../src/alloc.h" +#include "../src/config.h" #include "../src/diag.h" #include #include diff --git a/tests/bfstd.c b/tests/bfstd.c index 0ded5de..26abdb6 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -7,9 +7,6 @@ #include "../src/diag.h" #include #include -#include -#include -#include #include #include diff --git a/tests/bit.c b/tests/bit.c index b944748..3d66ce3 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -3,10 +3,10 @@ #include "tests.h" #include "../src/bit.h" +#include "../src/config.h" #include "../src/diag.h" #include #include -#include bfs_static_assert(UMAX_WIDTH(0x1) == 1); bfs_static_assert(UMAX_WIDTH(0x3) == 2); diff --git a/tests/ioq.c b/tests/ioq.c index 56e1886..1ce8f75 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -4,10 +4,12 @@ #include "tests.h" #include "../src/ioq.h" #include "../src/bfstd.h" +#include "../src/config.h" #include "../src/diag.h" #include "../src/dir.h" #include #include +#include /** * Test for blocking within ioq_slot_push(). diff --git a/tests/main.c b/tests/main.c index 38438b2..8849e8c 100644 --- a/tests/main.c +++ b/tests/main.c @@ -6,11 +6,8 @@ */ #include "tests.h" -#include "../src/bfstd.h" #include "../src/color.h" #include "../src/config.h" -#include "../src/diag.h" -#include #include #include #include diff --git a/tests/xtime.c b/tests/xtime.c index 3f1fec2..c8dc00b 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -5,10 +5,10 @@ #include "../src/xtime.h" #include "../src/bfstd.h" #include "../src/config.h" +#include "../src/diag.h" #include +#include #include -#include -#include #include static bool tm_equal(const struct tm *tma, const struct tm *tmb) { diff --git a/tests/xtouch.c b/tests/xtouch.c index 8c5c5f3..fad272f 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From 912d2b94cf6ff0871c07325af5ed520a2bc97722 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 20 Mar 2024 10:44:34 -0400 Subject: Implement -limit N Closes: https://github.com/tavianator/bfs/issues/133 --- completions/bfs.bash | 3 +-- completions/bfs.fish | 1 + completions/bfs.zsh | 1 + docs/USAGE.md | 34 ++++++++++++++++++++++++++++++++++ docs/bfs.1 | 5 +++++ src/eval.c | 13 +++++++++++++ src/eval.h | 1 + src/opt.c | 1 + src/parse.c | 38 +++++++++++++++++++++++++++++++++++++- tests/bfs/limit.out | 4 ++++ tests/bfs/limit.sh | 1 + tests/bfs/limit_0.sh | 1 + tests/bfs/limit_implicit_print.sh | 1 + tests/bfs/limit_incomplete.sh | 1 + tests/bfs/limit_one.sh | 1 + 15 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 tests/bfs/limit.out create mode 100644 tests/bfs/limit.sh create mode 100644 tests/bfs/limit_0.sh create mode 100644 tests/bfs/limit_implicit_print.sh create mode 100644 tests/bfs/limit_incomplete.sh create mode 100644 tests/bfs/limit_one.sh (limited to 'src/opt.c') diff --git a/completions/bfs.bash b/completions/bfs.bash index 2f52e8d..db582da 100644 --- a/completions/bfs.bash +++ b/completions/bfs.bash @@ -37,6 +37,7 @@ _bfs() { -ipath -iregex -iwholename + -limit -links -lname -maxdepth @@ -94,8 +95,6 @@ _bfs() { -depth -follow -ignore_readdir_race - -maxdepth - -mindepth -mount -nocolor -noignore_readdir_race diff --git a/completions/bfs.fish b/completions/bfs.fish index 3f399e7..1303639 100644 --- a/completions/bfs.fish +++ b/completions/bfs.fish @@ -133,6 +133,7 @@ complete -c bfs -o fls -d "Like -ls, but write to specified file" -F complete -c bfs -o fprint -d "Like -print, but write to specified file" -F complete -c bfs -o fprint0 -d "Like -print0, but write to specified file" -F complete -c bfs -o fprintf -d "Like -printf, but write to specified file" -F +complete -c bfs -o limit -d "Limit the number of results" -x complete -c bfs -o ls -d "List files like ls -dils" complete -c bfs -o print -d "Print the path to the found file" complete -c bfs -o print0 -d "Like -print, but use the null character as a separator rather than newlines" diff --git a/completions/bfs.zsh b/completions/bfs.zsh index 3d7dc3a..51b5029 100644 --- a/completions/bfs.zsh +++ b/completions/bfs.zsh @@ -133,6 +133,7 @@ args=( '*-fprint0[print the path to the found file using null character as separator, but write to FILE instead of standard output]:output file:_files' '*-fprintf[print according to format string, but write to FILE instead of standard output]:output file:_files:output format' + '*-limit[quit after N results]:maximum result count' '*-ls[list files like ls -dils]' '*-print[print the path to the found file]' '*-print0[print the path to the found file using null character as separator]' diff --git a/docs/USAGE.md b/docs/USAGE.md index 3efdee0..071c95b 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -130,6 +130,40 @@ Unlike `-prune`, `-exclude` even works in combination with `-depth`/`-delete`. --- +### `-limit` + +The `-limit N` action makes `bfs` quit once it gets evaluated `N` times. +Placing it after an action like `-print` limits the number of results that get printed, for example: + +```console +$ bfs -s -type f -name '*.txt' +./1.txt +./2.txt +./3.txt +./4.txt +$ bfs -s -type f -name '*.txt' -print -limit 2 +./1.txt +./2.txt +``` + +This is similar to + +```console +$ bfs -s -type f -name '*.txt' | head -n2 +``` + +but more powerful because you can apply separate limits to different expressions: + +```console +$ bfs \( -name '*.txt' -print -limit 3 -o -name '*.log' -print -limit 4 \) -limit 5 +[At most 3 .txt files, at most 4 .log files, and at most 5 in total] +``` + +and more efficient because it will quit immediately. +When piping to `head`, `bfs` will only quit *after* it tries to output too many results. + +--- + ### `-hidden`/`-nohidden` `-hidden` matches "hidden" files (dotfiles). diff --git a/docs/bfs.1 b/docs/bfs.1 index 2ecb891..3a4f15a 100644 --- a/docs/bfs.1 +++ b/docs/bfs.1 @@ -725,6 +725,11 @@ but write to instead of standard output. .RE .TP +\fB\-limit \fIN\fR +Quit once this action is evaluated +.I N +times. +.TP .B \-ls List files like .B ls diff --git a/src/eval.c b/src/eval.c index 9e55964..2f06858 100644 --- a/src/eval.c +++ b/src/eval.c @@ -840,6 +840,19 @@ error: return true; } +/** + * -limit action. + */ +bool eval_limit(const struct bfs_expr *expr, struct bfs_eval *state) { + long long evals = expr->evaluations + 1; + if (evals >= expr->num) { + state->action = BFTW_STOP; + state->quit = true; + } + + return true; +} + /** * -prune action. */ diff --git a/src/eval.h b/src/eval.h index 98bbc08..f7f6c77 100644 --- a/src/eval.h +++ b/src/eval.h @@ -88,6 +88,7 @@ bool eval_fprint(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_fprint0(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_fprintf(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_fprintx(const struct bfs_expr *expr, struct bfs_eval *state); +bool eval_limit(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_prune(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_quit(const struct bfs_expr *expr, struct bfs_eval *state); diff --git a/src/opt.c b/src/opt.c index 76965de..a470d25 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1181,6 +1181,7 @@ static struct bfs_expr *annotate_visit(struct bfs_opt *opt, struct bfs_expr *exp eval_fprint0, eval_fprintf, eval_fprintx, + eval_limit, eval_prune, eval_true, // Non-returning diff --git a/src/parse.c b/src/parse.c index 3b7386d..2dfcab2 100644 --- a/src/parse.c +++ b/src/parse.c @@ -97,6 +97,8 @@ struct bfs_parser { char **last_arg; /** A "-depth"-type argument, if any. */ char **depth_arg; + /** A "-limit" argument, if any. */ + char **limit_arg; /** A "-prune" argument, if any. */ char **prune_arg; /** A "-mount" argument, if any. */ @@ -733,7 +735,7 @@ static struct bfs_expr *parse_action(struct bfs_parser *parser, bfs_eval_fn *eva return NULL; } - if (eval_fn != eval_prune && eval_fn != eval_quit) { + if (eval_fn != eval_limit && eval_fn != eval_prune && eval_fn != eval_quit) { parser->implicit_print = false; } @@ -1569,6 +1571,29 @@ static struct bfs_expr *parse_jobs(struct bfs_parser *parser, int arg1, int arg2 return expr; } +/** + * Parse -limit N. + */ +static struct bfs_expr *parse_limit(struct bfs_parser *parser, int arg1, int arg2) { + struct bfs_expr *expr = parse_unary_action(parser, eval_limit); + if (!expr) { + return NULL; + } + + char **arg = &expr->argv[1]; + if (!parse_int(parser, arg, *arg, &expr->num, IF_LONG_LONG)) { + return NULL; + } + + if (expr->num <= 0) { + parse_expr_error(parser, expr, "The ${blu}%s${rs} must be at least ${bld}1${rs}.\n", expr->argv[0]); + return NULL; + } + + parser->limit_arg = expr->argv; + return expr; +} + /** * Parse -links N. */ @@ -2845,6 +2870,8 @@ static struct bfs_expr *parse_help(struct bfs_parser *parser, int arg1, int arg2 cfprintf(cout, " ${blu}-fprintf${rs} ${bld}FILE${rs} ${bld}FORMAT${rs}\n"); cfprintf(cout, " Like ${blu}-ls${rs}/${blu}-print${rs}/${blu}-print0${rs}/${blu}-printf${rs}, but write to ${bld}FILE${rs} instead of standard\n" " output\n"); + cfprintf(cout, " ${blu}-limit${rs} ${bld}N${rs}\n"); + cfprintf(cout, " Quit after this action is evaluated ${bld}N${rs} times\n"); cfprintf(cout, " ${blu}-ls${rs}\n"); cfprintf(cout, " List files like ${ex}ls${rs} ${bld}-dils${rs}\n"); cfprintf(cout, " ${blu}-print${rs}\n"); @@ -2968,6 +2995,7 @@ static const struct table_entry parse_table[] = { {"-iregex", T_TEST, parse_regex, BFS_REGEX_ICASE}, {"-iwholename", T_TEST, parse_path, true}, {"-j", T_FLAG, parse_jobs, 0, 0, true}, + {"-limit", T_ACTION, parse_limit}, {"-links", T_TEST, parse_links}, {"-lname", T_TEST, parse_lname, false}, {"-ls", T_ACTION, parse_ls}, @@ -3330,6 +3358,14 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { } if (parser->implicit_print) { + char **limit = parser->limit_arg; + if (limit) { + parse_argv_error(parser, parser->limit_arg, 2, + "With ${blu}%s${rs}, you must specify an action explicitly; for example, ${blu}-print${rs} ${blu}%s${rs} ${bld}%s${rs}.\n", + limit[0], limit[0], limit[1]); + return NULL; + } + struct bfs_expr *print = parse_new_expr(parser, eval_fprint, 1, &fake_print_arg); if (!print) { return NULL; diff --git a/tests/bfs/limit.out b/tests/bfs/limit.out new file mode 100644 index 0000000..ea94276 --- /dev/null +++ b/tests/bfs/limit.out @@ -0,0 +1,4 @@ +basic/a +basic/b +basic/c/d +basic/e/f diff --git a/tests/bfs/limit.sh b/tests/bfs/limit.sh new file mode 100644 index 0000000..84b605f --- /dev/null +++ b/tests/bfs/limit.sh @@ -0,0 +1 @@ +bfs_diff -s basic -type f -print -limit 4 diff --git a/tests/bfs/limit_0.sh b/tests/bfs/limit_0.sh new file mode 100644 index 0000000..3ce26de --- /dev/null +++ b/tests/bfs/limit_0.sh @@ -0,0 +1 @@ +! invoke_bfs basic -print -limit 0 diff --git a/tests/bfs/limit_implicit_print.sh b/tests/bfs/limit_implicit_print.sh new file mode 100644 index 0000000..cdb059d --- /dev/null +++ b/tests/bfs/limit_implicit_print.sh @@ -0,0 +1 @@ +! invoke_bfs basic -type f -limit 1 diff --git a/tests/bfs/limit_incomplete.sh b/tests/bfs/limit_incomplete.sh new file mode 100644 index 0000000..2d1e842 --- /dev/null +++ b/tests/bfs/limit_incomplete.sh @@ -0,0 +1 @@ +! invoke_bfs basic -print -limit diff --git a/tests/bfs/limit_one.sh b/tests/bfs/limit_one.sh new file mode 100644 index 0000000..3f8181c --- /dev/null +++ b/tests/bfs/limit_one.sh @@ -0,0 +1 @@ +! invoke_bfs basic -print -limit one -- cgit v1.2.3 From c643e1dd27e633c67589b622a06582bbb6fb5cf6 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 22 Mar 2024 14:06:04 -0400 Subject: Fix some -Wpedantic warnings --- src/config.h | 6 ++---- src/opt.c | 12 ++++++------ 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'src/opt.c') diff --git a/src/config.h b/src/config.h index 45422a2..892f24c 100644 --- a/src/config.h +++ b/src/config.h @@ -192,9 +192,7 @@ typedef long double max_align_t; /** * Silence warnings about switch/case fall-throughs. */ -#if __has_c_attribute(fallthrough) -# define fallthru [[fallthrough]] -#elif __has_attribute(fallthrough) +#if __has_attribute(fallthrough) # define fallthru __attribute__((fallthrough)) #else # define fallthru ((void)0) @@ -309,7 +307,7 @@ typedef long double max_align_t; * attr_d */ #define attr(...) \ - attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none) + attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none, ) /** * attr() helper. For exposition, pretend we support only 2 args, instead of 9. diff --git a/src/opt.c b/src/opt.c index a470d25..b74b4e1 100644 --- a/src/opt.c +++ b/src/opt.c @@ -724,7 +724,7 @@ struct visitor { visit_fn *leave; /** A visitor lookup table. */ - struct visitor_table table[]; + const struct visitor_table *table; }; /** Recursive visitor implementation. */ @@ -1331,7 +1331,7 @@ static struct bfs_expr *annotate_visit(struct bfs_opt *opt, struct bfs_expr *exp static const struct visitor annotate = { .name = "annotate", .visit = annotate_visit, - .table = { + .table = (const struct visitor_table[]) { {eval_access, annotate_access}, {eval_empty, annotate_empty}, {eval_exec, annotate_exec}, @@ -1516,7 +1516,7 @@ static struct bfs_expr *canonicalize_assoc(struct bfs_opt *opt, struct bfs_expr */ static const struct visitor canonicalize = { .name = "canonicalize", - .table = { + .table = (const struct visitor_table[]) { {eval_not, canonicalize_not}, {eval_and, canonicalize_assoc}, {eval_or, canonicalize_assoc}, @@ -1609,7 +1609,7 @@ static struct bfs_expr *reorder_andor(struct bfs_opt *opt, struct bfs_expr *expr */ static const struct visitor reorder = { .name = "reorder", - .table = { + .table = (const struct visitor_table[]) { {eval_and, reorder_andor}, {eval_or, reorder_andor}, {NULL, NULL}, @@ -1887,7 +1887,7 @@ static const struct visitor data_flow = { .enter = data_flow_enter, .visit = data_flow_visit, .leave = data_flow_leave, - .table = { + .table = (const struct visitor_table[]) { {eval_access, data_flow_access}, {eval_gid, data_flow_gid}, {eval_inum, data_flow_inum}, @@ -2103,7 +2103,7 @@ static struct bfs_expr *simplify_comma(struct bfs_opt *opt, struct bfs_expr *exp */ static const struct visitor simplify = { .name = "simplify", - .table = { + .table = (const struct visitor_table[]) { {eval_not, simplify_not}, {eval_and, simplify_and}, {eval_or, simplify_or}, -- cgit v1.2.3 From c66379749f423413913b406609dfe9311ba6e555 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 Apr 2024 14:53:56 -0400 Subject: Rename config.h to prelude.h --- src/alloc.c | 2 +- src/alloc.h | 2 +- src/bar.c | 2 +- src/bfstd.c | 2 +- src/bfstd.h | 2 +- src/bftw.c | 2 +- src/bit.h | 2 +- src/color.c | 2 +- src/color.h | 2 +- src/config.h | 377 --------------------------------------------------------- src/ctx.h | 2 +- src/diag.c | 2 +- src/diag.h | 2 +- src/dir.c | 2 +- src/dstring.c | 2 +- src/dstring.h | 2 +- src/eval.c | 2 +- src/eval.h | 2 +- src/exec.c | 2 +- src/expr.h | 2 +- src/fsade.c | 2 +- src/fsade.h | 2 +- src/ioq.c | 2 +- src/ioq.h | 2 +- src/main.c | 4 +- src/mtab.c | 2 +- src/mtab.h | 2 +- src/opt.c | 2 +- src/parse.c | 2 +- src/prelude.h | 377 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/printf.c | 2 +- src/pwcache.c | 2 +- src/sanity.h | 2 +- src/stat.c | 2 +- src/stat.h | 2 +- src/thread.c | 2 +- src/thread.h | 2 +- src/trie.c | 2 +- src/xregex.c | 2 +- src/xspawn.c | 2 +- src/xspawn.h | 2 +- src/xtime.c | 2 +- tests/alloc.c | 2 +- tests/bfstd.c | 2 +- tests/bit.c | 2 +- tests/ioq.c | 2 +- tests/main.c | 2 +- tests/tests.h | 2 +- tests/trie.c | 2 +- tests/xspawn.c | 2 +- tests/xtime.c | 2 +- tests/xtouch.c | 2 +- 52 files changed, 428 insertions(+), 428 deletions(-) delete mode 100644 src/config.h create mode 100644 src/prelude.h (limited to 'src/opt.c') diff --git a/src/alloc.c b/src/alloc.c index b65d0c5..ec8608f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include diff --git a/src/alloc.h b/src/alloc.h index ae055bc..095134a 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -8,7 +8,7 @@ #ifndef BFS_ALLOC_H #define BFS_ALLOC_H -#include "config.h" +#include "prelude.h" #include #include #include diff --git a/src/bar.c b/src/bar.c index 8ab4112..184d9a0 100644 --- a/src/bar.c +++ b/src/bar.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bar.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "dstring.h" #include #include diff --git a/src/bfstd.c b/src/bfstd.c index 2499f00..e1b4804 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" diff --git a/src/bfstd.h b/src/bfstd.h index fc22971..42f5d5b 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -8,7 +8,7 @@ #ifndef BFS_BFSTD_H #define BFS_BFSTD_H -#include "config.h" +#include "prelude.h" #include "sanity.h" #include diff --git a/src/bftw.c b/src/bftw.c index 6130c44..c4d3c17 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -18,10 +18,10 @@ * various helper functions to take fewer parameters. */ +#include "prelude.h" #include "bftw.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "dstring.h" diff --git a/src/bit.h b/src/bit.h index 69df21e..17cfbcf 100644 --- a/src/bit.h +++ b/src/bit.h @@ -8,7 +8,7 @@ #ifndef BFS_BIT_H #define BFS_BIT_H -#include "config.h" +#include "prelude.h" #include #include diff --git a/src/color.c b/src/color.c index 8c32a68..f004bf2 100644 --- a/src/color.c +++ b/src/color.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "color.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "dstring.h" diff --git a/src/color.h b/src/color.h index e3e7973..3278cd6 100644 --- a/src/color.h +++ b/src/color.h @@ -8,7 +8,7 @@ #ifndef BFS_COLOR_H #define BFS_COLOR_H -#include "config.h" +#include "prelude.h" #include "dstring.h" #include diff --git a/src/config.h b/src/config.h deleted file mode 100644 index 2eff1fc..0000000 --- a/src/config.h +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -/** - * Configuration and feature/platform detection. - */ - -#ifndef BFS_CONFIG_H -#define BFS_CONFIG_H - -// Possible __STDC_VERSION__ values - -#define C95 199409L -#define C99 199901L -#define C11 201112L -#define C17 201710L -#define C23 202311L - -#include - -#if __STDC_VERSION__ < C23 -# include -# include -# include -#endif - -// bfs packaging configuration - -#ifndef BFS_COMMAND -# define BFS_COMMAND "bfs" -#endif -#ifndef BFS_HOMEPAGE -# define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" -#endif - -// This is a symbol instead of a literal so we don't have to rebuild everything -// when the version number changes -extern const char bfs_version[]; - -// Check for system headers - -#ifdef __has_include - -#if __has_include() -# define BFS_HAS_MNTENT_H true -#endif -#if __has_include() -# define BFS_HAS_PATHS_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_ACL_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_CAPABILITY_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_EXTATTR_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_MKDEV_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_PARAM_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_SYSMACROS_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_XATTR_H true -#endif -#if __has_include() -# define BFS_HAS_THREADS_H true -#endif -#if __has_include() -# define BFS_HAS_UTIL_H true -#endif - -#else // !__has_include - -#define BFS_HAS_MNTENT_H __GLIBC__ -#define BFS_HAS_PATHS_H true -#define BFS_HAS_SYS_ACL_H true -#define BFS_HAS_SYS_CAPABILITY_H __linux__ -#define BFS_HAS_SYS_EXTATTR_H __FreeBSD__ -#define BFS_HAS_SYS_MKDEV_H false -#define BFS_HAS_SYS_PARAM_H true -#define BFS_HAS_SYS_SYSMACROS_H __GLIBC__ -#define BFS_HAS_SYS_XATTR_H __linux__ -#define BFS_HAS_THREADS_H (!__STDC_NO_THREADS__) -#define BFS_HAS_UTIL_H __NetBSD__ - -#endif // !__has_include - -#ifndef BFS_USE_MNTENT_H -# define BFS_USE_MNTENT_H BFS_HAS_MNTENT_H -#endif -#ifndef BFS_USE_PATHS_H -# define BFS_USE_PATHS_H BFS_HAS_PATHS_H -#endif -#ifndef BFS_USE_SYS_ACL_H -# define BFS_USE_SYS_ACL_H (BFS_HAS_SYS_ACL_H && !__illumos__ && (!__linux__ || BFS_USE_LIBACL)) -#endif -#ifndef BFS_USE_SYS_CAPABILITY_H -# define BFS_USE_SYS_CAPABILITY_H (BFS_HAS_SYS_CAPABILITY_H && !__FreeBSD__ && (!__linux__ || BFS_USE_LIBCAP)) -#endif -#ifndef BFS_USE_SYS_EXTATTR_H -# define BFS_USE_SYS_EXTATTR_H (BFS_HAS_SYS_EXTATTR_H && !__DragonFly__) -#endif -#ifndef BFS_USE_SYS_MKDEV_H -# define BFS_USE_SYS_MKDEV_H BFS_HAS_SYS_MKDEV_H -#endif -#ifndef BFS_USE_SYS_PARAM_H -# define BFS_USE_SYS_PARAM_H BFS_HAS_SYS_PARAM_H -#endif -#ifndef BFS_USE_SYS_SYSMACROS_H -# define BFS_USE_SYS_SYSMACROS_H BFS_HAS_SYS_SYSMACROS_H -#endif -#ifndef BFS_USE_SYS_XATTR_H -# define BFS_USE_SYS_XATTR_H BFS_HAS_SYS_XATTR_H -#endif -#ifndef BFS_USE_THREADS_H -# define BFS_USE_THREADS_H BFS_HAS_THREADS_H -#endif -#ifndef BFS_USE_UTIL_H -# define BFS_USE_UTIL_H BFS_HAS_UTIL_H -#endif - -// Stub out feature detection on old/incompatible compilers - -#ifndef __has_feature -# define __has_feature(feat) false -#endif - -#ifndef __has_c_attribute -# define __has_c_attribute(attr) false -#endif - -#ifndef __has_attribute -# define __has_attribute(attr) false -#endif - -// Platform detection - -// Get the definition of BSD if available -#if BFS_USE_SYS_PARAM_H -# include -#endif - -#ifndef __GLIBC_PREREQ -# define __GLIBC_PREREQ(maj, min) false -#endif - -#ifndef __NetBSD_Prereq__ -# define __NetBSD_Prereq__(maj, min, patch) false -#endif - -// Fundamental utilities - -/** - * Get the length of an array. - */ -#define countof(array) (sizeof(array) / sizeof(0[array])) - -/** - * False sharing/destructive interference/largest cache line size. - */ -#ifdef __GCC_DESTRUCTIVE_SIZE -# define FALSE_SHARING_SIZE __GCC_DESTRUCTIVE_SIZE -#else -# define FALSE_SHARING_SIZE 64 -#endif - -/** - * True sharing/constructive interference/smallest cache line size. - */ -#ifdef __GCC_CONSTRUCTIVE_SIZE -# define TRUE_SHARING_SIZE __GCC_CONSTRUCTIVE_SIZE -#else -# define TRUE_SHARING_SIZE 64 -#endif - -/** - * Alignment specifier that avoids false sharing. - */ -#define cache_align alignas(FALSE_SHARING_SIZE) - -#if __COSMOPOLITAN__ -typedef long double max_align_t; -#endif - -// Wrappers for attributes - -/** - * Silence warnings about switch/case fall-throughs. - */ -#if __has_attribute(fallthrough) -# define fallthru __attribute__((fallthrough)) -#else -# define fallthru ((void)0) -#endif - -/** - * Silence warnings about unused declarations. - */ -#if __has_attribute(unused) -# define attr_maybe_unused __attribute__((unused)) -#else -# define attr_maybe_unused -#endif - -/** - * Warn if a value is unused. - */ -#if __has_attribute(warn_unused_result) -# define attr_nodiscard __attribute__((warn_unused_result)) -#else -# define attr_nodiscard -#endif - -/** - * Hint to avoid inlining a function. - */ -#if __has_attribute(noinline) -# define attr_noinline __attribute__((noinline)) -#else -# define attr_noinline -#endif - -/** - * Hint that a function is unlikely to be called. - */ -#if __has_attribute(cold) -# define attr_cold attr_noinline __attribute__((cold)) -#else -# define attr_cold attr_noinline -#endif - -/** - * Adds compiler warnings for bad printf()-style function calls, if supported. - */ -#if __has_attribute(format) -# define attr_printf(fmt, args) __attribute__((format(printf, fmt, args))) -#else -# define attr_printf(fmt, args) -#endif - -/** - * Annotates allocator-like functions. - */ -#if __has_attribute(malloc) -# if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC -# define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) -# else -# define attr_malloc(...) attr_nodiscard __attribute__((malloc)) -# endif -#else -# define attr_malloc(...) attr_nodiscard -#endif - -/** - * Specifies that a function returns allocations with a given alignment. - */ -#if __has_attribute(alloc_align) -# define attr_alloc_align(param) __attribute__((alloc_align(param))) -#else -# define attr_alloc_align(param) -#endif - -/** - * Specifies that a function returns allocations with a given size. - */ -#if __has_attribute(alloc_size) -# define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) -#else -# define attr_alloc_size(...) -#endif - -/** - * Shorthand for attr_alloc_align() and attr_alloc_size(). - */ -#define attr_aligned_alloc(align, ...) \ - attr_alloc_align(align) \ - attr_alloc_size(__VA_ARGS__) - -/** - * Check if function multiversioning via GNU indirect functions (ifunc) is supported. - */ -#ifndef BFS_USE_TARGET_CLONES -# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) -# define BFS_USE_TARGET_CLONES true -# endif -#endif - -/** - * Apply the target_clones attribute, if available. - */ -#if BFS_USE_TARGET_CLONES -# define attr_target_clones(...) __attribute__((target_clones(__VA_ARGS__))) -#else -# define attr_target_clones(...) -#endif - -/** - * Shorthand for multiple attributes at once. attr(a, b(c), d) is equivalent to - * - * attr_a - * attr_b(c) - * attr_d - */ -#define attr(...) \ - attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none, ) - -/** - * attr() helper. For exposition, pretend we support only 2 args, instead of 9. - * There are a few cases: - * - * attr() - * => attr__(attr_, none, none) - * => attr_ => - * attr_none => - * attr_too_many_none() => - * - * attr(a) - * => attr__(attr_a, none, none) - * => attr_a => __attribute__((a)) - * attr_none => - * attr_too_many_none() => - * - * attr(a, b(c)) - * => attr__(attr_a, b(c), none, none) - * => attr_a => __attribute__((a)) - * attr_b(c) => __attribute__((b(c))) - * attr_too_many_none(none) => - * - * attr(a, b(c), d) - * => attr__(attr_a, b(c), d, none, none) - * => attr_a => __attribute__((a)) - * attr_b(c) => __attribute__((b(c))) - * attr_too_many_d(none, none) => error - * - * Some attribute names are the same as standard library functions, e.g. printf. - * Standard libraries are permitted to define these functions as macros, like - * - * #define printf(...) __builtin_printf(__VA_ARGS__) - * - * The token paste in - * - * #define attr(...) attr__(attr_##__VA_ARGS__, none, none) - * - * is necessary to prevent macro expansion before evaluating attr__(). - * Otherwise, we could get - * - * attr(printf(1, 2)) - * => attr__(__builtin_printf(1, 2), none, none) - * => attr____builtin_printf(1, 2) - * => error - */ -#define attr__(a1, a2, a3, a4, a5, a6, a7, a8, a9, none, ...) \ - a1 \ - attr_##a2 \ - attr_##a3 \ - attr_##a4 \ - attr_##a5 \ - attr_##a6 \ - attr_##a7 \ - attr_##a8 \ - attr_##a9 \ - attr_too_many_##none(__VA_ARGS__) - -// Ignore `attr_none` from expanding 1-9 argument attr(a1, a2, ...) -#define attr_none -// Ignore `attr_` from expanding 0-argument attr() -#define attr_ -// Only trigger an error on more than 9 arguments -#define attr_too_many_none(...) - -#endif // BFS_CONFIG_H diff --git a/src/ctx.h b/src/ctx.h index e14db21..fc3020c 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -8,9 +8,9 @@ #ifndef BFS_CTX_H #define BFS_CTX_H +#include "prelude.h" #include "alloc.h" #include "bftw.h" -#include "config.h" #include "diag.h" #include "expr.h" #include "trie.h" diff --git a/src/diag.c b/src/diag.c index cb27b92..deb6f26 100644 --- a/src/diag.c +++ b/src/diag.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "diag.h" #include "alloc.h" #include "bfstd.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "dstring.h" #include "expr.h" diff --git a/src/diag.h b/src/diag.h index 4054c48..2b13609 100644 --- a/src/diag.h +++ b/src/diag.h @@ -8,7 +8,7 @@ #ifndef BFS_DIAG_H #define BFS_DIAG_H -#include "config.h" +#include "prelude.h" #include /** diff --git a/src/dir.c b/src/dir.c index 98518f2..53c9be3 100644 --- a/src/dir.c +++ b/src/dir.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "dir.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "trie.h" diff --git a/src/dstring.c b/src/dstring.c index 10b0fad..913dda8 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "dstring.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include #include diff --git a/src/dstring.h b/src/dstring.h index 6006199..9ea7eb9 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -8,8 +8,8 @@ #ifndef BFS_DSTRING_H #define BFS_DSTRING_H +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include #include diff --git a/src/eval.c b/src/eval.c index d0112c2..b103912 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5,12 +5,12 @@ * Implementation of all the primary expressions. */ +#include "prelude.h" #include "eval.h" #include "bar.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/eval.h b/src/eval.h index ae43628..4dd7996 100644 --- a/src/eval.h +++ b/src/eval.h @@ -9,7 +9,7 @@ #ifndef BFS_EVAL_H #define BFS_EVAL_H -#include "config.h" +#include "prelude.h" struct bfs_ctx; struct bfs_expr; diff --git a/src/exec.c b/src/exec.c index 60bfd28..e782d49 100644 --- a/src/exec.c +++ b/src/exec.c @@ -1,12 +1,12 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "exec.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dstring.h" diff --git a/src/expr.h b/src/expr.h index 75cb5fd..7bcace7 100644 --- a/src/expr.h +++ b/src/expr.h @@ -8,8 +8,8 @@ #ifndef BFS_EXPR_H #define BFS_EXPR_H +#include "prelude.h" #include "color.h" -#include "config.h" #include "eval.h" #include "stat.h" #include diff --git a/src/fsade.c b/src/fsade.c index 0810c7f..34a4d57 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "fsade.h" #include "atomic.h" #include "bfstd.h" #include "bftw.h" -#include "config.h" #include "dir.h" #include "dstring.h" #include "sanity.h" diff --git a/src/fsade.h b/src/fsade.h index 1f1dbfc..6300852 100644 --- a/src/fsade.h +++ b/src/fsade.h @@ -9,7 +9,7 @@ #ifndef BFS_FSADE_H #define BFS_FSADE_H -#include "config.h" +#include "prelude.h" #define BFS_CAN_CHECK_ACL BFS_USE_SYS_ACL_H diff --git a/src/ioq.c b/src/ioq.c index b936681..189bdac 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -118,12 +118,12 @@ * [1]: https://arxiv.org/abs/2201.02179 */ +#include "prelude.h" #include "ioq.h" #include "alloc.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "stat.h" diff --git a/src/ioq.h b/src/ioq.h index 818eea6..d8e1573 100644 --- a/src/ioq.h +++ b/src/ioq.h @@ -8,7 +8,7 @@ #ifndef BFS_IOQ_H #define BFS_IOQ_H -#include "config.h" +#include "prelude.h" #include "dir.h" #include "stat.h" #include diff --git a/src/main.c b/src/main.c index e120f03..9d8b206 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ * - bit.h (bit manipulation) * - bfstd.[ch] (standard library wrappers/polyfills) * - color.[ch] (for pretty terminal colors) - * - config.h (configuration and feature/platform detection) + * - prelude.h (configuration and feature/platform detection) * - diag.[ch] (formats diagnostic messages) * - dir.[ch] (a directory API facade) * - dstring.[ch] (a dynamic string library) @@ -45,8 +45,8 @@ * - xtime.[ch] (date/time handling utilities) */ +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "eval.h" diff --git a/src/mtab.c b/src/mtab.c index 86ae151..7905d14 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "mtab.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "stat.h" #include "trie.h" #include diff --git a/src/mtab.h b/src/mtab.h index d99d78f..67290c2 100644 --- a/src/mtab.h +++ b/src/mtab.h @@ -8,7 +8,7 @@ #ifndef BFS_MTAB_H #define BFS_MTAB_H -#include "config.h" +#include "prelude.h" struct bfs_stat; diff --git a/src/opt.c b/src/opt.c index b74b4e1..ffc795b 100644 --- a/src/opt.c +++ b/src/opt.c @@ -25,11 +25,11 @@ * effects are reachable at all, skipping the traversal if not. */ +#include "prelude.h" #include "opt.h" #include "bftw.h" #include "bit.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/parse.c b/src/parse.c index a3e32fe..c2ae58f 100644 --- a/src/parse.c +++ b/src/parse.c @@ -8,12 +8,12 @@ * flags like always-true options, and skipping over paths wherever they appear. */ +#include "prelude.h" #include "parse.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/prelude.h b/src/prelude.h new file mode 100644 index 0000000..c3a0752 --- /dev/null +++ b/src/prelude.h @@ -0,0 +1,377 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Configuration and feature/platform detection. + */ + +#ifndef BFS_PRELUDE_H +#define BFS_PRELUDE_H + +// Possible __STDC_VERSION__ values + +#define C95 199409L +#define C99 199901L +#define C11 201112L +#define C17 201710L +#define C23 202311L + +#include + +#if __STDC_VERSION__ < C23 +# include +# include +# include +#endif + +// bfs packaging configuration + +#ifndef BFS_COMMAND +# define BFS_COMMAND "bfs" +#endif +#ifndef BFS_HOMEPAGE +# define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" +#endif + +// This is a symbol instead of a literal so we don't have to rebuild everything +// when the version number changes +extern const char bfs_version[]; + +// Check for system headers + +#ifdef __has_include + +#if __has_include() +# define BFS_HAS_MNTENT_H true +#endif +#if __has_include() +# define BFS_HAS_PATHS_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_ACL_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_CAPABILITY_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_EXTATTR_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_MKDEV_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_PARAM_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_SYSMACROS_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_XATTR_H true +#endif +#if __has_include() +# define BFS_HAS_THREADS_H true +#endif +#if __has_include() +# define BFS_HAS_UTIL_H true +#endif + +#else // !__has_include + +#define BFS_HAS_MNTENT_H __GLIBC__ +#define BFS_HAS_PATHS_H true +#define BFS_HAS_SYS_ACL_H true +#define BFS_HAS_SYS_CAPABILITY_H __linux__ +#define BFS_HAS_SYS_EXTATTR_H __FreeBSD__ +#define BFS_HAS_SYS_MKDEV_H false +#define BFS_HAS_SYS_PARAM_H true +#define BFS_HAS_SYS_SYSMACROS_H __GLIBC__ +#define BFS_HAS_SYS_XATTR_H __linux__ +#define BFS_HAS_THREADS_H (!__STDC_NO_THREADS__) +#define BFS_HAS_UTIL_H __NetBSD__ + +#endif // !__has_include + +#ifndef BFS_USE_MNTENT_H +# define BFS_USE_MNTENT_H BFS_HAS_MNTENT_H +#endif +#ifndef BFS_USE_PATHS_H +# define BFS_USE_PATHS_H BFS_HAS_PATHS_H +#endif +#ifndef BFS_USE_SYS_ACL_H +# define BFS_USE_SYS_ACL_H (BFS_HAS_SYS_ACL_H && !__illumos__ && (!__linux__ || BFS_USE_LIBACL)) +#endif +#ifndef BFS_USE_SYS_CAPABILITY_H +# define BFS_USE_SYS_CAPABILITY_H (BFS_HAS_SYS_CAPABILITY_H && !__FreeBSD__ && (!__linux__ || BFS_USE_LIBCAP)) +#endif +#ifndef BFS_USE_SYS_EXTATTR_H +# define BFS_USE_SYS_EXTATTR_H (BFS_HAS_SYS_EXTATTR_H && !__DragonFly__) +#endif +#ifndef BFS_USE_SYS_MKDEV_H +# define BFS_USE_SYS_MKDEV_H BFS_HAS_SYS_MKDEV_H +#endif +#ifndef BFS_USE_SYS_PARAM_H +# define BFS_USE_SYS_PARAM_H BFS_HAS_SYS_PARAM_H +#endif +#ifndef BFS_USE_SYS_SYSMACROS_H +# define BFS_USE_SYS_SYSMACROS_H BFS_HAS_SYS_SYSMACROS_H +#endif +#ifndef BFS_USE_SYS_XATTR_H +# define BFS_USE_SYS_XATTR_H BFS_HAS_SYS_XATTR_H +#endif +#ifndef BFS_USE_THREADS_H +# define BFS_USE_THREADS_H BFS_HAS_THREADS_H +#endif +#ifndef BFS_USE_UTIL_H +# define BFS_USE_UTIL_H BFS_HAS_UTIL_H +#endif + +// Stub out feature detection on old/incompatible compilers + +#ifndef __has_feature +# define __has_feature(feat) false +#endif + +#ifndef __has_c_attribute +# define __has_c_attribute(attr) false +#endif + +#ifndef __has_attribute +# define __has_attribute(attr) false +#endif + +// Platform detection + +// Get the definition of BSD if available +#if BFS_USE_SYS_PARAM_H +# include +#endif + +#ifndef __GLIBC_PREREQ +# define __GLIBC_PREREQ(maj, min) false +#endif + +#ifndef __NetBSD_Prereq__ +# define __NetBSD_Prereq__(maj, min, patch) false +#endif + +// Fundamental utilities + +/** + * Get the length of an array. + */ +#define countof(array) (sizeof(array) / sizeof(0[array])) + +/** + * False sharing/destructive interference/largest cache line size. + */ +#ifdef __GCC_DESTRUCTIVE_SIZE +# define FALSE_SHARING_SIZE __GCC_DESTRUCTIVE_SIZE +#else +# define FALSE_SHARING_SIZE 64 +#endif + +/** + * True sharing/constructive interference/smallest cache line size. + */ +#ifdef __GCC_CONSTRUCTIVE_SIZE +# define TRUE_SHARING_SIZE __GCC_CONSTRUCTIVE_SIZE +#else +# define TRUE_SHARING_SIZE 64 +#endif + +/** + * Alignment specifier that avoids false sharing. + */ +#define cache_align alignas(FALSE_SHARING_SIZE) + +#if __COSMOPOLITAN__ +typedef long double max_align_t; +#endif + +// Wrappers for attributes + +/** + * Silence warnings about switch/case fall-throughs. + */ +#if __has_attribute(fallthrough) +# define fallthru __attribute__((fallthrough)) +#else +# define fallthru ((void)0) +#endif + +/** + * Silence warnings about unused declarations. + */ +#if __has_attribute(unused) +# define attr_maybe_unused __attribute__((unused)) +#else +# define attr_maybe_unused +#endif + +/** + * Warn if a value is unused. + */ +#if __has_attribute(warn_unused_result) +# define attr_nodiscard __attribute__((warn_unused_result)) +#else +# define attr_nodiscard +#endif + +/** + * Hint to avoid inlining a function. + */ +#if __has_attribute(noinline) +# define attr_noinline __attribute__((noinline)) +#else +# define attr_noinline +#endif + +/** + * Hint that a function is unlikely to be called. + */ +#if __has_attribute(cold) +# define attr_cold attr_noinline __attribute__((cold)) +#else +# define attr_cold attr_noinline +#endif + +/** + * Adds compiler warnings for bad printf()-style function calls, if supported. + */ +#if __has_attribute(format) +# define attr_printf(fmt, args) __attribute__((format(printf, fmt, args))) +#else +# define attr_printf(fmt, args) +#endif + +/** + * Annotates allocator-like functions. + */ +#if __has_attribute(malloc) +# if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC +# define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) +# else +# define attr_malloc(...) attr_nodiscard __attribute__((malloc)) +# endif +#else +# define attr_malloc(...) attr_nodiscard +#endif + +/** + * Specifies that a function returns allocations with a given alignment. + */ +#if __has_attribute(alloc_align) +# define attr_alloc_align(param) __attribute__((alloc_align(param))) +#else +# define attr_alloc_align(param) +#endif + +/** + * Specifies that a function returns allocations with a given size. + */ +#if __has_attribute(alloc_size) +# define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) +#else +# define attr_alloc_size(...) +#endif + +/** + * Shorthand for attr_alloc_align() and attr_alloc_size(). + */ +#define attr_aligned_alloc(align, ...) \ + attr_alloc_align(align) \ + attr_alloc_size(__VA_ARGS__) + +/** + * Check if function multiversioning via GNU indirect functions (ifunc) is supported. + */ +#ifndef BFS_USE_TARGET_CLONES +# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) +# define BFS_USE_TARGET_CLONES true +# endif +#endif + +/** + * Apply the target_clones attribute, if available. + */ +#if BFS_USE_TARGET_CLONES +# define attr_target_clones(...) __attribute__((target_clones(__VA_ARGS__))) +#else +# define attr_target_clones(...) +#endif + +/** + * Shorthand for multiple attributes at once. attr(a, b(c), d) is equivalent to + * + * attr_a + * attr_b(c) + * attr_d + */ +#define attr(...) \ + attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none, ) + +/** + * attr() helper. For exposition, pretend we support only 2 args, instead of 9. + * There are a few cases: + * + * attr() + * => attr__(attr_, none, none) + * => attr_ => + * attr_none => + * attr_too_many_none() => + * + * attr(a) + * => attr__(attr_a, none, none) + * => attr_a => __attribute__((a)) + * attr_none => + * attr_too_many_none() => + * + * attr(a, b(c)) + * => attr__(attr_a, b(c), none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_none(none) => + * + * attr(a, b(c), d) + * => attr__(attr_a, b(c), d, none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_d(none, none) => error + * + * Some attribute names are the same as standard library functions, e.g. printf. + * Standard libraries are permitted to define these functions as macros, like + * + * #define printf(...) __builtin_printf(__VA_ARGS__) + * + * The token paste in + * + * #define attr(...) attr__(attr_##__VA_ARGS__, none, none) + * + * is necessary to prevent macro expansion before evaluating attr__(). + * Otherwise, we could get + * + * attr(printf(1, 2)) + * => attr__(__builtin_printf(1, 2), none, none) + * => attr____builtin_printf(1, 2) + * => error + */ +#define attr__(a1, a2, a3, a4, a5, a6, a7, a8, a9, none, ...) \ + a1 \ + attr_##a2 \ + attr_##a3 \ + attr_##a4 \ + attr_##a5 \ + attr_##a6 \ + attr_##a7 \ + attr_##a8 \ + attr_##a9 \ + attr_too_many_##none(__VA_ARGS__) + +// Ignore `attr_none` from expanding 1-9 argument attr(a1, a2, ...) +#define attr_none +// Ignore `attr_` from expanding 0-argument attr() +#define attr_ +// Only trigger an error on more than 9 arguments +#define attr_too_many_none(...) + +#endif // BFS_PRELUDE_H diff --git a/src/printf.c b/src/printf.c index 3b8269e..4df399b 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1,12 +1,12 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "printf.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/pwcache.c b/src/pwcache.c index 79437d8..af8c237 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "pwcache.h" #include "alloc.h" -#include "config.h" #include "trie.h" #include #include diff --git a/src/sanity.h b/src/sanity.h index 423e6ff..e168b8f 100644 --- a/src/sanity.h +++ b/src/sanity.h @@ -8,7 +8,7 @@ #ifndef BFS_SANITY_H #define BFS_SANITY_H -#include "config.h" +#include "prelude.h" #include #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) diff --git a/src/stat.c b/src/stat.c index 2f2743b..eca5bab 100644 --- a/src/stat.c +++ b/src/stat.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "stat.h" #include "atomic.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include diff --git a/src/stat.h b/src/stat.h index 856a2ca..1fdd263 100644 --- a/src/stat.h +++ b/src/stat.h @@ -12,7 +12,7 @@ #ifndef BFS_STAT_H #define BFS_STAT_H -#include "config.h" +#include "prelude.h" #include #include #include diff --git a/src/thread.c b/src/thread.c index 200d8c3..3793896 100644 --- a/src/thread.c +++ b/src/thread.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "thread.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/src/thread.h b/src/thread.h index 8174fe4..db11bd8 100644 --- a/src/thread.h +++ b/src/thread.h @@ -8,7 +8,7 @@ #ifndef BFS_THREAD_H #define BFS_THREAD_H -#include "config.h" +#include "prelude.h" #include #if __STDC_VERSION__ < C23 && !defined(thread_local) diff --git a/src/trie.c b/src/trie.c index f275064..808953e 100644 --- a/src/trie.c +++ b/src/trie.c @@ -81,10 +81,10 @@ * and insert intermediate singleton "jump" nodes when necessary. */ +#include "prelude.h" #include "trie.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "list.h" #include diff --git a/src/xregex.c b/src/xregex.c index 3df27f0..c2711bc 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xregex.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" diff --git a/src/xspawn.c b/src/xspawn.c index 347625d..113d7ec 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xspawn.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "list.h" #include #include diff --git a/src/xspawn.h b/src/xspawn.h index a20cbd0..6a8f54a 100644 --- a/src/xspawn.h +++ b/src/xspawn.h @@ -8,7 +8,7 @@ #ifndef BFS_XSPAWN_H #define BFS_XSPAWN_H -#include "config.h" +#include "prelude.h" #include #include #include diff --git a/src/xtime.c b/src/xtime.c index bcf6dd3..91ed915 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xtime.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/alloc.c b/tests/alloc.c index 54b84ba..6c0defd 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "alloc.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/bfstd.c b/tests/bfstd.c index 5e408ca..07b68b0 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/bit.c b/tests/bit.c index b444e50..674d1b2 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "bit.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/ioq.c b/tests/ioq.c index a69f2bf..ef5ee3b 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "ioq.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "dir.h" #include diff --git a/tests/main.c b/tests/main.c index 281c417..429772b 100644 --- a/tests/main.c +++ b/tests/main.c @@ -5,10 +5,10 @@ * Entry point for unit tests. */ +#include "prelude.h" #include "tests.h" #include "bfstd.h" #include "color.h" -#include "config.h" #include #include #include diff --git a/tests/tests.h b/tests/tests.h index d61ffd7..9078938 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -8,7 +8,7 @@ #ifndef BFS_TESTS_H #define BFS_TESTS_H -#include "config.h" +#include "prelude.h" #include "diag.h" /** Unit test function type. */ diff --git a/tests/trie.c b/tests/trie.c index 2a6eb48..4667322 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "trie.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/xspawn.c b/tests/xspawn.c index fd8362e..7362aa5 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "dstring.h" #include "xspawn.h" #include diff --git a/tests/xtime.c b/tests/xtime.c index fd7aa0f..a7c63d2 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "xtime.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/xtouch.c b/tests/xtouch.c index b1daec7..82d749d 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -1,8 +1,8 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include "sanity.h" #include "xtime.h" #include -- cgit v1.2.3 From 4a802da8d598c6fe3317e186b64a0b020cc96f01 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 24 Apr 2024 11:36:51 -0400 Subject: opt: Add missing NULL check in visit_shallow() visit_shallow() should propagate NULL, but look_up_visitor() dereferences expr to know which visitor to return. --- src/opt.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/opt.c') diff --git a/src/opt.c b/src/opt.c index ffc795b..883d598 100644 --- a/src/opt.c +++ b/src/opt.c @@ -947,8 +947,12 @@ static struct bfs_expr *visit_shallow(struct bfs_opt *opt, struct bfs_expr *expr expr = general(opt, expr, visitor); } + if (!expr) { + return NULL; + } + visit_fn *specific = look_up_visitor(expr, visitor->table); - if (expr && specific) { + if (specific) { expr = specific(opt, expr, visitor); } -- cgit v1.2.3