summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2022-03-26 19:42:55 -0400
committerTavian Barnes <tavianator@tavianator.com>2022-03-27 12:53:15 -0400
commit4846e61080c575a67ebe9805a2332d8dd2b90555 (patch)
treec76a7408dd534f5dd12a087687c739bd9f2a50bd
parentc2139e2e03cbcee9a1ae03956b1f06d3a9c269b0 (diff)
downloadbfs-4846e61080c575a67ebe9805a2332d8dd2b90555.tar.xz
diag: New functions for highlighting command line arguments
-rw-r--r--ctx.h3
-rw-r--r--diag.c129
-rw-r--r--diag.h24
-rw-r--r--opt.c78
-rw-r--r--parse.c26
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 <tavianator@tavianator.com> *
+ * Copyright (C) 2019-2022 Tavian Barnes <tavianator@tavianator.com> *
* *
* Permission to use, copy, modify, and/or distribute this software for any *
* purpose with or without fee is hereby granted. *
@@ -17,7 +17,9 @@
#include "diag.h"
#include "ctx.h"
#include "color.h"
+#include "expr.h"
#include "util.h"
+#include <assert.h>
#include <errno.h>
#include <stdarg.h>
@@ -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 <tavianator@tavianator.com> *
+ * Copyright (C) 2019-2022 Tavian Barnes <tavianator@tavianator.com> *
* *
* Permission to use, copy, modify, and/or distribute this software for any *
* purpose with or without fee is hereby granted. *
@@ -26,6 +26,8 @@
#include <stdarg.h>
#include <stdbool.h>
+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
@@ -335,75 +335,14 @@ 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;
@@ -717,6 +721,19 @@ static struct bfs_expr *parse_unary_action(struct parser_state *state, bfs_eval_
}
/**
+ * 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.
*/
static struct bfs_expr *parse_test_icmp(struct parser_state *state, bfs_eval_fn *eval_fn) {
@@ -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()");