summaryrefslogtreecommitdiffstats
path: root/src/diag.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/diag.c')
-rw-r--r--src/diag.c296
1 files changed, 296 insertions, 0 deletions
diff --git a/src/diag.c b/src/diag.c
new file mode 100644
index 0000000..deb6f26
--- /dev/null
+++ b/src/diag.c
@@ -0,0 +1,296 @@
+// Copyright © Tavian Barnes <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include "prelude.h"
+#include "diag.h"
+#include "alloc.h"
+#include "bfstd.h"
+#include "color.h"
+#include "ctx.h"
+#include "dstring.h"
+#include "expr.h"
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/** bfs_diagf() implementation. */
+attr(printf(2, 0))
+static void bfs_vdiagf(const struct bfs_loc *loc, const char *format, va_list args) {
+ fprintf(stderr, "%s: %s@%s:%d: ", xgetprogname(), loc->func, loc->file, loc->line);
+ vfprintf(stderr, format, args);
+ fprintf(stderr, "\n");
+}
+
+void bfs_diagf(const struct bfs_loc *loc, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ bfs_vdiagf(loc, format, args);
+ va_end(args);
+}
+
+noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ bfs_vdiagf(loc, format, args);
+ va_end(args);
+
+ abort();
+}
+
+const char *debug_flag_name(enum debug_flags flag) {
+ switch (flag) {
+ case DEBUG_COST:
+ return "cost";
+ case DEBUG_EXEC:
+ return "exec";
+ case DEBUG_OPT:
+ return "opt";
+ case DEBUG_RATES:
+ return "rates";
+ case DEBUG_SEARCH:
+ return "search";
+ case DEBUG_STAT:
+ return "stat";
+ case DEBUG_TREE:
+ return "tree";
+
+ case DEBUG_ALL:
+ break;
+ }
+
+ bfs_bug("Unrecognized debug flag");
+ return "???";
+}
+
+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, ...) {
+ 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, ...) {
+ va_list args;
+ va_start(args, format);
+ bool ret = bfs_vwarning(ctx, format, args);
+ va_end(args);
+ return ret;
+}
+
+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);
+ va_end(args);
+ return ret;
+}
+
+void bfs_verror(const struct bfs_ctx *ctx, const char *format, va_list args) {
+ int error = errno;
+
+ bfs_error_prefix(ctx);
+
+ errno = error;
+ cvfprintf(ctx->cerr, format, args);
+}
+
+bool bfs_vwarning(const struct bfs_ctx *ctx, const char *format, va_list args) {
+ int error = errno;
+
+ if (bfs_warning_prefix(ctx)) {
+ errno = error;
+ cvfprintf(ctx->cerr, format, args);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool bfs_vdebug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, va_list args) {
+ int error = errno;
+
+ if (bfs_debug_prefix(ctx, flag)) {
+ errno = error;
+ cvfprintf(ctx->cerr, format, args);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/** Get the command name without any leading directories. */
+static const char *bfs_cmd(const struct bfs_ctx *ctx) {
+ return ctx->argv[0] + xbaseoff(ctx->argv[0]);
+}
+
+void bfs_error_prefix(const struct bfs_ctx *ctx) {
+ cfprintf(ctx->cerr, "${bld}%s:${rs} ${err}error:${rs} ", bfs_cmd(ctx));
+}
+
+bool bfs_warning_prefix(const struct bfs_ctx *ctx) {
+ if (ctx->warn) {
+ cfprintf(ctx->cerr, "${bld}%s:${rs} ${wrn}warning:${rs} ", bfs_cmd(ctx));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool bfs_debug_prefix(const struct bfs_ctx *ctx, enum debug_flags flag) {
+ if (ctx->debug & flag) {
+ cfprintf(ctx->cerr, "${bld}%s:${rs} ${cyn}-D %s${rs}: ", bfs_cmd(ctx), debug_flag_name(flag));
+ return true;
+ } else {
+ 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;
+
+ for (size_t i = 0; i < ctx->argc; ++i) {
+ if (&ctx->argv[i] == expr->argv) {
+ for (size_t j = 0; j < expr->argc; ++j) {
+ bfs_assert(i + j < ctx->argc);
+ args[i + j] = true;
+ ret = true;
+ }
+ break;
+ }
+ }
+
+ for (struct bfs_expr *child = bfs_expr_children(expr); child; child = child->next) {
+ ret |= highlight_expr_recursive(ctx, child, 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 highlighted 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);
+ }
+
+ dchar **argv = ZALLOC_ARRAY(dchar *, ctx->argc);
+ if (!argv) {
+ return;
+ }
+
+ for (size_t i = 0; i < ctx->argc; ++i) {
+ if (dstrescat(&argv[i], ctx->argv[i], WESC_SHELL | WESC_TTY) != 0) {
+ goto done;
+ }
+ }
+
+ size_t max_argc = 0;
+ for (size_t i = 0; i < ctx->argc; ++i) {
+ if (i > 0) {
+ cfprintf(ctx->cerr, " ");
+ }
+
+ if (args[i]) {
+ max_argc = i + 1;
+ cfprintf(ctx->cerr, "${bld}%s${rs}", argv[i]);
+ } else {
+ cfprintf(ctx->cerr, "%s", argv[i]);
+ }
+ }
+
+ cfprintf(ctx->cerr, "\n");
+
+ if (warning) {
+ bfs_warning_prefix(ctx);
+ } else {
+ bfs_error_prefix(ctx);
+ }
+
+ for (size_t i = 0; i < max_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, "${wrn}");
+ } else {
+ cfprintf(ctx->cerr, "${err}");
+ }
+ }
+
+ size_t len = xstrwidth(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 >= max_argc || !args[i + 1])) {
+ cfprintf(ctx->cerr, "${rs}");
+ }
+ }
+
+ cfprintf(ctx->cerr, "\n");
+
+done:
+ for (size_t i = 0; i < ctx->argc; ++i) {
+ dstrfree(argv[i]);
+ }
+ free(argv);
+}
+
+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;
+}