From 4846e61080c575a67ebe9805a2332d8dd2b90555 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 26 Mar 2022 19:42:55 -0400 Subject: diag: New functions for highlighting command line arguments --- ctx.h | 3 ++ diag.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- diag.h | 24 +++++++++++- opt.c | 78 ++++++--------------------------------- parse.c | 26 ++++++++++--- 5 files changed, 185 insertions(+), 75 deletions(-) diff --git a/ctx.h b/ctx.h index 5d0daba..3ad7f85 100644 --- a/ctx.h +++ b/ctx.h @@ -57,8 +57,11 @@ const char *debug_flag_name(enum debug_flags flag); * The execution context for bfs. */ struct bfs_ctx { + /** The number of command line arguments. */ + size_t argc; /** The unparsed command line arguments. */ char **argv; + /** The root paths. */ const char **paths; /** The main command line expression. */ diff --git a/diag.c b/diag.c index 4b54c0a..e85d7bf 100644 --- a/diag.c +++ b/diag.c @@ -1,6 +1,6 @@ /**************************************************************************** * bfs * - * Copyright (C) 2019 Tavian Barnes * + * 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. * @@ -17,7 +17,9 @@ #include "diag.h" #include "ctx.h" #include "color.h" +#include "expr.h" #include "util.h" +#include #include #include @@ -102,3 +104,128 @@ bool bfs_debug_prefix(const struct bfs_ctx *ctx, enum debug_flags flag) { return false; } } + +/** Recursive part of highlight_expr(). */ +static bool highlight_expr_recursive(const struct bfs_ctx *ctx, const struct bfs_expr *expr, bool *args) { + if (!expr) { + return false; + } + + bool ret = false; + + if (!expr->synthetic) { + size_t i = expr->argv - ctx->argv; + for (size_t j = 0; j < expr->argc; ++j) { + assert(i + j < ctx->argc); + args[i + j] = true; + ret = true; + } + } + + if (bfs_expr_has_children(expr)) { + ret |= highlight_expr_recursive(ctx, expr->lhs, args); + ret |= highlight_expr_recursive(ctx, expr->rhs, args); + } + + return ret; +} + +/** Highlight an expression in the command line. */ +static bool highlight_expr(const struct bfs_ctx *ctx, const struct bfs_expr *expr, bool *args) { + for (size_t i = 0; i < ctx->argc; ++i) { + args[i] = false; + } + + return highlight_expr_recursive(ctx, expr, args); +} + +/** Print a hilighted portion of the command line. */ +static void bfs_argv_diag(const struct bfs_ctx *ctx, const bool *args, bool warning) { + if (warning) { + bfs_warning_prefix(ctx); + } else { + bfs_error_prefix(ctx); + } + + for (size_t i = 0; i < ctx->argc; ++i) { + if (i > 0) { + cfprintf(ctx->cerr, " "); + } + + if (args[i]) { + cfprintf(ctx->cerr, "${bld}%s${rs}", ctx->argv[i]); + } else { + cfprintf(ctx->cerr, "%s", ctx->argv[i]); + } + } + + cfprintf(ctx->cerr, "\n"); + + if (warning) { + bfs_warning_prefix(ctx); + } else { + bfs_error_prefix(ctx); + } + + for (size_t i = 0; i < ctx->argc; ++i) { + if (i > 0) { + if (args[i - 1] && args[i]) { + cfprintf(ctx->cerr, "~"); + } else { + cfprintf(ctx->cerr, " "); + } + } + + if (args[i] && (i == 0 || !args[i - 1])) { + if (warning) { + cfprintf(ctx->cerr, "${wr}"); + } else { + cfprintf(ctx->cerr, "${er}"); + } + } + + size_t len = xstrwidth(ctx->argv[i]); + for (size_t j = 0; j < len; ++j) { + if (args[i]) { + cfprintf(ctx->cerr, "~"); + } else { + cfprintf(ctx->cerr, " "); + } + } + + if (args[i] && (i + 1 >= ctx->argc || !args[i + 1])) { + cfprintf(ctx->cerr, "${rs}"); + } + } + + cfprintf(ctx->cerr, "\n"); +} + +void bfs_argv_error(const struct bfs_ctx *ctx, const bool *args) { + bfs_argv_diag(ctx, args, false); +} + +void bfs_expr_error(const struct bfs_ctx *ctx, const struct bfs_expr *expr) { + bool args[ctx->argc]; + if (highlight_expr(ctx, expr, args)) { + bfs_argv_error(ctx, args); + } +} + +bool bfs_argv_warning(const struct bfs_ctx *ctx, const bool *args) { + if (!ctx->warn) { + return false; + } + + bfs_argv_diag(ctx, args, true); + return true; +} + +bool bfs_expr_warning(const struct bfs_ctx *ctx, const struct bfs_expr *expr) { + bool args[ctx->argc]; + if (highlight_expr(ctx, expr, args)) { + return bfs_argv_warning(ctx, args); + } + + return false; +} diff --git a/diag.h b/diag.h index aa5e1c7..39129cc 100644 --- a/diag.h +++ b/diag.h @@ -1,6 +1,6 @@ /**************************************************************************** * bfs * - * Copyright (C) 2019 Tavian Barnes * + * 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. * @@ -26,6 +26,8 @@ #include #include +struct bfs_expr; + /** * Like perror(), but decorated like bfs_error(). */ @@ -83,4 +85,24 @@ bool bfs_warning_prefix(const struct bfs_ctx *ctx); */ bool bfs_debug_prefix(const struct bfs_ctx *ctx, enum debug_flags flag); +/** + * Highlight parts of the command line in an error message. + */ +void bfs_argv_error(const struct bfs_ctx *ctx, const bool *args); + +/** + * Highlight parts of an expression in an error message. + */ +void bfs_expr_error(const struct bfs_ctx *ctx, const struct bfs_expr *expr); + +/** + * Highlight parts of the command line in a warning message. + */ +bool bfs_argv_warning(const struct bfs_ctx *ctx, const bool *args); + +/** + * Highlight parts of an expression in a warning message. + */ +bool bfs_expr_warning(const struct bfs_ctx *ctx, const struct bfs_expr *expr); + #endif // BFS_DIAG_H diff --git a/opt.c b/opt.c index f46436a..e82e3c1 100644 --- a/opt.c +++ b/opt.c @@ -334,76 +334,15 @@ static bool opt_debug(const struct opt_state *state, int level, const char *form } } -/** Warn about an expression. */ -static void opt_vwarning(const struct opt_state *state, const struct bfs_expr *expr, const char *format, va_list args) { - if (bfs_expr_has_children(expr)) { - if (expr->lhs) { - va_list copy; - va_copy(copy, args); - opt_vwarning(state, expr->lhs, format, copy); - va_end(copy); - } - - if (expr->rhs) { - opt_vwarning(state, expr->rhs, format, args); - } - - return; - } - - if (expr->synthetic) { - return; - } - - const struct bfs_ctx *ctx = state->ctx; - if (!bfs_warning_prefix(ctx)) { - return; - } - - size_t offset = 0; - size_t start = SIZE_MAX, end = SIZE_MAX; - for (size_t i = 0; ctx->argv[i]; ++i) { - if (i > 0) { - cfprintf(ctx->cerr, " "); - offset += 1; - } - - if (expr->argv == &ctx->argv[i]) { - start = offset; - } - - cfprintf(ctx->cerr, "%s", ctx->argv[i]); - offset += strlen(ctx->argv[i]); - - if (&expr->argv[expr->argc - 1] == &ctx->argv[i]) { - end = offset; - } - } - cfprintf(ctx->cerr, "\n"); - - assert(start <= offset); - assert(end <= offset); - - bfs_warning_prefix(ctx); - for (size_t i = 0; i < end; ++i) { - if (i < start) { - fputc(' ', stderr); - } else { - fputc('~', stderr); - } - } - fputc('\n', stderr); - - bfs_vwarning(ctx, format, args); -} - /** Warn about an expression. */ BFS_FORMATTER(3, 4) static void opt_warning(const struct opt_state *state, const struct bfs_expr *expr, const char *format, ...) { - va_list args; - va_start(args, format); - opt_vwarning(state, expr, format, args); - va_end(args); + if (bfs_expr_warning(state->ctx, expr)) { + va_list args; + va_start(args, format); + bfs_warning(state->ctx, format, args); + va_end(args); + } } /** Extract a child expression, freeing the outer expression. */ @@ -428,6 +367,10 @@ 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; @@ -462,6 +405,7 @@ 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); diff --git a/parse.c b/parse.c index 87d73e3..25a4ed8 100644 --- a/parse.c +++ b/parse.c @@ -173,6 +173,10 @@ static struct bfs_expr *new_binary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *l expr->rhs = rhs; assert(bfs_expr_has_children(expr)); + if (argv == &fake_and_arg || argv == &fake_or_arg) { + expr->synthetic = true; + } + expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; if (lhs->ephemeral_fds > rhs->ephemeral_fds) { expr->ephemeral_fds = lhs->ephemeral_fds; @@ -716,6 +720,19 @@ static struct bfs_expr *parse_unary_action(struct parser_state *state, bfs_eval_ return parse_action(state, eval_fn, 2); } +/** + * Add an expression to the exclusions. + */ +static int parse_exclude(struct parser_state *state, struct bfs_expr *expr) { + struct bfs_ctx *ctx = state->ctx; + ctx->exclude = new_binary_expr(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. */ @@ -1865,9 +1882,7 @@ static struct bfs_expr *parse_nohidden(struct parser_state *state, int arg1, int hidden->pure = true; hidden->synthetic = true; - struct bfs_ctx *ctx = state->ctx; - ctx->exclude = new_binary_expr(eval_or, ctx->exclude, hidden, &fake_or_arg); - if (!ctx->exclude) { + if (parse_exclude(state, hidden) != 0) { return NULL; } @@ -3321,9 +3336,7 @@ static struct bfs_expr *parse_factor(struct parser_state *state) { state->excluding = false; - struct bfs_ctx *ctx = state->ctx; - ctx->exclude = new_binary_expr(eval_or, ctx->exclude, factor, &fake_or_arg); - if (!ctx->exclude) { + if (parse_exclude(state, factor) != 0) { return NULL; } @@ -3688,6 +3701,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { argv = default_argv; } + ctx->argc = argc; ctx->argv = malloc((argc + 1)*sizeof(*ctx->argv)); if (!ctx->argv) { perror("malloc()"); -- cgit v1.2.3