Moving print logic out of match.c and renaming explain -> printmatch
This commit is contained in:
parent
c51a91c470
commit
326a0b960c
2
Makefile
2
Makefile
@ -22,7 +22,7 @@ G=
|
|||||||
O=-O3
|
O=-O3
|
||||||
ALL_FLAGS=$(CFLAGS) $(OSFLAGS) -DBP_NAME="\"$(NAME)\"" $(EXTRA) $(CWARN) $(G) $(O)
|
ALL_FLAGS=$(CFLAGS) $(OSFLAGS) -DBP_NAME="\"$(NAME)\"" $(EXTRA) $(CWARN) $(G) $(O)
|
||||||
|
|
||||||
CFILES=pattern.c utils.c match.c files.c explain.c json.c utf8.c
|
CFILES=pattern.c utils.c match.c files.c printmatch.c json.c utf8.c
|
||||||
OBJFILES=$(CFILES:.c=.o)
|
OBJFILES=$(CFILES:.c=.o)
|
||||||
|
|
||||||
all: $(NAME) bp.1
|
all: $(NAME) bp.1
|
||||||
|
@ -140,11 +140,11 @@ so use at your own risk! These grammar files are only approximations of syntax.
|
|||||||
File | Description
|
File | Description
|
||||||
-------------------------------|-----------------------------------------------------
|
-------------------------------|-----------------------------------------------------
|
||||||
[bp.c](bp.c) | The main program.
|
[bp.c](bp.c) | The main program.
|
||||||
[explain.c](explain.c) | Printing a visual explanation of a match.
|
|
||||||
[files.c](files.c) | Loading files into memory.
|
[files.c](files.c) | Loading files into memory.
|
||||||
[json.c](json.c) | JSON output of matches.
|
[json.c](json.c) | JSON output of matches.
|
||||||
[match.c](match.c) | Pattern matching code (find occurrences of a bp pattern within an input string).
|
[match.c](match.c) | Pattern matching code (find occurrences of a bp pattern within an input string).
|
||||||
[pattern.c](pattern.c) | Pattern compiling code (compile a bp pattern from an input string).
|
[pattern.c](pattern.c) | Pattern compiling code (compile a bp pattern from an input string).
|
||||||
|
[printmatch.c](printmatch.c) | Printing a visual explanation of a match.
|
||||||
[utf8.c](utf8.c) | UTF-8 helper code.
|
[utf8.c](utf8.c) | UTF-8 helper code.
|
||||||
[utils.c](utils.c) | Miscellaneous helper functions.
|
[utils.c](utils.c) | Miscellaneous helper functions.
|
||||||
|
|
||||||
|
2
bp.c
2
bp.c
@ -20,11 +20,11 @@
|
|||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "explain.h"
|
|
||||||
#include "files.h"
|
#include "files.h"
|
||||||
#include "json.h"
|
#include "json.h"
|
||||||
#include "match.h"
|
#include "match.h"
|
||||||
#include "pattern.h"
|
#include "pattern.h"
|
||||||
|
#include "printmatch.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#ifndef BP_NAME
|
#ifndef BP_NAME
|
||||||
|
13
explain.h
13
explain.h
@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// Debug visualization of matches
|
|
||||||
//
|
|
||||||
#ifndef EXPLAIN__H
|
|
||||||
#define EXPLAIN__H
|
|
||||||
|
|
||||||
#include "match.h"
|
|
||||||
|
|
||||||
__attribute__((nonnull))
|
|
||||||
void explain_match(match_t *m);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
|
87
match.c
87
match.c
@ -857,91 +857,4 @@ match_t *get_named_capture(match_t *m, const char *name, size_t namelen)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void fputc_safe(FILE *out, char c, print_options_t *opts)
|
|
||||||
{
|
|
||||||
(void)fputc(c, out);
|
|
||||||
if (c == '\n' && opts && opts->on_nl) {
|
|
||||||
opts->on_nl(out);
|
|
||||||
if (opts->replace_color) fprintf(out, "%s", opts->replace_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fprint_match(FILE *out, const char *file_start, match_t *m, print_options_t *opts)
|
|
||||||
{
|
|
||||||
if (m->pat->type == BP_REPLACE) {
|
|
||||||
const char *text = m->pat->args.replace.text;
|
|
||||||
const char *end = &text[m->pat->args.replace.len];
|
|
||||||
if (opts && opts->replace_color) fprintf(out, "%s", opts->replace_color);
|
|
||||||
|
|
||||||
// TODO: clean up the line numbering code
|
|
||||||
for (const char *r = text; r < end; ) {
|
|
||||||
// Capture substitution
|
|
||||||
if (*r == '@' && r+1 < end && r[1] != '@') {
|
|
||||||
const char *next = r+1;
|
|
||||||
// Retrieve the capture value:
|
|
||||||
match_t *cap = NULL;
|
|
||||||
if (isdigit(*next)) {
|
|
||||||
int n = (int)strtol(next, (char**)&next, 10);
|
|
||||||
cap = get_numbered_capture(m->children[0], n);
|
|
||||||
} else {
|
|
||||||
const char *name = next, *name_end = after_name(next, end);
|
|
||||||
if (name_end) {
|
|
||||||
cap = get_named_capture(m->children[0], name, (size_t)(name_end - name));
|
|
||||||
next = name_end;
|
|
||||||
if (next < m->end && *next == ';') ++next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cap != NULL) {
|
|
||||||
fprint_match(out, file_start, cap, opts);
|
|
||||||
if (opts && opts->replace_color) fprintf(out, "%s", opts->replace_color);
|
|
||||||
r = next;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*r == '\\') {
|
|
||||||
++r;
|
|
||||||
if (*r == 'N') { // \N (nodent)
|
|
||||||
++r;
|
|
||||||
// Mildly hacky: nodents here are based on the *first line*
|
|
||||||
// of the match. If the match spans multiple lines, or if
|
|
||||||
// the replacement text contains newlines, this may get weird.
|
|
||||||
const char *line_start = m->start;
|
|
||||||
while (line_start > file_start && line_start[-1] != '\n') --line_start;
|
|
||||||
fputc_safe(out, '\n', opts);
|
|
||||||
for (const char *p = line_start; p < m->start && (*p == ' ' || *p == '\t'); ++p)
|
|
||||||
fputc(*p, out);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
fputc_safe(out, unescapechar(r, &r, end), opts);
|
|
||||||
} else {
|
|
||||||
fputc_safe(out, *r, opts);
|
|
||||||
++r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (opts && opts->match_color) fprintf(out, "%s", opts->match_color);
|
|
||||||
const char *prev = m->start;
|
|
||||||
for (int i = 0; m->children && m->children[i]; i++) {
|
|
||||||
match_t *child = m->children[i];
|
|
||||||
// Skip children from e.g. zero-width matches like >@foo
|
|
||||||
if (!(prev <= child->start && child->start <= m->end &&
|
|
||||||
prev <= child->end && child->end <= m->end))
|
|
||||||
continue;
|
|
||||||
if (child->start > prev) {
|
|
||||||
if (opts && opts->fprint_between) opts->fprint_between(out, prev, child->start, opts->match_color);
|
|
||||||
else fwrite(prev, sizeof(char), (size_t)(child->start - prev), out);
|
|
||||||
}
|
|
||||||
fprint_match(out, file_start, child, opts);
|
|
||||||
if (opts && opts->match_color) fprintf(out, "%s", opts->match_color);
|
|
||||||
prev = child->end;
|
|
||||||
}
|
|
||||||
if (m->end > prev) {
|
|
||||||
if (opts && opts->fprint_between) opts->fprint_between(out, prev, m->end, opts->match_color);
|
|
||||||
else fwrite(prev, sizeof(char), (size_t)(m->end - prev), out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||||
|
9
match.h
9
match.h
@ -24,12 +24,6 @@ typedef struct match_s {
|
|||||||
struct match_s *_children[3];
|
struct match_s *_children[3];
|
||||||
} match_t;
|
} match_t;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const char *normal_color, *match_color, *replace_color;
|
|
||||||
void (*fprint_between)(FILE *out, const char *start, const char *end, const char *normal_color);
|
|
||||||
void (*on_nl)(FILE *out);
|
|
||||||
} print_options_t;
|
|
||||||
|
|
||||||
__attribute__((nonnull))
|
__attribute__((nonnull))
|
||||||
void recycle_match(match_t **at_m);
|
void recycle_match(match_t **at_m);
|
||||||
size_t free_all_matches(void);
|
size_t free_all_matches(void);
|
||||||
@ -40,9 +34,6 @@ __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))
|
||||||
match_t *get_named_capture(match_t *m, const char *name, size_t namelen);
|
match_t *get_named_capture(match_t *m, const char *name, size_t namelen);
|
||||||
__attribute__((nonnull(1,2,3)))
|
|
||||||
//void fprint_match(FILE *out, const char *file_start, match_t *m, const char *colors[3]);
|
|
||||||
void fprint_match(FILE *out, const char *file_start, match_t *m, print_options_t *opts);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
//
|
//
|
||||||
// explain.c - Debug visualization of pattern matches.
|
// printmatch.c - Debug visualization of pattern matches.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "match.h"
|
#include "match.h"
|
||||||
|
#include "printmatch.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
typedef struct match_node_s {
|
typedef struct match_node_s {
|
||||||
@ -170,4 +172,92 @@ void explain_match(match_t *m)
|
|||||||
printf("\033[?7h"); // Re-enable line wrapping
|
printf("\033[?7h"); // Re-enable line wrapping
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void fputc_safe(FILE *out, char c, print_options_t *opts)
|
||||||
|
{
|
||||||
|
(void)fputc(c, out);
|
||||||
|
if (c == '\n' && opts && opts->on_nl) {
|
||||||
|
opts->on_nl(out);
|
||||||
|
if (opts->replace_color) fprintf(out, "%s", opts->replace_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fprint_match(FILE *out, const char *file_start, match_t *m, print_options_t *opts)
|
||||||
|
{
|
||||||
|
if (m->pat->type == BP_REPLACE) {
|
||||||
|
const char *text = m->pat->args.replace.text;
|
||||||
|
const char *end = &text[m->pat->args.replace.len];
|
||||||
|
if (opts && opts->replace_color) fprintf(out, "%s", opts->replace_color);
|
||||||
|
|
||||||
|
// TODO: clean up the line numbering code
|
||||||
|
for (const char *r = text; r < end; ) {
|
||||||
|
// Capture substitution
|
||||||
|
if (*r == '@' && r+1 < end && r[1] != '@') {
|
||||||
|
const char *next = r+1;
|
||||||
|
// Retrieve the capture value:
|
||||||
|
match_t *cap = NULL;
|
||||||
|
if (isdigit(*next)) {
|
||||||
|
int n = (int)strtol(next, (char**)&next, 10);
|
||||||
|
cap = get_numbered_capture(m->children[0], n);
|
||||||
|
} else {
|
||||||
|
const char *name = next, *name_end = after_name(next, end);
|
||||||
|
if (name_end) {
|
||||||
|
cap = get_named_capture(m->children[0], name, (size_t)(name_end - name));
|
||||||
|
next = name_end;
|
||||||
|
if (next < m->end && *next == ';') ++next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cap != NULL) {
|
||||||
|
fprint_match(out, file_start, cap, opts);
|
||||||
|
if (opts && opts->replace_color) fprintf(out, "%s", opts->replace_color);
|
||||||
|
r = next;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*r == '\\') {
|
||||||
|
++r;
|
||||||
|
if (*r == 'N') { // \N (nodent)
|
||||||
|
++r;
|
||||||
|
// Mildly hacky: nodents here are based on the *first line*
|
||||||
|
// of the match. If the match spans multiple lines, or if
|
||||||
|
// the replacement text contains newlines, this may get weird.
|
||||||
|
const char *line_start = m->start;
|
||||||
|
while (line_start > file_start && line_start[-1] != '\n') --line_start;
|
||||||
|
fputc_safe(out, '\n', opts);
|
||||||
|
for (const char *p = line_start; p < m->start && (*p == ' ' || *p == '\t'); ++p)
|
||||||
|
fputc(*p, out);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fputc_safe(out, unescapechar(r, &r, end), opts);
|
||||||
|
} else {
|
||||||
|
fputc_safe(out, *r, opts);
|
||||||
|
++r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (opts && opts->match_color) fprintf(out, "%s", opts->match_color);
|
||||||
|
const char *prev = m->start;
|
||||||
|
for (int i = 0; m->children && m->children[i]; i++) {
|
||||||
|
match_t *child = m->children[i];
|
||||||
|
// Skip children from e.g. zero-width matches like >@foo
|
||||||
|
if (!(prev <= child->start && child->start <= m->end &&
|
||||||
|
prev <= child->end && child->end <= m->end))
|
||||||
|
continue;
|
||||||
|
if (child->start > prev) {
|
||||||
|
if (opts && opts->fprint_between) opts->fprint_between(out, prev, child->start, opts->match_color);
|
||||||
|
else fwrite(prev, sizeof(char), (size_t)(child->start - prev), out);
|
||||||
|
}
|
||||||
|
fprint_match(out, file_start, child, opts);
|
||||||
|
if (opts && opts->match_color) fprintf(out, "%s", opts->match_color);
|
||||||
|
prev = child->end;
|
||||||
|
}
|
||||||
|
if (m->end > prev) {
|
||||||
|
if (opts && opts->fprint_between) opts->fprint_between(out, prev, m->end, opts->match_color);
|
||||||
|
else fwrite(prev, sizeof(char), (size_t)(m->end - prev), out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
22
printmatch.h
Normal file
22
printmatch.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Debug visualization of matches
|
||||||
|
//
|
||||||
|
#ifndef EXPLAIN__H
|
||||||
|
#define EXPLAIN__H
|
||||||
|
|
||||||
|
#include "match.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char *normal_color, *match_color, *replace_color;
|
||||||
|
void (*fprint_between)(FILE *out, const char *start, const char *end, const char *normal_color);
|
||||||
|
void (*on_nl)(FILE *out);
|
||||||
|
} print_options_t;
|
||||||
|
__attribute__((nonnull(1,2,3)))
|
||||||
|
//void fprint_match(FILE *out, const char *file_start, match_t *m, const char *colors[3]);
|
||||||
|
void fprint_match(FILE *out, const char *file_start, match_t *m, print_options_t *opts);
|
||||||
|
|
||||||
|
__attribute__((nonnull))
|
||||||
|
void explain_match(match_t *m);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
|
Loading…
Reference in New Issue
Block a user