aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2021-09-23 20:51:04 -0700
committerBruce Hill <bruce@bruce-hill.com>2021-09-23 20:51:04 -0700
commit3a2492d584e3c7d4cada1386921d80ace33c1fd2 (patch)
treec644006290392edf5da2d3e85c41feef40d81f22
parentfddba4c54f3ce21e711cc2cd931ce5c4cad660b9 (diff)
WIP: working line breaks, formatting, etc. All seems functional, but a
bit messy
-rw-r--r--Lua/lbp.c4
-rw-r--r--bp.c99
-rw-r--r--match.c69
-rw-r--r--match.h11
-rw-r--r--print.c268
-rw-r--r--print.h29
-rw-r--r--utils.c2
7 files changed, 133 insertions, 349 deletions
diff --git a/Lua/lbp.c b/Lua/lbp.c
index 0c81188..6ce03a6 100644
--- a/Lua/lbp.c
+++ b/Lua/lbp.c
@@ -36,7 +36,7 @@ static void push_matchstring(lua_State *L, match_t *m)
char *buf = NULL;
size_t size = 0;
FILE *out = open_memstream(&buf, &size);
- fprint_match(out, m->start, m);
+ fprint_match(out, m->start, m, NULL);
fflush(out);
lua_pushlstring(L, buf, size);
fclose(out);
@@ -184,7 +184,7 @@ static int Lreplace(lua_State *L)
const char *prev = text;
for (match_t *m = NULL; next_match(&m, NULL, text, &text[textlen], maybe_pat.value.pat, NULL, false); ) {
fwrite(prev, sizeof(char), (size_t)(m->start - prev), out);
- fprint_match(out, text, m);
+ fprint_match(out, text, m, NULL);
prev = m->end;
++replacements;
}
diff --git a/bp.c b/bp.c
index 3acafe4..003d062 100644
--- a/bp.c
+++ b/bp.c
@@ -59,6 +59,10 @@ static const char *usage = (
// Used as a heuristic to check if a file is binary or text:
#define CHECK_FIRST_N_BYTES 256
+#define USE_DEFAULT_CONTEXT -3
+#define ALL_CONTEXT -2
+#define NO_CONTEXT -1
+
// Flag-configurable options:
static struct {
int context_before, context_after;
@@ -234,33 +238,85 @@ static void sig_handler(int sig)
if (kill(0, sig)) _exit(EXIT_FAILURE);
}
-void fprint_between(FILE *out, file_t *f, const char *prev, const char *next)
+void fprint_linenum(FILE *out, file_t *f, int linenum, const char *normal_color)
+{
+ switch (options.format) {
+ case FORMAT_FANCY: case FORMAT_PLAIN: {
+ int space = 0;
+ for (int i = (int)f->nlines; i > 0; i /= 10) ++space;
+ if (options.format == FORMAT_FANCY)
+ fprintf(out, "\033[0;2m%*d\033(0\x78\033(B%s", space, linenum, normal_color ? normal_color : "");
+ else fprintf(out, "%*d|", space, linenum);
+ break;
+ }
+ case FORMAT_FILE_LINE: {
+ fprintf(out, "%s:%d:", f->filename, linenum);
+ break;
+ }
+ default: break;
+ }
+}
+
+static file_t *printing_file = NULL;
+static void _fprint_between(FILE *out, const char *start, const char *end, const char *normal_color)
{
+ if (!end) end = printing_file->end;
+ while (start < end) {
+ if (!start) start = printing_file->start;
+ if (start == printing_file->start || start[-1] == '\n') {
+ int linenum = (int)get_line_number(printing_file, start);
+ fprint_linenum(out, printing_file, linenum, normal_color);
+ }
+ const char *line_end = memchr(start, '\n', (size_t)(end - start));
+ if (line_end && line_end != end) {
+ fwrite(start, sizeof(char), (size_t)(line_end - start + 1), out);
+ start = line_end + 1;
+ } else {
+ fwrite(start, sizeof(char), (size_t)(end - start), out);
+ break;
+ }
+ }
+}
+
+static void on_nl(FILE *out)
+{
+ switch (options.format) {
+ case FORMAT_FANCY: case FORMAT_PLAIN:
+ for (int i = (int)printing_file->nlines; i > 0; i /= 10) fputc('.', out);
+ fprintf(out, "%s", options.format == FORMAT_FANCY ? "\033[0;2m\033(0\x78\033(B\033[m" : "|");
+ break;
+ default: break;
+ }
+}
+
+static void fprint_context_between(FILE *out, const char *prev, const char *next)
+{
+ if (!prev && !next) return;
// if (options.context_before == NO_CONTEXT && options.context_after == NO_CONTEXT)
// return;
if (options.context_before == ALL_CONTEXT || options.context_after == ALL_CONTEXT) {
- fwrite(prev ? prev : f->start, sizeof(char), (size_t)((next ? next : f->end) - (prev ? prev : f->start)), out);
+ _fprint_between(out, prev, next, "\033[m");
return;
}
const char *after_prev = prev, *before_next = next;
if (prev && options.context_after >= 0) {
- size_t after_prev_line = get_line_number(f, prev) + (size_t)options.context_after + 1;
- after_prev = get_line(f, after_prev_line > f->nlines ? f->nlines : after_prev_line);
+ size_t after_prev_line = get_line_number(printing_file, prev) + (size_t)options.context_after + 1;
+ after_prev = after_prev_line > printing_file->nlines ? printing_file->end : get_line(printing_file, after_prev_line > printing_file->nlines ? printing_file->nlines : after_prev_line);
}
if (next && options.context_before >= 0) {
- size_t before_next_line = get_line_number(f, next);
+ size_t before_next_line = get_line_number(printing_file, next);
before_next_line = options.context_before >= (int)before_next_line ? 1 : before_next_line - (size_t)options.context_before;
- before_next = get_line(f, before_next_line);
+ before_next = get_line(printing_file, before_next_line);
}
if (!prev) {
- fwrite(before_next, sizeof(char), (size_t)(next - before_next), out);
+ _fprint_between(out, before_next, next, "\033[m");
} else if (!next) {
- fwrite(prev, sizeof(char), (size_t)(after_prev - prev), out);
+ _fprint_between(out, prev, after_prev, "\033[m");
} else if (after_prev >= before_next) {
- fwrite(prev, sizeof(char), (size_t)(next - prev), out);
+ _fprint_between(out, prev, next, "\033[m");
} else {
- fwrite(prev, sizeof(char), (size_t)(after_prev - prev), out);
- fwrite(before_next, sizeof(char), (size_t)(next - before_next), out);
+ _fprint_between(out, prev, after_prev, "\033[m");
+ _fprint_between(out, before_next, next, "\033[m");
}
}
@@ -272,17 +328,32 @@ static int print_matches(FILE *out, def_t *defs, file_t *f, pat_t *pattern)
static int printed_filenames = 0;
int matches = 0;
const char *prev = NULL;
+
+ printing_file = f;
+
+ print_options_t print_opts = {.fprint_between = _fprint_between, .on_nl = on_nl};
+ if (options.format == FORMAT_FANCY) {
+ print_opts.match_color = "\033[0;31;1m";
+ print_opts.replace_color = "\033[0;34;1m";
+ print_opts.normal_color = "\033[m";
+ }
for (match_t *m = NULL; next_match(&m, defs, f->start, f->end, pattern, options.skip, options.ignorecase); ) {
if (++matches == 1 && options.print_filenames) {
if (printed_filenames++ > 0) printf("\n");
fprint_filename(out, f->filename);
}
- fprint_between(out, f, prev, m->start);
- fprint_match(out, f->start, m);
+ fprint_context_between(out, prev, m->start);
+ if (print_opts.normal_color) fprintf(out, "%s", print_opts.normal_color);
+ if (m->start == f->start || m->start[-1] == '\n')
+ fprint_linenum(out, f, (int)get_line_number(f, m->start), 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;
}
if (matches > 0)
- fprint_between(out, f, prev, NULL);
+ fprint_context_between(out, prev, NULL);
+
+ printing_file = NULL;
return matches;
}
diff --git a/match.c b/match.c
index 29db8c6..870b4d0 100644
--- a/match.c
+++ b/match.c
@@ -844,37 +844,46 @@ match_t *get_named_capture(match_t *m, const char *name, size_t namelen)
return NULL;
}
-void fprint_match(FILE *out, const char *file_start, match_t *m)
+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] != '@') {
- ++r;
-
+ const char *next = r+1;
// Retrieve the capture value:
match_t *cap = NULL;
- if (isdigit(*r)) {
- int n = (int)strtol(r, (char**)&r, 10);
+ if (isdigit(*next)) {
+ int n = (int)strtol(next, (char**)&next, 10);
cap = get_numbered_capture(m->children[0], n);
} else {
- const char *name = r, *end = after_name(r);
+ const char *name = next, *end = after_name(next);
if (end > name) {
cap = get_named_capture(m->children[0], name, (size_t)(end - name));
- r = end;
- if (r < m->end && *r == ';') ++r;
+ next = end;
+ if (next < m->end && *next == ';') ++next;
}
}
if (cap != NULL) {
- fprint_match(out, file_start, cap);
+ fprint_match(out, file_start, cap, opts);
+ if (opts && opts->replace_color) fprintf(out, "%s", opts->replace_color);
+ r = next;
continue;
- } else {
- --r;
}
}
@@ -887,30 +896,19 @@ void fprint_match(FILE *out, const char *file_start, match_t *m)
// 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;
- char denter = *line_start == '\t' ? '\t' : ' ';
- fputc('\n', out);
- if (denter == ' ' || denter == '\t') {
- for (const char *p = line_start; p && *p == denter && p < m->start; ++p)
- fputc(denter, out);
- }
+ fputc_safe(out, '\n', opts);
+ for (const char *p = line_start; p < m->start && (*p == ' ' || *p == '\t'); ++p)
+ fputc(*p, out);
continue;
}
- const char *start = r;
- char c = unescapechar(r, &r);
- if (r > start) (void)fputc(c, out);
- else (void)fputc('\\', out);
- continue;
- } else if (*r == '\n') {
- (void)fputc('\n', out);
- ++r;
- continue;
+ fputc_safe(out, unescapechar(r, &r), opts);
} else {
- (void)fputc(*r, out);
+ fputc_safe(out, *r, opts);
++r;
- continue;
}
}
} 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];
@@ -918,13 +916,18 @@ void fprint_match(FILE *out, const char *file_start, match_t *m)
if (!(prev <= child->start && child->start <= m->end &&
prev <= child->end && child->end <= m->end))
continue;
- if (child->start > prev)
- fwrite(prev, sizeof(char), (size_t)(child->start - prev), out);
- fprint_match(out, file_start, child);
+ 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)
- fwrite(prev, sizeof(char), (size_t)(m->end - prev), out);
+ 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);
+ }
}
}
diff --git a/match.h b/match.h
index 15578e0..7cb1c59 100644
--- a/match.h
+++ b/match.h
@@ -33,6 +33,12 @@ typedef struct match_s {
struct match_s *_children[3];
} 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__((returns_nonnull))
match_t *new_match(def_t *defs, pat_t *pat, const char *start, const char *end, match_t *children[]);
__attribute__((nonnull))
@@ -45,8 +51,9 @@ __attribute__((nonnull))
match_t *get_numbered_capture(match_t *m, int n);
__attribute__((nonnull, pure))
match_t *get_named_capture(match_t *m, const char *name, size_t namelen);
-__attribute__((nonnull))
-void fprint_match(FILE *out, const char *file_start, match_t *m);
+__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
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/print.c b/print.c
index a5c27af..e69de29 100644
--- a/print.c
+++ b/print.c
@@ -1,268 +0,0 @@
-//
-// print.c - Code for printing and visualizing matches.
-//
-
-#include <ctype.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "files.h"
-#include "match.h"
-#include "print.h"
-#include "utils.h"
-
-static const char *color_match = "\033[0;31;1m";
-static const char *color_replace = "\033[0;34;1m";
-static const char *color_normal = "\033[m";
-static const char *current_color = NULL;
-
-//
-// Print a line number, if it needs to be printed.
-// In the lineformat string, replace "@" with the filename, and "#" with the line number.
-//
-__attribute__((nonnull(1,2)))
-static inline void print_line_number(FILE *out, printer_t *pr, size_t line_number, const char *color, int is_line_continued)
-{
- if (!pr->needs_line_number) return;
- for (const char *c = pr->lineformat; c && *c; c++) {
- if (*c == '@') { // Print filename
- fprintf(out, "%s", pr->file->filename);
- } else if (*c == '#') { // Print line number
- const char *after;
- int space = (int)strtol(c+1, (char**)&after, 10);
- if (after > c+1) c = after-1; // Width was specified
- else // Otherwise default to "wide enough for every line number in this file"
- for (int i = (int)pr->file->nlines; i > 0; i /= 10) ++space;
-
- if (is_line_continued) fprintf(out, "%*s", abs(space), "");
- else fprintf(out, "%*lu", space, line_number);
- } else fputc(*c, out);
- }
- if (color) {
- fprintf(out, "%s", color);
- current_color = color;
- }
- pr->needs_line_number = 0;
-}
-
-//
-// Print a range of text from a file, adding line numbers if necessary.
-//
-__attribute__((nonnull(1,2,3,4)))
-static void print_between(FILE *out, printer_t *pr, const char *start, const char *end, const char *color)
-{
- file_t *f = pr->file;
- while (start < end) {
- size_t line_num = f ? get_line_number(f, start) : 0;
- print_line_number(out, pr, line_num, color, 0);
- 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);
- current_color = color;
- }
- fwrite(start, sizeof(char), (size_t)(eol-start), out);
- if (eol[-1] == '\n')
- pr->needs_line_number = 1;
- start = eol;
- }
- pr->pos = end;
-}
-
-//
-// Return a pointer to the first character of context information before `pos`,
-// according to the context settings in `pr`
-//
-static const char *context_before(printer_t *pr, const char *pos)
-{
- if (pr->context_before == ALL_CONTEXT) {
- return pr->pos;
- } else if (pr->context_before >= 0) {
- size_t n = get_line_number(pr->file, pos);
- if (n >= (size_t)(pr->context_before + 1))
- n -= (size_t)pr->context_before;
- else
- n = 1;
- const char *sol = get_line(pr->file, n);
- if (sol == NULL || sol < pr->pos) sol = pr->pos;
- return sol;
- } else {
- return pos;
- }
-}
-
-//
-// Return a pointer to the last character of context information after `pos`,
-// according to the context settings in `pr`
-//
-static const char *context_after(printer_t *pr, const char *pos)
-{
- if (pr->context_after == ALL_CONTEXT) {
- return pr->file->end;
- } else if (pr->context_after >= 0) {
- size_t n = get_line_number(pr->file, pos) + (size_t)pr->context_after;
- const char *eol = get_line(pr->file, n+1);
- return eol ? eol : pr->file->end;
- } else {
- return pos;
- }
-}
-
-//
-// Print the text of a match (no context).
-//
-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 = 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);
- current_color = color_replace;
- }
- const char *text = m->pat->args.replace.text;
- const char *end = &text[m->pat->args.replace.len];
-
- // TODO: clean up the line numbering code
- for (const char *r = text; r < end; ) {
- print_line_number(out, pr, line, pr->use_color ? color_replace : NULL, line > line_end);
-
- // Capture substitution
- if (*r == '@' && r+1 < end && r[1] != '@') {
- ++r;
-
- // Retrieve the capture value:
- match_t *cap = NULL;
- if (isdigit(*r)) {
- int n = (int)strtol(r, (char**)&r, 10);
- cap = get_numbered_capture(m->children[0], n);
- } else {
- const char *name = r, *end = after_name(r);
- if (end > name) {
- cap = get_named_capture(m->children[0], name, (size_t)(end - name));
- r = end;
- if (r < m->end && *r == ';') ++r;
- }
- }
-
- if (cap != NULL) {
- _print_match(out, pr, cap);
- if (pr->use_color && current_color != color_replace) {
- fprintf(out, "%s", color_replace);
- current_color = color_replace;
- }
- continue;
- } else {
- --r;
- }
- }
-
- 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 = get_line(
- pr->file, get_line_number(pr->file, m->start));
- char denter = line_start ? *line_start : '\t';
- fputc('\n', out);
- pr->needs_line_number = 1;
- print_line_number(out, pr, line, pr->use_color ? color_replace : NULL, 1);
- ++line;
- if (denter == ' ' || denter == '\t') {
- for (const char *p = line_start; p && *p == denter && p < m->start; ++p)
- fputc(denter, out);
- }
- continue;
- }
- const char *start = r;
- char c = unescapechar(r, &r);
- if (r > start) {
- (void)fputc(c, out);
- if (c == '\n') {
- ++line;
- pr->needs_line_number = 1;
- }
- } else (void)fputc('\\', out);
- continue;
- } else if (*r == '\n') {
- (void)fputc('\n', out);
- ++line;
- pr->needs_line_number = 1;
- ++r;
- continue;
- } else {
- (void)fputc(*r, out);
- ++r;
- continue;
- }
- }
- print_line_number(out, pr, line, pr->use_color ? color_normal : NULL, line > line_end);
- } else {
- 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)
- print_between(out, pr, prev, child->start, pr->use_color ? color_match : NULL);
- _print_match(out, pr, child);
- prev = child->end;
- }
- if (m->end > prev)
- print_between(out, pr, prev, m->end, pr->use_color ? color_match : NULL);
- }
- pr->pos = m->end;
-}
-
-//
-// Print the text of a match and any context.
-//
-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 ? pr->file->start : m->start;
- pr->needs_line_number = 1;
- }
- if (m) {
- const char *before_m = context_before(pr, m->start);
- if (!first) {
- // When not printing context lines, print each match on its own
- // line instead of jamming them all together:
- if (pr->context_before == NO_CONTEXT && pr->context_after == NO_CONTEXT && (!pr->needs_line_number || !pr->lineformat)) {
- fprintf(out, "\n");
- pr->needs_line_number = 1;
- }
-
- const char *after_last = context_after(pr, pr->pos);
- if (after_last >= before_m) {
- // Overlapping ranges:
- before_m = pr->pos;
- } else {
- // Non-overlapping ranges:
- print_between(out, pr, pr->pos, after_last, pr->use_color ? color_normal : NULL);
- if (pr->context_before > 0 || pr->context_after > 0)
- fprintf(out, "\n"); // Gap between chunks
- }
- }
- print_between(out, pr, before_m, m->start, pr->use_color ? color_normal : NULL);
- _print_match(out, pr, m);
- } else {
- // After the last match is printed, print the trailing context:
- const char *after_last = context_after(pr, pr->pos);
- print_between(out, pr, pr->pos, after_last, pr->use_color ? color_normal : NULL);
- }
- if (pr->use_color) fprintf(out, "%s", color_normal);
-}
-
-// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/print.h b/print.h
index b690d69..e69de29 100644
--- a/print.h
+++ b/print.h
@@ -1,29 +0,0 @@
-//
-// Header file for print.c (printing/visualizing matches)
-//
-#ifndef PRINT__H
-#define PRINT__H
-
-#include <stdbool.h>
-
-#include "files.h"
-#include "match.h"
-
-#define USE_DEFAULT_CONTEXT -3
-#define ALL_CONTEXT -2
-#define NO_CONTEXT -1
-
-typedef struct {
- file_t *file;
- const char *pos;
- int context_before, context_after;
- bool needs_line_number:1;
- bool use_color:1;
- const char *lineformat;
-} printer_t;
-
-__attribute__((nonnull(1,2)))
-void print_match(FILE *out, printer_t *pr, match_t *m);
-
-#endif
-// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/utils.c b/utils.c
index 9057df6..38d7f30 100644
--- a/utils.c
+++ b/utils.c
@@ -121,7 +121,7 @@ char unescapechar(const char *escaped, const char **end)
}
default:
if (end) *end = escaped;
- return (char)0;
+ return '\\';
}
if (end) *end = &escaped[len];
return (char)ret;