parent
538706858e
commit
2bc0ca7390
55
Lua/lbp.c
55
Lua/lbp.c
@ -33,8 +33,10 @@ static void push_match(lua_State *L, match_t *m, const char *start);
|
|||||||
|
|
||||||
lua_State *cur_state = NULL;
|
lua_State *cur_state = NULL;
|
||||||
|
|
||||||
static void match_error(const char *msg)
|
static void match_error(pat_t *pat, const char *msg)
|
||||||
{
|
{
|
||||||
|
(void)pat;
|
||||||
|
recycle_all_matches();
|
||||||
lua_pushstring(cur_state, msg);
|
lua_pushstring(cur_state, msg);
|
||||||
lua_error(cur_state);
|
lua_error(cur_state);
|
||||||
}
|
}
|
||||||
@ -139,19 +141,6 @@ static void push_match(lua_State *L, match_t *m, const char *start)
|
|||||||
lua_setfield(L, -2, "after");
|
lua_setfield(L, -2, "after");
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
lua_State *L;
|
|
||||||
const char *text;
|
|
||||||
} lua_match_context_t;
|
|
||||||
|
|
||||||
static bp_match_behavior handle_match(match_t *m, int matchnum, void *data)
|
|
||||||
{
|
|
||||||
(void)matchnum;
|
|
||||||
lua_match_context_t *ctx = data;
|
|
||||||
push_match(ctx->L, m, ctx->text);
|
|
||||||
return BP_STOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Lmatch(lua_State *L)
|
static int Lmatch(lua_State *L)
|
||||||
{
|
{
|
||||||
if (lua_isstring(L, 1)) {
|
if (lua_isstring(L, 1)) {
|
||||||
@ -178,29 +167,17 @@ static int Lmatch(lua_State *L)
|
|||||||
if (index > (lua_Integer)strlen(text)+1)
|
if (index > (lua_Integer)strlen(text)+1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
match_t *m = NULL;
|
||||||
|
int ret = 0;
|
||||||
cur_state = L;
|
cur_state = L;
|
||||||
lua_match_context_t ctx = {.L=L, .text=text};
|
if (next_match_safe(&m, text+index-1, &text[textlen], pat, builtins, NULL, false, match_error)) {
|
||||||
bp_errhand_t prev_handler = set_match_error_handler(match_error);
|
push_match(L, m, text);
|
||||||
int ret = each_match(handle_match, &ctx, text+index-1, &text[textlen], pat, builtins, NULL, false);
|
stop_matching(&m);
|
||||||
(void)set_match_error_handler(prev_handler);
|
ret = 1;
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FILE *out;
|
|
||||||
const char *prev, *text;
|
|
||||||
} lua_replace_context_t;
|
|
||||||
|
|
||||||
static bp_match_behavior handle_replacement(match_t *m, int matchnum, void *data)
|
|
||||||
{
|
|
||||||
(void)matchnum;
|
|
||||||
lua_replace_context_t *ctx = data;
|
|
||||||
fwrite(ctx->prev, sizeof(char), (size_t)(m->start - ctx->prev), ctx->out);
|
|
||||||
fprint_match(ctx->out, ctx->text, m, NULL);
|
|
||||||
ctx->prev = m->end;
|
|
||||||
return BP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Lreplace(lua_State *L)
|
static int Lreplace(lua_State *L)
|
||||||
{
|
{
|
||||||
if (lua_isstring(L, 1)) {
|
if (lua_isstring(L, 1)) {
|
||||||
@ -228,14 +205,16 @@ static int Lreplace(lua_State *L)
|
|||||||
char *buf = NULL;
|
char *buf = NULL;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
FILE *out = open_memstream(&buf, &size);
|
FILE *out = open_memstream(&buf, &size);
|
||||||
|
int replacements = 0;
|
||||||
const char *prev = text;
|
const char *prev = text;
|
||||||
pat_t *rep_pat = maybe_replacement.value.pat;
|
pat_t *rep_pat = maybe_replacement.value.pat;
|
||||||
cur_state = L;
|
cur_state = L;
|
||||||
|
for (match_t *m = NULL; next_match_safe(&m, text, &text[textlen], rep_pat, builtins, NULL, false, match_error); ) {
|
||||||
lua_replace_context_t ctx = {.out=out, .prev=text, .text=text};
|
fwrite(prev, sizeof(char), (size_t)(m->start - prev), out);
|
||||||
bp_errhand_t prev_handler = set_match_error_handler(match_error);
|
fprint_match(out, text, m, NULL);
|
||||||
int replacements = each_match(handle_replacement, &ctx, text, &text[textlen], rep_pat, builtins, NULL, false);
|
prev = m->end;
|
||||||
(void)set_match_error_handler(prev_handler);
|
++replacements;
|
||||||
|
}
|
||||||
fwrite(prev, sizeof(char), (size_t)(&text[textlen] - prev), out);
|
fwrite(prev, sizeof(char), (size_t)(&text[textlen] - prev), out);
|
||||||
fflush(out);
|
fflush(out);
|
||||||
lua_pushlstring(L, buf, size);
|
lua_pushlstring(L, buf, size);
|
||||||
|
2
Makefile
2
Makefile
@ -2,7 +2,7 @@ NAME=bp
|
|||||||
CC=cc
|
CC=cc
|
||||||
PREFIX=/usr/local
|
PREFIX=/usr/local
|
||||||
SYSCONFDIR=/etc
|
SYSCONFDIR=/etc
|
||||||
CFLAGS=-std=c99 -Werror -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -flto=auto
|
CFLAGS=-std=c99 -Werror -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -flto
|
||||||
CWARN=-Wall -Wextra
|
CWARN=-Wall -Wextra
|
||||||
# -Wpedantic -Wsign-conversion -Wtype-limits -Wunused-result -Wnull-dereference \
|
# -Wpedantic -Wsign-conversion -Wtype-limits -Wunused-result -Wnull-dereference \
|
||||||
# -Waggregate-return -Walloc-zero -Walloca -Warith-conversion -Wcast-align -Wcast-align=strict \
|
# -Waggregate-return -Walloc-zero -Walloca -Warith-conversion -Wcast-align -Wcast-align=strict \
|
||||||
|
100
bp.c
100
bp.c
@ -60,9 +60,6 @@ static const char *usage = (
|
|||||||
#define ALL_CONTEXT -2
|
#define ALL_CONTEXT -2
|
||||||
#define NO_CONTEXT -1
|
#define NO_CONTEXT -1
|
||||||
|
|
||||||
#define each_match_file(callback, userdata, f, pat, defs) \
|
|
||||||
each_match(callback, userdata, (f)->start, (f)->end, pat, defs, options.skip, options.ignorecase)
|
|
||||||
|
|
||||||
// Flag-configurable options:
|
// Flag-configurable options:
|
||||||
static struct {
|
static struct {
|
||||||
int context_before, context_after;
|
int context_before, context_after;
|
||||||
@ -91,7 +88,6 @@ const char *LINE_FORMATS[] = {
|
|||||||
// If a file is partly through being modified when the program exits, restore it from backup.
|
// If a file is partly through being modified when the program exits, restore it from backup.
|
||||||
static FILE *modifying_file = NULL;
|
static FILE *modifying_file = NULL;
|
||||||
static file_t *backup_file;
|
static file_t *backup_file;
|
||||||
static int files_printed = 0;
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Helper function to reduce code duplication
|
// Helper function to reduce code duplication
|
||||||
@ -211,30 +207,34 @@ static int is_text_file(const char *filename)
|
|||||||
//
|
//
|
||||||
// Print matches in JSON format.
|
// Print matches in JSON format.
|
||||||
//
|
//
|
||||||
static bp_match_behavior print_match_as_json(match_t *m, int matchnum, void *data)
|
static int print_matches_as_json(file_t *f, pat_t *pattern, pat_t *defs)
|
||||||
{
|
{
|
||||||
file_t *f = (file_t*)data;
|
int nmatches = 0;
|
||||||
if (++matchnum > 1)
|
for (match_t *m = NULL; next_match(&m, f->start, f->end, pattern, defs, options.skip, options.ignorecase); ) {
|
||||||
|
if (++nmatches > 1)
|
||||||
printf(",\n");
|
printf(",\n");
|
||||||
printf("{\"match\":");
|
printf("{\"filename\":\"%s\",\"match\":", f->filename);
|
||||||
json_match(f->start, m, options.verbose);
|
json_match(f->start, m, options.verbose);
|
||||||
printf("}");
|
printf("}");
|
||||||
return BP_CONTINUE;
|
}
|
||||||
|
return nmatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Print matches in a visual explanation style
|
// Print matches in a visual explanation style
|
||||||
//
|
//
|
||||||
static bp_match_behavior print_match_explanation(match_t *m, int matchnum, void *data)
|
static int explain_matches(file_t *f, pat_t *pattern, pat_t *defs)
|
||||||
{
|
{
|
||||||
file_t *f = (file_t*)data;
|
int nmatches = 0;
|
||||||
if (matchnum == 0) {
|
for (match_t *m = NULL; next_match(&m, f->start, f->end, pattern, defs, options.skip, options.ignorecase); ) {
|
||||||
|
if (++nmatches == 1) {
|
||||||
if (options.print_filenames)
|
if (options.print_filenames)
|
||||||
fprint_filename(stdout, f->filename);
|
fprint_filename(stdout, f->filename);
|
||||||
} else
|
} else
|
||||||
printf("\n\n");
|
printf("\n\n");
|
||||||
explain_match(m);
|
explain_match(m);
|
||||||
return BP_CONTINUE;
|
}
|
||||||
|
return nmatches;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -346,54 +346,40 @@ static void on_nl(FILE *out)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
file_t *f;
|
|
||||||
FILE *out;
|
|
||||||
const char *prev;
|
|
||||||
print_options_t print_opts;
|
|
||||||
} print_userdata_t;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Print the text of a single match with the chosen context info
|
|
||||||
//
|
|
||||||
static bp_match_behavior print_single_match(match_t *m, int matchnum, void *data)
|
|
||||||
{
|
|
||||||
print_userdata_t *info = data;
|
|
||||||
if (matchnum == 0 && options.print_filenames) {
|
|
||||||
if (files_printed++ > 0)
|
|
||||||
fprintf(info->out, "\n");
|
|
||||||
fprint_filename(info->out, info->f->filename);
|
|
||||||
}
|
|
||||||
fprint_context(info->out, info->f, info->prev, m->start);
|
|
||||||
if (info->print_opts.normal_color) fprintf(info->out, "%s", info->print_opts.normal_color);
|
|
||||||
fprint_match(info->out, info->f->start, m, &info->print_opts);
|
|
||||||
if (info->print_opts.normal_color) fprintf(info->out, "%s", info->print_opts.normal_color);
|
|
||||||
info->prev = m->end;
|
|
||||||
return BP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Print all the matches in a file.
|
// Print all the matches in a file.
|
||||||
//
|
//
|
||||||
static int print_matches(FILE *out, file_t *f, pat_t *pattern, pat_t *defs)
|
static int print_matches(FILE *out, file_t *f, pat_t *pattern, pat_t *defs)
|
||||||
{
|
{
|
||||||
print_userdata_t userdata = {.out = out, .f = f, .prev = NULL};
|
static int printed_filenames = 0;
|
||||||
userdata.print_opts = (print_options_t){.fprint_between = _fprint_between, .on_nl = on_nl};
|
int matches = 0;
|
||||||
|
const char *prev = NULL;
|
||||||
|
|
||||||
printing_file = f;
|
printing_file = f;
|
||||||
last_line_num = -1;
|
last_line_num = -1;
|
||||||
|
|
||||||
|
print_options_t print_opts = {.fprint_between = _fprint_between, .on_nl = on_nl};
|
||||||
if (options.format == FORMAT_FANCY) {
|
if (options.format == FORMAT_FANCY) {
|
||||||
userdata.print_opts.match_color = "\033[0;31;1m";
|
print_opts.match_color = "\033[0;31;1m";
|
||||||
userdata.print_opts.replace_color = "\033[0;34;1m";
|
print_opts.replace_color = "\033[0;34;1m";
|
||||||
userdata.print_opts.normal_color = "\033[m";
|
print_opts.normal_color = "\033[m";
|
||||||
|
}
|
||||||
|
for (match_t *m = NULL; next_match(&m, f->start, f->end, pattern, defs, options.skip, options.ignorecase); ) {
|
||||||
|
if (++matches == 1 && options.print_filenames) {
|
||||||
|
if (printed_filenames++ > 0) printf("\n");
|
||||||
|
fprint_filename(out, f->filename);
|
||||||
|
}
|
||||||
|
fprint_context(out, f, prev, m->start);
|
||||||
|
if (print_opts.normal_color) fprintf(out, "%s", print_opts.normal_color);
|
||||||
|
fprint_match(out, f->start, m, &print_opts);
|
||||||
|
if (print_opts.normal_color) fprintf(out, "%s", print_opts.normal_color);
|
||||||
|
prev = m->end;
|
||||||
}
|
}
|
||||||
int matches = each_match_file(print_single_match, &userdata, f, pattern, defs);
|
|
||||||
// Print trailing context if needed:
|
// Print trailing context if needed:
|
||||||
if (matches > 0) {
|
if (matches > 0) {
|
||||||
fprint_context(out, f, userdata.prev, NULL);
|
fprint_context(out, f, prev, NULL);
|
||||||
if (last_line_num < 0) { // Hacky fix to ensure line number gets printed for `bp -p '$$'`
|
if (last_line_num < 0) { // Hacky fix to ensure line number gets printed for `bp -p '$$'`
|
||||||
fprint_linenum(out, f, f->nlines, userdata.print_opts.normal_color);
|
fprint_linenum(out, f, f->nlines, print_opts.normal_color);
|
||||||
fputc('\n', out);
|
fputc('\n', out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -418,14 +404,21 @@ static int process_file(const char *filename, pat_t *pattern, pat_t *defs)
|
|||||||
|
|
||||||
int matches = 0;
|
int matches = 0;
|
||||||
if (options.mode == MODE_EXPLAIN) {
|
if (options.mode == MODE_EXPLAIN) {
|
||||||
matches += each_match_file(print_match_explanation, f, f, pattern, defs);
|
matches += explain_matches(f, pattern, defs);
|
||||||
} else if (options.mode == MODE_LISTFILES) {
|
} else if (options.mode == MODE_LISTFILES) {
|
||||||
matches += each_match_file((bp_match_callback)(BP_STOP), f, f, pattern, defs);
|
match_t *m = NULL;
|
||||||
|
if (next_match(&m, f->start, f->end, pattern, defs, options.skip, options.ignorecase)) {
|
||||||
|
printf("%s\n", f->filename);
|
||||||
|
matches += 1;
|
||||||
|
}
|
||||||
|
stop_matching(&m);
|
||||||
} else if (options.mode == MODE_JSON) {
|
} else if (options.mode == MODE_JSON) {
|
||||||
matches += each_match_file(print_match_as_json, f, f, pattern, defs);
|
matches += print_matches_as_json(f, pattern, defs);
|
||||||
} else if (options.mode == MODE_INPLACE) {
|
} else if (options.mode == MODE_INPLACE) {
|
||||||
if (each_match_file((bp_match_callback)(BP_STOP), f, f, pattern, defs) == 0)
|
match_t *m = NULL;
|
||||||
return 0;
|
bool found = next_match(&m, f->start, f->end, pattern, defs, options.skip, options.ignorecase);
|
||||||
|
stop_matching(&m);
|
||||||
|
if (!found) return 0;
|
||||||
|
|
||||||
// Ensure the file is resident in memory:
|
// Ensure the file is resident in memory:
|
||||||
if (f->mmapped) {
|
if (f->mmapped) {
|
||||||
@ -448,6 +441,8 @@ static int process_file(const char *filename, pat_t *pattern, pat_t *defs)
|
|||||||
}
|
}
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
|
if (recycle_all_matches() != 0)
|
||||||
|
fprintf(stderr, "\033[33;1mMemory leak: there should no longer be any matches in use at this point.\033[m\n");
|
||||||
destroy_file(&f);
|
destroy_file(&f);
|
||||||
(void)fflush(stdout);
|
(void)fflush(stdout);
|
||||||
return matches;
|
return matches;
|
||||||
@ -687,6 +682,7 @@ int main(int argc, char *argv[])
|
|||||||
// This code frees up all residual heap-allocated memory. Since the program
|
// This code frees up all residual heap-allocated memory. Since the program
|
||||||
// is about to exit, this step is unnecessary. However, it is useful for
|
// is about to exit, this step is unnecessary. However, it is useful for
|
||||||
// tracking down memory leaks.
|
// tracking down memory leaks.
|
||||||
|
free_all_matches();
|
||||||
free_all_pats();
|
free_all_pats();
|
||||||
while (loaded_files) {
|
while (loaded_files) {
|
||||||
file_t *next = loaded_files->next;
|
file_t *next = loaded_files->next;
|
||||||
|
111
match.c
111
match.c
@ -9,7 +9,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <setjmp.h>
|
|
||||||
|
|
||||||
#include "match.h"
|
#include "match.h"
|
||||||
#include "pattern.h"
|
#include "pattern.h"
|
||||||
@ -50,15 +49,12 @@ typedef struct match_ctx_s {
|
|||||||
static match_t *unused_matches = NULL;
|
static match_t *unused_matches = NULL;
|
||||||
static match_t *in_use_matches = NULL;
|
static match_t *in_use_matches = NULL;
|
||||||
|
|
||||||
static void default_error_handler(const char *msg) {
|
static void default_error_handler(pat_t *pat, const char *msg) {
|
||||||
|
(void)pat;
|
||||||
errx(EXIT_FAILURE, "%s", msg);
|
errx(EXIT_FAILURE, "%s", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case of errors, jump out of everything, clean up memory, and call the error handler.
|
|
||||||
// Errors in this case are things like referencing a rule that isn't defined.
|
|
||||||
static jmp_buf error_jump;
|
|
||||||
static bp_errhand_t error_handler = default_error_handler;
|
static bp_errhand_t error_handler = default_error_handler;
|
||||||
static char *error_message = NULL;
|
|
||||||
|
|
||||||
#define MATCHES(...) (match_t*[]){__VA_ARGS__, NULL}
|
#define MATCHES(...) (match_t*[]){__VA_ARGS__, NULL}
|
||||||
|
|
||||||
@ -66,20 +62,17 @@ __attribute__((hot, nonnull(1,2,3)))
|
|||||||
static match_t *match(match_ctx_t *ctx, const char *str, pat_t *pat);
|
static match_t *match(match_ctx_t *ctx, const char *str, pat_t *pat);
|
||||||
__attribute__((returns_nonnull))
|
__attribute__((returns_nonnull))
|
||||||
static match_t *new_match(pat_t *pat, const char *start, const char *end, match_t *children[]);
|
static match_t *new_match(pat_t *pat, const char *start, const char *end, match_t *children[]);
|
||||||
__attribute__((nonnull))
|
|
||||||
static void recycle_match(match_t **at_m);
|
|
||||||
static size_t free_all_matches(void);
|
|
||||||
static size_t recycle_all_matches(void);
|
|
||||||
|
|
||||||
__attribute__((format(printf,1,2)))
|
__attribute__((format(printf,2,3)))
|
||||||
static inline void match_error(const char *fmt, ...)
|
static inline void match_error(pat_t *pat, const char *fmt, ...)
|
||||||
{
|
{
|
||||||
if (error_message) free(error_message);
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
vasprintf(&error_message, fmt, args);
|
char buf[256];
|
||||||
|
vsnprintf(buf, sizeof(buf)-1, fmt, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
longjmp(error_jump, 1);
|
if (error_handler)
|
||||||
|
error_handler(pat, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static match_t *clone_match(match_t *m)
|
static match_t *clone_match(match_t *m)
|
||||||
@ -233,8 +226,7 @@ static pat_t *_lookup_def(pat_t *defs, const char *name, size_t namelen)
|
|||||||
return defs->args.def.meaning;
|
return defs->args.def.meaning;
|
||||||
defs = defs->args.def.next_def;
|
defs = defs->args.def.next_def;
|
||||||
} else {
|
} else {
|
||||||
free_all_matches();
|
match_error(defs, "Invalid pattern type in definitions");
|
||||||
match_error("Invalid pattern type in definitions");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -675,8 +667,7 @@ static match_t *match(match_ctx_t *ctx, const char *str, pat_t *pat)
|
|||||||
|
|
||||||
pat_t *ref = lookup_ctx(ctx, pat->args.ref.name, pat->args.ref.len);
|
pat_t *ref = lookup_ctx(ctx, pat->args.ref.name, pat->args.ref.len);
|
||||||
if (ref == NULL) {
|
if (ref == NULL) {
|
||||||
free_all_matches();
|
match_error(pat, "Unknown pattern: '%.*s'", (int)pat->args.ref.len, pat->args.ref.name);
|
||||||
match_error("Unknown pattern: '%.*s'", (int)pat->args.ref.len, pat->args.ref.name);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -763,8 +754,7 @@ static match_t *match(match_ctx_t *ctx, const char *str, pat_t *pat)
|
|||||||
return new_match(pat, str, str, NULL);
|
return new_match(pat, str, str, NULL);
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
free_all_matches();
|
match_error(pat, "Unknown pattern type: %u", pat->type);
|
||||||
match_error("Unknown pattern type: %u", pat->type);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -849,11 +839,23 @@ size_t free_all_matches(void)
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Iterate over matches.
|
// Iterate over matches.
|
||||||
|
// Usage: for (match_t *m = NULL; next_match(&m, ...); ) {...}
|
||||||
//
|
//
|
||||||
int each_match(bp_match_callback callback, void *userdata, const char *start, const char *end, pat_t *pat, pat_t *defs, pat_t *skip, bool ignorecase)
|
bool next_match(match_t **m, const char *start, const char *end, pat_t *pat, pat_t *defs, pat_t *skip, bool ignorecase)
|
||||||
{
|
{
|
||||||
if (!callback || !start || !pat) return -1;
|
const char *pos;
|
||||||
if (!end) end = start + strlen(start);
|
if (*m) {
|
||||||
|
// Make sure forward progress is occurring, even after zero-width matches:
|
||||||
|
pos = ((*m)->end > (*m)->start) ? (*m)->end : (*m)->end+1;
|
||||||
|
recycle_match(m);
|
||||||
|
} else {
|
||||||
|
pos = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pat) {
|
||||||
|
error_handler = default_error_handler;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
match_ctx_t ctx = {
|
match_ctx_t ctx = {
|
||||||
.cache = &(cache_t){0},
|
.cache = &(cache_t){0},
|
||||||
@ -862,55 +864,20 @@ int each_match(bp_match_callback callback, void *userdata, const char *start, co
|
|||||||
.ignorecase = ignorecase,
|
.ignorecase = ignorecase,
|
||||||
.defs = defs,
|
.defs = defs,
|
||||||
};
|
};
|
||||||
|
*m = (pos <= end) ? _next_match(&ctx, pos, pat, skip) : NULL;
|
||||||
int num_matches = 0;
|
|
||||||
pat_t *first = get_prerequisite(&ctx, pat);
|
|
||||||
// Don't bother looping if this can only match at the start/end:
|
|
||||||
if (first->type == BP_START_OF_FILE || first->type == BP_END_OF_FILE) {
|
|
||||||
match_t *m = match(&ctx, first->type == BP_START_OF_FILE ? start : end, pat);
|
|
||||||
if (m) {
|
|
||||||
if ((size_t)callback > 3)
|
|
||||||
(void)callback(m, num_matches, userdata);
|
|
||||||
++num_matches;
|
|
||||||
}
|
|
||||||
return num_matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hit_error = setjmp(error_jump) != 0;
|
|
||||||
if (!hit_error) {
|
|
||||||
for (const char *str = start; str <= end; ) {
|
|
||||||
match_t *m = _next_match(&ctx, str, pat, skip);
|
|
||||||
if (!m) break;
|
|
||||||
else if (callback == (void*)BP_STOP)
|
|
||||||
break;
|
|
||||||
else if (callback != (void*)BP_CONTINUE && callback(m, num_matches++, userdata) == BP_STOP)
|
|
||||||
break;
|
|
||||||
else if (str == m->start && str == end)
|
|
||||||
break;
|
|
||||||
str = m->end > str ? m->end : next_char(str, end);
|
|
||||||
recycle_all_matches();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cache_destroy(&ctx);
|
cache_destroy(&ctx);
|
||||||
free_all_matches();
|
return *m != NULL;
|
||||||
|
|
||||||
if (hit_error && error_handler) {
|
|
||||||
error_handler(error_message ? error_message : "An unknown error occurred");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error_message) {
|
|
||||||
free(error_message);
|
|
||||||
error_message = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return num_matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bp_errhand_t set_match_error_handler(bp_errhand_t errhand)
|
//
|
||||||
|
// Wrapper for next_match() that sets an error handler
|
||||||
|
//
|
||||||
|
bool next_match_safe(match_t **m, const char *start, const char *end, pat_t *pat, pat_t *defs, pat_t *skip, bool ignorecase, bp_errhand_t errhand)
|
||||||
{
|
{
|
||||||
bp_errhand_t old_errhand = errhand;
|
|
||||||
error_handler = errhand;
|
error_handler = errhand;
|
||||||
return old_errhand;
|
bool ret = next_match(m, start, end, pat, defs, skip, ignorecase);
|
||||||
|
error_handler = default_error_handler;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -945,7 +912,13 @@ static match_t *_get_numbered_capture(match_t *m, int *n)
|
|||||||
match_t *get_numbered_capture(match_t *m, int n)
|
match_t *get_numbered_capture(match_t *m, int n)
|
||||||
{
|
{
|
||||||
if (n <= 0) return m;
|
if (n <= 0) return m;
|
||||||
return _get_numbered_capture(m, &n);
|
if (m->children) {
|
||||||
|
for (int i = 0; m->children[i]; i++) {
|
||||||
|
match_t *cap = _get_numbered_capture(m->children[i], &n);
|
||||||
|
if (cap) return cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
14
match.h
14
match.h
@ -25,13 +25,15 @@ typedef struct match_s {
|
|||||||
struct match_s *_children[3];
|
struct match_s *_children[3];
|
||||||
} match_t;
|
} match_t;
|
||||||
|
|
||||||
typedef void (*bp_errhand_t)(const char *err_msg);
|
typedef void (*bp_errhand_t)(pat_t *pat, const char *err_msg);
|
||||||
|
|
||||||
typedef enum {BP_STOP = 0, BP_CONTINUE} bp_match_behavior;
|
|
||||||
typedef bp_match_behavior (*bp_match_callback)(match_t *m, int matchnum, void *userdata);
|
|
||||||
int each_match(bp_match_callback fn, void *userdata, const char *start, const char *end, pat_t *pat, pat_t *defs, pat_t *skip, bool ignorecase);
|
|
||||||
bp_errhand_t set_match_error_handler(bp_errhand_t errhand);
|
|
||||||
|
|
||||||
|
__attribute__((nonnull))
|
||||||
|
void recycle_match(match_t **at_m);
|
||||||
|
size_t free_all_matches(void);
|
||||||
|
size_t recycle_all_matches(void);
|
||||||
|
bool next_match(match_t **m, const char *start, const char *end, pat_t *pat, pat_t *defs, pat_t *skip, bool ignorecase);
|
||||||
|
#define stop_matching(m) next_match(m, NULL, NULL, NULL, NULL, NULL, 0)
|
||||||
|
bool next_match_safe(match_t **m, const char *start, const char *end, pat_t *pat, pat_t *defs, pat_t *skip, bool ignorecase, bp_errhand_t errhand);
|
||||||
__attribute__((nonnull))
|
__attribute__((nonnull))
|
||||||
match_t *get_numbered_capture(match_t *m, int n);
|
match_t *get_numbered_capture(match_t *m, int n);
|
||||||
__attribute__((nonnull, pure))
|
__attribute__((nonnull, pure))
|
||||||
|
Loading…
Reference in New Issue
Block a user