diff options
| -rw-r--r-- | README.md | 1 | ||||
| -rw-r--r-- | bp.1 | 4 | ||||
| -rw-r--r-- | bpeg.c | 14 | ||||
| -rw-r--r-- | file_loader.c | 18 | ||||
| -rw-r--r-- | file_loader.h | 2 | ||||
| -rw-r--r-- | types.h | 1 | ||||
| -rw-r--r-- | vm.c | 39 | ||||
| -rw-r--r-- | vm.h | 4 |
8 files changed, 61 insertions, 22 deletions
@@ -10,6 +10,7 @@ It's written in pure C with no dependencies. * `-h` `--help` print the usage and quit * `-v` `--verbose` print verbose debugging info * `-i` `--ignore-case` perform a case-insensitive match +* `-I` `--inplace` perform replacements or filtering in-place on files * `-e` `--explain` print an explanation of the matches * `-j` `--json` print matches as JSON objects * `-l` `--list-files` print only filenames containing matches @@ -11,6 +11,7 @@ bp \- Bruce's Parsing Expression Grammar tool [\fI-j\fR|\fI--json\fR] [\fI-l\fR|\fI--list-files\fR] [\fI-i\fR|\fI--ignore-case\fR \fI<pattern>\fR] +[\fI-I\fR|\fI--inplace\fR] [\fI-p\fR|\fI--pattern\fR \fI<pattern>\fR] [\fI-P\fR|\fI--pattern-string\fR \fI<string-pattern>\fR] [\fI-d\fR|\fI--define\fR \fI<name>\fR:\fI<pattern>\fR] @@ -38,6 +39,9 @@ Print only the names of files containing matches instead of the matches themselv .B \-i\fR, \fB--ignore-case Perform pattern matching case-insensitively. +.B \-I\fR, \fB--inplace +Perform filtering or replacement in-place (i.e. overwrite files with new content). + .B \-d\fR, \fB--define \fI<name>\fR:\fI<pattern>\fR Define a grammar rule using a bp pattern. @@ -28,6 +28,7 @@ static const char *usage = ( " -v --verbose print verbose debugging info\n" " -e --explain explain the matches\n" " -j --json print matches as a list of JSON objects\n" + " -I --inplace modify a file in-place\n" " -i --ignore-case preform matching case-insensitively\n" " -l --list-files list filenames only\n" " -d --define <name>:<def> define a grammar rule\n" @@ -61,7 +62,7 @@ static int print_errors(file_t *f, match_t *m) int ret = 0; if (m->op->op == VM_CAPTURE && m->op->args.capture.name && streq(m->op->args.capture.name, "!")) { printf("\033[31;1m"); - print_match(f, m, print_options); + print_match(stdout, f, m, print_options); printf("\033[0m\n"); fprint_line(stdout, f, m->start, m->end, " "); return 1; @@ -76,6 +77,8 @@ static int run_match(grammar_t *g, const char *filename, vm_op_t *pattern, unsig static int printed_matches = 0; file_t *f = load_file(filename); check(f, "Could not open file: %s", filename); + if (flags & BPEG_INPLACE) // Need to do this before matching + intern_file(f); match_t *m = match(g, f, f->contents, pattern, flags); if (m && print_errors(f, m) > 0) _exit(1); @@ -96,6 +99,10 @@ static int run_match(grammar_t *g, const char *filename, vm_op_t *pattern, unsig 0, f->end - f->contents); json_match(f->contents, m, (flags & BPEG_VERBOSE) ? 1 : 0); printf("]}}\n"); + } else if (flags & BPEG_INPLACE) { + FILE *out = fopen(filename, "w"); + print_match(out, f, m, 0); + fclose(out); } else { if (printed_matches > 1) fputc('\n', stdout); @@ -105,7 +112,7 @@ static int run_match(grammar_t *g, const char *filename, vm_op_t *pattern, unsig else printf("%s:\n", filename); } - print_match(f, m, print_options); + print_match(stdout, f, m, print_options); } destroy_file(&f); return 0; @@ -149,6 +156,8 @@ int main(int argc, char *argv[]) flags |= BPEG_EXPLAIN; } else if (streq(argv[i], "--json")) { flags |= BPEG_JSON; + } else if (streq(argv[i], "--inplace")) { + flags |= BPEG_INPLACE; } else if (streq(argv[i], "--ignore-case")) { flags |= BPEG_IGNORECASE; } else if (streq(argv[i], "--list-files")) { @@ -215,6 +224,7 @@ int main(int argc, char *argv[]) case 'v': flags |= BPEG_VERBOSE; break; // -v case 'e': flags |= BPEG_EXPLAIN; break; // -e case 'j': flags |= BPEG_JSON; break; // -j + case 'I': flags |= BPEG_INPLACE; break; // -I case 'i': flags |= BPEG_IGNORECASE; break; // -i case 'l': flags |= BPEG_LISTFILES; break; // -l default: diff --git a/file_loader.c b/file_loader.c index 72cea72..1f61953 100644 --- a/file_loader.c +++ b/file_loader.c @@ -89,6 +89,24 @@ file_t *spoof_file(const char *filename, char *text) return f; } +/* + * Ensure that the file's contents are held in memory, rather than being memory + * mapped IO. + */ +void intern_file(file_t *f) +{ + if (!f->mmapped) return; + size_t size = (size_t)(f->end - f->contents); + char *buf = xcalloc(sizeof(char), size + 1); + memcpy(buf, f->contents, size); + munmap(f->contents, size); + f->contents = buf; + f->end = buf + size; + f->mmapped = 0; + free(f->lines); + populate_lines(f); +} + void destroy_file(file_t **f) { if ((*f)->filename) { diff --git a/file_loader.h b/file_loader.h index d6d305a..28a6281 100644 --- a/file_loader.h +++ b/file_loader.h @@ -16,6 +16,8 @@ typedef struct { file_t *load_file(const char *filename); file_t *spoof_file(const char *filename, char *text); __attribute__((nonnull)) +void intern_file(file_t *f); +__attribute__((nonnull)) void destroy_file(file_t **f); __attribute__((pure, nonnull)) size_t get_line_number(file_t *f, const char *p); @@ -14,6 +14,7 @@ enum BPEGFlag { BPEG_EXPLAIN = 1 << 2, BPEG_JSON = 1 << 3, BPEG_LISTFILES = 1 << 4, + BPEG_INPLACE = 1 << 5, }; /* @@ -3,6 +3,7 @@ */ #include <ctype.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> @@ -489,20 +490,20 @@ typedef struct { const char *color; } print_state_t; -static void print_line_number(print_state_t *state, print_options_t options) +static void print_line_number(FILE *out, print_state_t *state, print_options_t options) { state->printed_line = state->line; if (!(options & PRINT_LINE_NUMBERS)) return; if (options & PRINT_COLOR) - printf("\033[0;2m% 5ld\033(0\x78\033(B%s", state->line, state->color); + fprintf(out, "\033[0;2m% 5ld\033(0\x78\033(B%s", state->line, state->color); else - printf("% 5ld|", state->line); + fprintf(out, "% 5ld|", state->line); } /* * Print a match with replacements and highlighting. */ -static void _print_match(file_t *f, match_t *m, print_state_t *state, print_options_t options) +static void _print_match(FILE *out, file_t *f, match_t *m, print_state_t *state, print_options_t options) { static const char *hl = "\033[0;31;1m"; const char *old_color = state->color; @@ -514,7 +515,7 @@ static void _print_match(file_t *f, match_t *m, print_state_t *state, print_opti } else if (m->op->op == VM_REPLACE) { if (options & PRINT_COLOR && state->color != hl) { state->color = hl; - printf("%s", state->color); + fprintf(out, "%s", state->color); } const char *text = m->op->args.replace.text; const char *end = &text[m->op->args.replace.len]; @@ -523,7 +524,7 @@ static void _print_match(file_t *f, match_t *m, print_state_t *state, print_opti ++r; match_t *cap = get_cap(m, &r); if (cap != NULL) { - _print_match(f, cap, state, options); + _print_match(out, f, cap, state, options); continue; } else { --r; @@ -531,21 +532,21 @@ static void _print_match(file_t *f, match_t *m, print_state_t *state, print_opti } if (state->printed_line != state->line) - print_line_number(state, options); + print_line_number(out, state, options); if (*r == '\\') { ++r; unsigned char c = unescapechar(r, &r); - fputc(c, stdout); + fputc(c, out); if (c == '\n') ++state->line; continue; } else if (*r == '\n') { - fputc('\n', stdout); + fputc('\n', out); ++state->line; ++r; continue; } else { - fputc(*r, stdout); + fputc(*r, out); ++r; continue; } @@ -554,7 +555,7 @@ static void _print_match(file_t *f, match_t *m, print_state_t *state, print_opti if (m->op->op == VM_CAPTURE) { if (options & PRINT_COLOR && state->color != hl) { state->color = hl; - printf("%s", state->color); + fprintf(out, "%s", state->color); } } @@ -567,33 +568,33 @@ static void _print_match(file_t *f, match_t *m, print_state_t *state, print_opti if (child->start > prev) { for (const char *p = prev; p < child->start; ++p) { if (state->printed_line != state->line) - print_line_number(state, options); - fputc(*p, stdout); + print_line_number(out, state, options); + fputc(*p, out); if (*p == '\n') ++state->line; } } - _print_match(f, child, state, options); + _print_match(out, f, child, state, options); prev = child->end; } if (m->end > prev) { for (const char *p = prev; p < m->end; ++p) { if (state->printed_line != state->line) - print_line_number(state, options); - fputc(*p, stdout); + print_line_number(out, state, options); + fputc(*p, out); if (*p == '\n') ++state->line; } } } if (options & PRINT_COLOR && old_color != state->color) { - printf("%s", old_color); + fprintf(out, "%s", old_color); state->color = old_color; } } -void print_match(file_t *f, match_t *m, print_options_t options) +void print_match(FILE *out, file_t *f, match_t *m, print_options_t options) { print_state_t state = {.line = 1, .color = "\033[0m"}; - _print_match(f, m, &state, options); + _print_match(out, f, m, &state, options); } static match_t *match_backref(const char *str, vm_op_t *op, match_t *cap, unsigned int flags) @@ -4,6 +4,8 @@ #ifndef VM__H #define VM__H +#include <stdio.h> + #include "types.h" typedef enum { @@ -17,7 +19,7 @@ match_t *match(grammar_t *g, file_t *f, const char *str, vm_op_t *op, unsigned i __attribute__((nonnull)) void destroy_match(match_t **m); __attribute__((nonnull)) -void print_match(file_t *f, match_t *m, print_options_t options); +void print_match(FILE *out, file_t *f, match_t *m, print_options_t options); #endif // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1 |
