diff options
Diffstat (limited to 'src/erec.c')
-rw-r--r-- | src/erec.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/src/erec.c b/src/erec.c new file mode 100644 index 0000000..cd9f62b --- /dev/null +++ b/src/erec.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2008 Patrick McHardy <kaber@trash.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Development of this code funded by Astaro AG (http://www.astaro.com/) + */ + +#include <nft.h> + +#include <stdio.h> +#include <stdarg.h> + +#include <netlink.h> +#include <gmputil.h> +#include <erec.h> + +static const struct input_descriptor internal_indesc = { + .type = INDESC_INTERNAL, + .name = "internal", +}; + +const struct location internal_location = { + .indesc = &internal_indesc, +}; + +static const char * const error_record_names[] = { + [EREC_INFORMATIONAL] = NULL, + [EREC_WARNING] = "Warning", + [EREC_ERROR] = "Error" +}; + +void erec_add_location(struct error_record *erec, const struct location *loc) +{ + assert(erec->num_locations < EREC_LOCATIONS_MAX); + erec->locations[erec->num_locations] = *loc; + erec->locations[erec->num_locations].indesc = loc->indesc ? + : &internal_indesc; + erec->num_locations++; +} + +void erec_destroy(struct error_record *erec) +{ + xfree(erec->msg); + xfree(erec); +} + +__attribute__((format(printf, 3, 0))) +struct error_record *erec_vcreate(enum error_record_types type, + const struct location *loc, + const char *fmt, va_list ap) +{ + struct error_record *erec; + + erec = xmalloc(sizeof(*erec)); + erec->type = type; + erec->num_locations = 0; + erec_add_location(erec, loc); + + if (vasprintf(&erec->msg, fmt, ap) < 0) + erec->msg = NULL; + + return erec; +} + +__attribute__((format(printf, 3, 4))) +struct error_record *erec_create(enum error_record_types type, + const struct location *loc, + const char *fmt, ...) +{ + struct error_record *erec; + va_list ap; + + va_start(ap, fmt); + erec = erec_vcreate(type, loc, fmt, ap); + va_end(ap); + return erec; +} + +void print_location(FILE *f, const struct input_descriptor *indesc, + const struct location *loc) +{ + const struct input_descriptor *tmp; + const struct location *iloc; + + if (indesc->location.indesc != NULL) { + const char *prefix = "In file included from"; + iloc = &indesc->location; + for (tmp = iloc->indesc; + tmp != NULL && tmp->type != INDESC_INTERNAL; + tmp = iloc->indesc) { + fprintf(f, "%s %s:%u:%u-%u:\n", prefix, + tmp->name, + iloc->first_line, iloc->first_column, + iloc->last_column); + prefix = " from"; + iloc = &tmp->location; + } + } + if (indesc->type != INDESC_BUFFER && indesc->name) { + fprintf(f, "%s:%u:%u-%u: ", indesc->name, + loc->first_line, loc->first_column, + loc->last_column); + } +} + +const char *line_location(const struct input_descriptor *indesc, + const struct location *loc, char *buf, size_t bufsiz) +{ + const char *line = NULL; + FILE *f; + + f = fopen(indesc->name, "r"); + if (!f) + return NULL; + + if (!fseek(f, loc->line_offset, SEEK_SET) && + fread(buf, 1, bufsiz - 1, f) > 0) { + *strchrnul(buf, '\n') = '\0'; + line = buf; + } + fclose(f); + + return line; +} + +void erec_print(struct output_ctx *octx, const struct error_record *erec, + unsigned int debug_mask) +{ + const struct location *loc = erec->locations; + const struct input_descriptor *indesc = loc->indesc; + const char *line = NULL; + char buf[1024] = {}; + char *pbuf = NULL; + unsigned int i, end; + FILE *f; + int l; + + switch (indesc->type) { + case INDESC_BUFFER: + case INDESC_CLI: + line = indesc->data; + *strchrnul(line, '\n') = '\0'; + break; + case INDESC_STDIN: + line = indesc->data; + line += loc->line_offset; + *strchrnul(line, '\n') = '\0'; + break; + case INDESC_FILE: + line = line_location(indesc, loc, buf, sizeof(buf)); + break; + case INDESC_INTERNAL: + case INDESC_NETLINK: + break; + default: + BUG("invalid input descriptor type %u\n", indesc->type); + } + + f = octx->error_fp; + + if (indesc->type == INDESC_NETLINK) { + fprintf(f, "%s: ", indesc->name); + if (error_record_names[erec->type]) + fprintf(f, "%s: ", error_record_names[erec->type]); + fprintf(f, "%s\n", erec->msg); + for (l = 0; l < (int)erec->num_locations; l++) { + loc = &erec->locations[l]; + if (!loc->nle) + continue; + netlink_dump_expr(loc->nle, f, debug_mask); + } + return; + } + + print_location(f, indesc, loc); + + if (error_record_names[erec->type]) + fprintf(f, "%s: ", error_record_names[erec->type]); + fprintf(f, "%s\n", erec->msg); + + if (line) { + fprintf(f, "%s\n", line); + + end = 0; + for (l = erec->num_locations - 1; l >= 0; l--) { + loc = &erec->locations[l]; + end = max(end, loc->last_column); + } + pbuf = xmalloc(end + 1); + memset(pbuf, ' ', end + 1); + for (i = 0; i < end && line[i]; i++) { + if (line[i] == '\t') + pbuf[i] = '\t'; + } + for (l = erec->num_locations - 1; l >= 0; l--) { + loc = &erec->locations[l]; + for (i = loc->first_column ? loc->first_column - 1 : 0; + i < loc->last_column; i++) + pbuf[i] = l ? '~' : '^'; + } + pbuf[end] = '\0'; + fprintf(f, "%s", pbuf); + xfree(pbuf); + } + fprintf(f, "\n"); +} + +void erec_print_list(struct output_ctx *octx, struct list_head *list, + unsigned int debug_mask) +{ + struct error_record *erec, *next; + + list_for_each_entry_safe(erec, next, list, list) { + list_del(&erec->list); + erec_print(octx, erec, debug_mask); + erec_destroy(erec); + } +} + +int __fmtstring(4, 5) __stmt_binary_error(struct eval_ctx *ctx, + const struct location *l1, + const struct location *l2, + const char *fmt, ...) +{ + struct error_record *erec; + va_list ap; + + va_start(ap, fmt); + erec = erec_vcreate(EREC_ERROR, l1, fmt, ap); + if (l2 != NULL) + erec_add_location(erec, l2); + va_end(ap); + erec_queue(erec, ctx->msgs); + return -1; +} |