diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2021-09-23 13:38:20 -0700 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2021-09-23 13:38:20 -0700 |
| commit | cf99abb852536867169168d2c7fabf158099dfd9 (patch) | |
| tree | bf0f33da9f5ec02d9297e52ba83146edda9d9610 | |
| parent | 55ff8ec6637d90d50ed83c43f83e25fe4571f6bb (diff) | |
WIP implementation of setjmp/longjmp recovery
| -rw-r--r-- | bp.c | 2 | ||||
| -rw-r--r-- | pattern.c | 42 | ||||
| -rw-r--r-- | print.c | 10 | ||||
| -rw-r--r-- | types.h | 3 |
4 files changed, 45 insertions, 12 deletions
@@ -463,6 +463,8 @@ int main(int argc, char *argv[]) file_t *arg_file = spoof_file(&loaded_files, "<pattern argument>", 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")) { @@ -7,6 +7,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <setjmp.h> #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 @@ -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) { @@ -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; |
