aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2021-09-23 13:38:20 -0700
committerBruce Hill <bruce@bruce-hill.com>2021-09-23 13:38:20 -0700
commitcf99abb852536867169168d2c7fabf158099dfd9 (patch)
treebf0f33da9f5ec02d9297e52ba83146edda9d9610
parent55ff8ec6637d90d50ed83c43f83e25fe4571f6bb (diff)
WIP implementation of setjmp/longjmp recovery
-rw-r--r--bp.c2
-rw-r--r--pattern.c42
-rw-r--r--print.c10
-rw-r--r--types.h3
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, "<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")) {
diff --git a/pattern.c b/pattern.c
index 28a4ecf..6afe5a2 100644
--- a/pattern.c
+++ b/pattern.c
@@ -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
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;