Add new module 'logging' to handle debug logging

This should be nicer than using the preprocessor.
This commit is contained in:
Thomas Jensen 2023-11-25 13:53:21 +01:00
parent 716348befc
commit d8e0e7421c
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
5 changed files with 319 additions and 5 deletions

View File

@ -23,11 +23,11 @@ GEN_HDR = parser.h boxes.h lex.yy.h
GEN_SRC = parser.c lex.yy.c
GEN_FILES = $(GEN_SRC) $(GEN_HDR)
ORIG_HDRCL = boxes.in.h config.h
ORIG_HDR = $(ORIG_HDRCL) bxstring.h cmdline.h detect.h discovery.h generate.h input.h list.h parsecode.h parsing.h \
query.h regulex.h remove.h shape.h tools.h unicode.h
ORIG_HDR = $(ORIG_HDRCL) bxstring.h cmdline.h detect.h discovery.h generate.h input.h list.h logging.h parsecode.h \
parsing.h query.h regulex.h remove.h shape.h tools.h unicode.h
ORIG_GEN = lexer.l parser.y
ORIG_NORM = boxes.c bxstring.c cmdline.c detect.c discovery.c generate.c input.c list.c parsecode.c parsing.c query.c \
regulex.c remove.c shape.c tools.c unicode.c
ORIG_NORM = boxes.c bxstring.c cmdline.c detect.c discovery.c generate.c input.c list.c logging.c parsecode.c \
parsing.c query.c regulex.c remove.c shape.c tools.c unicode.c
ORIG_SRC = $(ORIG_GEN) $(ORIG_NORM)
ORIG_FILES = $(ORIG_SRC) $(ORIG_HDR)
@ -116,6 +116,7 @@ generate.o: generate.c generate.h boxes.h shape.h tools.h unicode.h config.h |
input.o: input.c boxes.h input.h regulex.h tools.h unicode.h config.h | check_dir
lex.yy.o: lex.yy.c parser.h boxes.h parsing.h tools.h shape.h unicode.h config.h | check_dir
list.o: list.c list.h boxes.h bxstring.h parsing.h query.h shape.h tools.h unicode.h config.h | check_dir
logging.o: logging.c logging.h tools.h config.h | check_dir
parsecode.o: parsecode.c parsecode.h discovery.h lex.yy.h parsing.h parser.h query.h regulex.h shape.h tools.h unicode.h config.h | check_dir
parser.o: parser.c boxes.h bxstring.h lex.yy.h parsecode.h parser.h parsing.h shape.h tools.h unicode.h config.h | check_dir
parsing.o: parsing.c parsing.h bxstring.h parser.h lex.yy.h boxes.h tools.h config.h | check_dir

View File

@ -143,6 +143,7 @@ typedef struct { /* Command line options: */
char *eol; /** `-e`: line break to use. Never NULL, default to "\n". */
int eol_overridden; /** `-e`: 0: value in `eol` is the default; 1: value in `eol` specified via `-e` */
char *f; /** `-f`: config file path */
int *debug; /** `-g`: activate debug logging for given debug log areas */
int help; /** `-h`: flags if help argument was specified */
char indentmode; /** `-i`: 'b', 't', 'n', or '\0' */
int killblank; /** `-k`: kill blank lines, -1 if not set */

View File

@ -33,6 +33,7 @@
#include "boxes.h"
#include "discovery.h"
#include "logging.h"
#include "query.h"
#include "tools.h"
#include "cmdline.h"
@ -91,6 +92,7 @@ void usage_long(FILE *st)
strcmp(EOL_DEFAULT, "\r\n") == 0 ? "CRLF" : "LF");
fprintf(st, " -f, --config <file> Configuration file [default: %s]\n",
config_file != NULL ? bxs_to_output(config_file) : "none");
/* fprintf(st, " -g, --debug <areas> Activate debug logging for specified log areas [default area: MAIN]"); // undocumented */
fprintf(st, " -h, --help Print usage information\n");
fprintf(st, " -i, --indent <mode> Indentation mode [default: box]\n");
fprintf(st, " -k <bool> Leading/trailing blank line retention on removal\n");
@ -121,6 +123,7 @@ static opt_t *create_new_opt()
result->eol = "\n"; /* we must default to "\n" instead of EOL_DEFAULT as long as stdout is in text mode */
result->tabexp = 'e';
result->killblank = -1;
result->debug = (int *) calloc(NUM_LOG_AREAS, sizeof(int));
for (int i = 0; i < NUM_SIDES; ++i) {
result->padding[i] = -1;
}
@ -458,6 +461,51 @@ static int size_of_box(opt_t *result, char *optarg)
static int debug_areas(opt_t *result, char *optarg)
{
char *dup = NULL;
if (optarg != NULL) {
dup = strdup(optarg); /* required because strtok() modifies its input */
}
else {
dup = strdup(log_area_names[MAIN]);
}
for (char *a = strtok(dup, ","); a != NULL; a = strtok(NULL, ","))
{
char *trimmed = trimdup(a, a + strlen(a) - 1);
if (strlen(trimmed) == 0) {
BFREE(trimmed);
continue;
}
int valid = 0;
for (size_t i = ALL; i < NUM_LOG_AREAS + 2; i++) {
if (strcasecmp(trimmed, log_area_names[i]) == 0) {
valid = 1;
if (i == ALL) {
for (size_t i = 0; i < NUM_LOG_AREAS; i++) {
result->debug[i] = 1;
}
}
else {
result->debug[i - 2] = 1;
}
break;
}
}
if (!valid) {
bx_fprintf(stderr, "%s: invalid debug area -- %s\n", PROJECT, trimmed);
BFREE(trimmed);
return 1;
}
BFREE(trimmed);
}
BFREE(dup);
return 0;
}
/**
* Tab handling. Format is `n[eku]`.
* @param result the options struct we are building
@ -644,6 +692,7 @@ opt_t *process_commandline(int argc, char *argv[])
{ "create", required_argument, NULL, 'c' },
{ "color", no_argument, NULL, OPT_COLOR },
{ "no-color", no_argument, NULL, OPT_NO_COLOR },
{ "debug", optional_argument, NULL, 'g' },
{ "design", required_argument, NULL, 'd' },
{ "eol", required_argument, NULL, 'e' },
{ "config", required_argument, NULL, 'f' },
@ -662,7 +711,7 @@ opt_t *process_commandline(int argc, char *argv[])
{ "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 }
};
const char *short_options = "a:c:d:e:f:hi:k:lmn:p:q:rs:t:v";
const char *short_options = "a:c:d:e:f:g::hi:k:lmn:p:q:rs:t:v";
int oc; /* option character */
do {
@ -709,6 +758,14 @@ opt_t *process_commandline(int argc, char *argv[])
case 'f':
result->f = strdup(optarg); /* input file */
break;
case 'g':
if (debug_areas(result, optarg) != 0) {
BFREE(result);
return NULL;
}
activate_debug_logging(result->debug);
break;
case 'h':
result->help = 1;

154
src/logging.c Normal file
View File

@ -0,0 +1,154 @@
/*
* boxes - Command line filter to draw/remove ASCII boxes around text
* Copyright (c) 1999-2023 Thomas Jensen and the boxes contributors
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License, version 3, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* Simple makeshift log facility for handling debug output.
*/
#include "config.h"
#include <stdarg.h>
#include <string.h>
#include "regulex.h"
#include "tools.h"
#include "logging.h"
char *log_area_names[] = { "RESERVED", "ALL", "MAIN", "REGEXP", "PARSER", "LEXER", "DISCOVERY" };
static int debug_logging_active = 0;
static int *areas_active = NULL;
static char *shorten_module(const char *module)
{
if (module == NULL) {
return strdup("NULL");
}
const char *s = strrchr(module, '/');
if (s == NULL) {
s = strrchr(module, '\\');
}
if (s != NULL) {
s++;
}
else {
s = module;
}
char *e = strrchr(module, '.');
if (e != NULL && e > s && *s != '\0') {
char *result = strdup(s);
result[e-s] = '\0';
return result;
}
return strdup(s);
}
void activate_debug_logging(int *log_areas)
{
debug_logging_active = 0;
if (areas_active != NULL) {
BFREE(areas_active);
}
areas_active = (int *) calloc(NUM_LOG_AREAS, sizeof(int));
if (log_areas != NULL) {
memcpy(areas_active, log_areas, NUM_LOG_AREAS * sizeof(int));
debug_logging_active = 1;
}
}
static int is_area_active(log_area_t log_area)
{
if (debug_logging_active && areas_active != NULL && log_area != RESERVED && log_area < NUM_LOG_AREAS + 2) {
if (log_area == ALL) {
for (size_t i = 0; i < NUM_LOG_AREAS; i++) {
if (!areas_active[i]) {
return 0;
}
}
return 1;
}
return areas_active[log_area - 2];
}
return 0;
}
int is_debug_logging(log_area_t log_area)
{
return (debug_logging_active && is_area_active(log_area)) ? 1 : 0;
}
int is_debug_activated()
{
if (debug_logging_active) {
for (size_t i = 0; i < NUM_LOG_AREAS; i++) {
if (areas_active[i]) {
return 1;
}
}
}
return 0;
}
void log_debug(const char *module, log_area_t log_area, const char *format, ...)
{
if (is_debug_logging(log_area)) {
char *msg = (char *) malloc(1024);
va_list va;
va_start(va, format);
vsprintf(msg, format, va);
va_end(va);
char *short_module = shorten_module(module);
bx_fprintf(stderr, "[%-9s] %s", short_module, msg);
BFREE(short_module);
BFREE(msg);
}
}
void log_debug_cont(log_area_t log_area, const char *format, ...)
{
if (is_debug_logging(log_area)) {
char *msg = (char *) malloc(1024);
va_list va;
va_start(va, format);
vsprintf(msg, format, va);
va_end(va);
bx_fprintf(stderr, "%s", msg);
BFREE(msg);
}
}
/* vim: set cindent sw=4: */

101
src/logging.h Normal file
View File

@ -0,0 +1,101 @@
/*
* boxes - Command line filter to draw/remove ASCII boxes around text
* Copyright (c) 1999-2023 Thomas Jensen and the boxes contributors
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License, version 3, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* Simple makeshift log facility for handling debug output.
*
* Debug output is printed on stderr. Each message is annotated with its module and "area".
* The "area" defines certain sections of code as a possible filter criterion.
*/
#ifndef LOGGING_H
#define LOGGING_H
/** Enum of the log areas supported. `ALL` means all of them, `RESERVED` cannot be used. */
typedef enum _log_area_t {
/** the 0 value is reserved and must not be used */
RESERVED,
/** all areas */
ALL,
/** the main areas of the code (activate this if unsure) */
MAIN,
/** regular expression handling */
REGEXP,
/** parser code */
PARSER,
/** lexer code */
LEXER,
/** config file discovery */
DISCOVERY
} log_area_t;
/** names of the values of `log_area_t` as strings for parsing */
extern char *log_area_names[];
/** number of log areas in `log_area_t`, excluding `ALL` and `RESERVED` */
#define NUM_LOG_AREAS 5
/**
* Activate debug logging.
* @param log_areas an array of log areas to activate, zero-terminated, should not be empty. The value will by copied
* and can be freed after returning from the function. Passing NULL will turn off debug logging.
*/
void activate_debug_logging(int *log_areas);
/**
* Determine if debug logging was activated for any debug area.
* @return 1 if active, 0 if not
*/
int is_debug_activated();
/**
* Determine if debug logging is active and the given area is active, too.
* @param log_area the log area to check
* @return 1 if active, 0 if not
*/
int is_debug_logging(log_area_t log_area);
/**
* Print a debug log message if the log area is active. Debug log messages will be printed on `stderr`.
* @param module the module name as per `__FILE__`
* @param log_area_t log_area
* @param format format string for the message, followed by the placeholder arguments
*/
void log_debug(const char *module, log_area_t log_area, const char *format, ...);
/**
* Print a debug log message if the log area is active. Debug log messages will be printed on `stderr`.
* The difference to `log_debug()` is that no module is printed.
* @param log_area_t log_area
* @param format format string for the message, followed by the placeholder arguments
*/
void log_debug_cont(log_area_t log_area, const char *format, ...);
#endif
/* vim: set cindent sw=4: */