From cf99abb852536867169168d2c7fabf158099dfd9 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Thu, 23 Sep 2021 13:38:20 -0700 Subject: WIP implementation of setjmp/longjmp recovery --- bp.c | 2 ++ pattern.c | 42 +++++++++++++++++++++++++++++++++++------- print.c | 10 +++++----- types.h | 3 +++ 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/bp.c b/bp.c index 08cde5c..078afff 100644 --- a/bp.c +++ b/bp.c @@ -463,6 +463,8 @@ int main(int argc, char *argv[]) file_t *arg_file = spoof_file(&loaded_files, "", flag, -1); pat_t *p = bp_pattern(arg_file, arg_file->start); if (!p) file_err(arg_file, arg_file->start, arg_file->end, "Failed to compile this part of the argument"); + if (p->type == BP_ERROR) + file_err(arg_file, p->args.error.start, p->args.error.end, p->args.error.msg); if (after_spaces(p->end, true) < arg_file->end) file_err(arg_file, p->end, arg_file->end, "Failed to compile this part of the argument"); pattern = chain_together(arg_file, pattern, p); } else if (FLAG("-w") || FLAG("--word")) { diff --git a/pattern.c b/pattern.c index 28a4ecf..6afe5a2 100644 --- a/pattern.c +++ b/pattern.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "files.h" #include "pattern.h" @@ -18,6 +19,29 @@ static pat_t *bp_pattern_nl(file_t *f, const char *str, bool allow_nl); __attribute__((nonnull)) static pat_t *bp_simplepattern(file_t *f, const char *str); +// For error-handling purposes, use setjmp/longjmp to break out of deeply +// recursive function calls when a parse error occurs. +bool is_in_try_catch = false; +static jmp_buf err_jmp; +pat_t *err_pat = NULL; + +#define __TRY_PATTERN__ bool was_in_try_catch = is_in_try_catch; \ + if (!is_in_try_catch) { is_in_try_catch = true; if (setjmp(err_jmp)) return err_pat; } +#define __END_TRY_PATTERN__ if (!was_in_try_catch) is_in_try_catch = false; + +static inline void parse_err(file_t *f, const char *start, const char *end, const char *msg) +{ + if (!is_in_try_catch) { + fprintf(stderr, "Parse error: %s\n%.*s\n", msg, (int)(end-start), start); + exit(1); + } + err_pat = new_pat(f, start, end, 0, 0, BP_ERROR); + err_pat->args.error.start = start; + err_pat->args.error.end = end; + err_pat->args.error.msg = msg; + longjmp(err_jmp, 1); +} + // // Allocate a new pattern for this file (ensuring it will be automatically // freed when the file is freed) @@ -67,9 +91,6 @@ static pat_t *expand_chain(file_t *f, pat_t *first, bool allow_nl) pat_t *second = bp_simplepattern(f, str); if (second == NULL) return first; second = expand_chain(f, second, allow_nl); - if (second->end <= first->end) - file_err(f, second->end, second->end, - "This chain is not parsing properly"); return chain_together(f, first, second); } @@ -90,7 +111,7 @@ static pat_t *expand_replacements(file_t *f, pat_t *replace_pat, bool allow_nl) for (; str < f->end && *str != closequote; str = next_char(str, f->end)) { if (*str == '\\') { if (!str[1] || str[1] == '\n') - file_err(f, str, str+1, + parse_err(f, str, str+1, "There should be an escape sequence after this backslash."); str = next_char(str, f->end); } @@ -130,7 +151,7 @@ static pat_t *expand_choices(file_t *f, pat_t *first, bool allow_nl) if (matchstr(&str, "=>", allow_nl)) second = expand_replacements(f, second ? second : new_pat(f, str-2, str-2, 0, 0, BP_STRING), allow_nl); if (!second) - file_err(f, str, str, "There should be a pattern here after a '/'"); + parse_err(f, str, str, "There should be a pattern here after a '/'"); second = expand_choices(f, second, allow_nl); return either_pat(f, first, second); } @@ -221,7 +242,7 @@ static pat_t *_bp_simplepattern(file_t *f, const char *str) pat_t *all = NULL; do { // Comma-separated items: if (str >= f->end || !*str || *str == '\n') - file_err(f, str, str, "There should be a character here after the '`'"); + parse_err(f, str, str, "There should be a character here after the '`'"); const char *c1_loc = str; str = next_char(c1_loc, f->end); @@ -490,6 +511,7 @@ static pat_t *_bp_simplepattern(file_t *f, const char *str) // pat_t *bp_stringpattern(file_t *f, const char *str) { + __TRY_PATTERN__ pat_t *ret = NULL; while (str < f->end) { char *start = (char*)str; @@ -518,6 +540,7 @@ pat_t *bp_stringpattern(file_t *f, const char *str) (void)matchchar(&str, ';', false); } } + __END_TRY_PATTERN__ return ret; } @@ -562,6 +585,7 @@ pat_t *bp_replacement(file_t *f, pat_t *replacepat, const char *replacement) pat_t *pat = new_pat(f, replacepat->start, replacepat->end, replacepat->min_matchlen, replacepat->max_matchlen, BP_REPLACE); pat->args.replace.pat = replacepat; const char *p = replacement; + __TRY_PATTERN__ for (; p < f->end; p++) { if (*p == '\\') { if (!p[1] || p[1] == '\n') @@ -569,6 +593,7 @@ pat_t *bp_replacement(file_t *f, pat_t *replacepat, const char *replacement) ++p; } } + __END_TRY_PATTERN__ size_t rlen = (size_t)(p-replacement); char *rcpy = new(char[rlen + 1]); memcpy(rcpy, replacement, rlen); @@ -592,7 +617,10 @@ static pat_t *bp_pattern_nl(file_t *f, const char *str, bool allow_nl) // pat_t *bp_pattern(file_t *f, const char *str) { - return bp_pattern_nl(f, str, false); + __TRY_PATTERN__ + pat_t *ret = bp_pattern_nl(f, str, false); + __END_TRY_PATTERN__ + return ret; } // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/print.c b/print.c index dcebce9..36e9d80 100644 --- a/print.c +++ b/print.c @@ -56,9 +56,9 @@ static void print_between(FILE *out, printer_t *pr, const char *start, const cha { file_t *f = pr->file; while (start < end) { - size_t line_num = get_line_number(f, start); + size_t line_num = f ? get_line_number(f, start) : 0; print_line_number(out, pr, line_num, color, 0); - const char *eol = get_line(pr->file, line_num + 1); + const char *eol = f ? get_line(f, line_num + 1) : end; if (!eol || eol > end) eol = end; if (color && color != current_color) { fprintf(out, "%s", color); @@ -118,8 +118,8 @@ static void _print_match(FILE *out, printer_t *pr, match_t *m) { pr->pos = m->start; if (m->pat->type == BP_REPLACE) { - size_t line = get_line_number(pr->file, m->start); - size_t line_end = get_line_number(pr->file, m->end); + size_t line = pr->file ? get_line_number(pr->file, m->start) : 0; + size_t line_end = pr->file ? get_line_number(pr->file, m->end) : 0; if (pr->use_color && current_color != color_replace) { fprintf(out, "%s", color_replace); @@ -232,7 +232,7 @@ void print_match(FILE *out, printer_t *pr, match_t *m) current_color = color_normal; bool first = (pr->pos == NULL); if (first) { // First match printed: - pr->pos = pr->file->start; + pr->pos = pr->file ? pr->file->start : m->start; pr->needs_line_number = 1; } if (m) { diff --git a/types.h b/types.h index eb22e69..f9a1e05 100644 --- a/types.h +++ b/types.h @@ -92,6 +92,9 @@ typedef struct pat_s { const char *at; struct pat_s *fallback; } leftrec; + struct { + const char *start, *end, *msg; + } error; struct pat_s *pat; } args; size_t id; -- cgit v1.2.3