// Copyright © Tavian Barnes <tavianator@tavianator.com> // SPDX-License-Identifier: 0BSD /** * Diagnostic messages. */ #ifndef BFS_DIAG_H #define BFS_DIAG_H #include "prelude.h" #include "bfstd.h" #include <stdarg.h> /** * static_assert() with an optional second argument. */ #if __STDC_VERSION__ >= C23 # define bfs_static_assert static_assert #else # define bfs_static_assert(...) bfs_static_assert_(__VA_ARGS__, #__VA_ARGS__, ) # define bfs_static_assert_(expr, msg, ...) _Static_assert(expr, msg) #endif /** * A source code location. */ struct bfs_loc { const char *file; int line; const char *func; }; #define BFS_LOC_INIT { .file = __FILE__, .line = __LINE__, .func = __func__ } /** * Get the current source code location. */ #if __STDC_VERSION__ >= C23 # define bfs_location() (&(static const struct bfs_loc)BFS_LOC_INIT) #else # define bfs_location() (&(const struct bfs_loc)BFS_LOC_INIT) #endif /** * Print a low-level diagnostic message to standard error, formatted like * * bfs: func@src/file.c:0: Message */ attr(printf(2, 3)) void bfs_diagf(const struct bfs_loc *loc, const char *format, ...); /** * Unconditional diagnostic message. */ #define bfs_diag(...) bfs_diagf(bfs_location(), __VA_ARGS__) /** * Print a diagnostic message including the last error. */ #define bfs_ediag(...) \ bfs_ediag_("" __VA_ARGS__, errstr()) #define bfs_ediag_(format, ...) \ bfs_diag(sizeof(format) > 1 ? format ": %s" : "%s", __VA_ARGS__) /** * Print a message to standard error and abort. */ attr(cold, printf(2, 3)) noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...); /** * Unconditional abort with a message. */ #define bfs_abort(...) \ bfs_abortf(bfs_location(), __VA_ARGS__) /** * Abort with a message including the last error. */ #define bfs_eabort(...) \ bfs_eabort_("" __VA_ARGS__, errstr()) #define bfs_eabort_(format, ...) \ bfs_abort(sizeof(format) > 1 ? format ": %s" : "%s", __VA_ARGS__) /** * Abort in debug builds; no-op in release builds. */ #ifdef NDEBUG # define bfs_bug(...) ((void)0) # define bfs_ebug(...) ((void)0) #else # define bfs_bug bfs_abort # define bfs_ebug bfs_eabort #endif /** * Unconditional assert. */ #define bfs_verify(...) \ bfs_verify_(#__VA_ARGS__, __VA_ARGS__, "", "") #define bfs_verify_(str, cond, format, ...) \ ((cond) ? (void)0 : bfs_abort( \ sizeof(format) > 1 \ ? "%.0s" format "%s%s" \ : "Assertion failed: `%s`%s", \ str, __VA_ARGS__)) /** * Unconditional assert, including the last error. */ #define bfs_everify(...) \ bfs_everify_(#__VA_ARGS__, __VA_ARGS__, "", errstr()) #define bfs_everify_(str, cond, format, ...) \ ((cond) ? (void)0 : bfs_abort( \ sizeof(format) > 1 \ ? "%.0s" format "%s: %s" \ : "Assertion failed: `%s`: %s", \ str, __VA_ARGS__)) /** * Assert in debug builds; no-op in release builds. */ #ifdef NDEBUG # define bfs_assert(...) ((void)0) # define bfs_eassert(...) ((void)0) #else # define bfs_assert bfs_verify # define bfs_eassert bfs_everify #endif struct bfs_ctx; struct bfs_expr; /** * Various debugging flags. */ enum debug_flags { /** Print cost estimates. */ DEBUG_COST = 1 << 0, /** Print executed command details. */ DEBUG_EXEC = 1 << 1, /** Print optimization details. */ DEBUG_OPT = 1 << 2, /** Print rate information. */ DEBUG_RATES = 1 << 3, /** Trace the filesystem traversal. */ DEBUG_SEARCH = 1 << 4, /** Trace all stat() calls. */ DEBUG_STAT = 1 << 5, /** Print the parse tree. */ DEBUG_TREE = 1 << 6, /** All debug flags. */ DEBUG_ALL = (1 << 7) - 1, }; /** * Convert a debug flag to a string. */ const char *debug_flag_name(enum debug_flags flag); /** * Like perror(), but decorated like bfs_error(). */ attr(cold) void bfs_perror(const struct bfs_ctx *ctx, const char *str); /** * Shorthand for printing error messages. */ attr(cold, printf(2, 3)) void bfs_error(const struct bfs_ctx *ctx, const char *format, ...); /** * Shorthand for printing warning messages. * * @return Whether a warning was printed. */ attr(cold, printf(2, 3)) bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...); /** * Shorthand for printing debug messages. * * @return Whether a debug message was printed. */ 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, 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, 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, printf(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) void bfs_error_prefix(const struct bfs_ctx *ctx); /** * Print the warning message prefix. */ attr(cold) bool bfs_warning_prefix(const struct bfs_ctx *ctx); /** * Print the debug message prefix. */ 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) void bfs_argv_error(const struct bfs_ctx *ctx, const bool args[]); /** * Highlight parts of an expression in an error message. */ 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) bool bfs_argv_warning(const struct bfs_ctx *ctx, const bool args[]); /** * Highlight parts of an expression in a warning message. */ attr(cold) bool bfs_expr_warning(const struct bfs_ctx *ctx, const struct bfs_expr *expr); #endif // BFS_DIAG_H