aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--bp.123
-rw-r--r--bp.1.md18
-rw-r--r--bp.c59
-rw-r--r--print.c27
-rw-r--r--print.h9
6 files changed, 85 insertions, 55 deletions
diff --git a/README.md b/README.md
index 8b52deb..402e36a 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,9 @@ It's written in pure C with no dependencies.
* `-p` `--pattern <pat>` provide a pattern (equivalent to `bp '\(<pat>)'`)
* `-r` `--replace <replacement>` replace the input pattern with the given replacement
* `-s` `--skip <skip pattern>` skip over the given pattern when looking for matches
-* `-C` `--context <N>` change how many lines of context are printed (`0`: no context, `all`: the whole file, `<N>` matching lines and `<N-1>` lines before/after)
+* `-B` `--context-before <N>` change how many lines of context are printed before each match
+* `-B` `--context-after <N>` change how many lines of context are printed after each match
+* `-C` `--context <N>` change how many lines of context are printed before and after each match
* `-g` `--grammar <grammar file>` use the specified file as a grammar
* `-G` `--git` get filenames from git
* `-f` `--format` `auto|plain|fancy` set the output format (`fancy` includes colors and line numbers)
diff --git a/bp.1 b/bp.1
index 2d884d8..d1ffb7a 100644
--- a/bp.1
+++ b/bp.1
@@ -62,15 +62,22 @@ Use \f[B]git\f[R] to get a list of files.
Remaining file arguments (if any) are passed to \f[B]git --ls-files\f[R]
instead of treated as literal files.
.TP
+\f[B]-B\f[R], \f[B]--context-before\f[R] \f[I]N\f[R]
+The number of lines of context to print before each match (default: 0).
+See \f[B]--context\f[R] below for details on \f[B]none\f[R] or
+\f[B]all\f[R].
+.TP
+\f[B]-A\f[R], \f[B]--context-after\f[R] \f[I]N\f[R]
+The number of lines of context to print after each match (default: 0).
+See \f[B]--context\f[R] below for details on \f[B]none\f[R] or
+\f[B]all\f[R].
+.TP
\f[B]-C\f[R], \f[B]--context\f[R] \f[I]N\f[R]
-The number of lines of context to print.
-If \f[I]N\f[R] is 0, print only the exact text of the matches.
-If \f[I]N\f[R] is \f[B]\[lq]all\[rq]\f[R], print the entire file.
-Otherwise, if \f[I]N\f[R] is a positive integer, print the whole line on
-which matches occur, as well as the \f[I]N-1\f[R] lines before and after
-the match.
-The default value for this argument is \f[B]1\f[R] (print whole lines
-where matches occur).
+The number of lines to print before and after each match (default: 0).
+If \f[I]N\f[R] is \f[B]none\f[R], print only the exact text of the
+matches.
+If \f[I]N\f[R] is \f[B]\[lq]all\[rq]\f[R], print all text before and
+after each match.
.TP
\f[B]-f\f[R], \f[B]--format\f[R] \f[I]auto\f[R]|\f[I]fancy\f[R]|\f[I]plain\f[R]
Set the output format.
diff --git a/bp.1.md b/bp.1.md
index b5cc6d8..bf20d37 100644
--- a/bp.1.md
+++ b/bp.1.md
@@ -61,13 +61,19 @@ for more info.
: Use `git` to get a list of files. Remaining file arguments (if any) are
passed to `git --ls-files` instead of treated as literal files.
+`-B`, `--context-before` *N*
+: The number of lines of context to print before each match (default: 0). See
+`--context` below for details on `none` or `all`.
+
+`-A`, `--context-after` *N*
+: The number of lines of context to print after each match (default: 0). See
+`--context` below for details on `none` or `all`.
+
+
`-C`, `--context` *N*
-: The number of lines of context to print. If *N* is 0, print only the
-exact text of the matches. If *N* is **"all"**, print the entire file.
-Otherwise, if *N* is a positive integer, print the whole line on which
-matches occur, as well as the *N-1* lines before and after the match. The
-default value for this argument is **1** (print whole lines where matches
-occur).
+: The number of lines to print before and after each match (default: 0). If *N*
+is `none`, print only the exact text of the matches. If *N* is **"all"**, print
+all text before and after each match.
`-f`, `--format` *auto*\|*fancy*\|*plain*
: Set the output format. *fancy* includes colors and line numbers, *plain*
diff --git a/bp.c b/bp.c
index c8cfdaa..73df4e6 100644
--- a/bp.c
+++ b/bp.c
@@ -51,7 +51,9 @@ static const char *usage = (
" -w --word <string-pat> find words matching the given string pattern\n"
" -r --replace <replacement> replace the input pattern with the given replacement\n"
" -s --skip <skip-pattern> skip over the given pattern when looking for matches\n"
- " -C --context <context> set number of lines of context to print (all: the whole file, 0: only the match, 1: the line, N: N lines of context)\n"
+ " -B --context-before <n> set number of lines of context to print before the match\n"
+ " -B --context-after <n> set number of lines of context to print after the match\n"
+ " -C --context <context> set number of lines of context to print before and after the match\n"
" -f --format auto|fancy|plain set the output format\n"
" -g --grammar <grammar-file> use the specified file as a grammar");
@@ -60,17 +62,16 @@ static const char *usage = (
// Flag-configurable options:
typedef enum { CONFIRM_ASK, CONFIRM_ALL, CONFIRM_NONE } confirm_t;
-#define USE_DEFAULT_CONTEXT -2
-#define ALL_CONTEXT -1
static struct {
- int context_lines;
+ int context_before, context_after;
bool ignorecase, verbose, git_mode;
confirm_t confirm;
enum { MODE_NORMAL, MODE_LISTFILES, MODE_INPLACE, MODE_JSON, MODE_EXPLAIN } mode;
enum { FORMAT_AUTO, FORMAT_FANCY, FORMAT_PLAIN } format;
pat_t *skip;
} options = {
- .context_lines = USE_DEFAULT_CONTEXT,
+ .context_before = USE_DEFAULT_CONTEXT,
+ .context_after = USE_DEFAULT_CONTEXT,
.ignorecase = false,
.verbose = false,
.confirm = CONFIRM_ALL,
@@ -240,15 +241,17 @@ static void confirm_replacements(file_t *f, match_t *m, confirm_t *confirm)
}
{ // Print the original
- printer_t pr = {.file = f, .context_lines = options.context_lines,
+ printer_t pr = {.file = f, .context_before = options.context_before,
+ .context_after = options.context_after,
.use_color = true, .print_line_numbers = true};
print_match(tty_out, &pr, m->children[0]);
// Print trailing context lines:
print_match(tty_out, &pr, NULL);
}
- if (options.context_lines > 1) fprintf(tty_out, "\n");
+ if (options.context_before > 1 || options.context_after > 1) fprintf(tty_out, "\n");
{ // Print the replacement
- printer_t pr = {.file = f, .context_lines = options.context_lines,
+ printer_t pr = {.file = f, .context_before = options.context_before,
+ .context_after = options.context_after,
.use_color = true, .print_line_numbers = true};
print_match(tty_out, &pr, m);
// Print trailing context lines:
@@ -295,7 +298,8 @@ static int inplace_modify_file(def_t *defs, file_t *f, pat_t *pattern)
printer_t pr = {
.file = f,
- .context_lines = ALL_CONTEXT,
+ .context_before = ALL_CONTEXT,
+ .context_after = ALL_CONTEXT,
.use_color = false,
.print_line_numbers = false,
};
@@ -306,8 +310,7 @@ static int inplace_modify_file(def_t *defs, file_t *f, pat_t *pattern)
match_t *m = NULL;
while ((m = next_match(defs, f, m, pattern, options.skip, options.ignorecase))) {
++matches;
- printer_t err_pr = {.file = f, .context_lines = 1, .use_color = true, .print_line_numbers = true};
- if (print_errors(&err_pr, m) > 0)
+ if (print_errors(f, m) > 0)
exit(EXIT_FAILURE);
// Lazy-open file for writing upon first match:
if (dest == NULL) {
@@ -346,15 +349,15 @@ static int print_matches(def_t *defs, file_t *f, pat_t *pattern)
int matches = 0;
printer_t pr = {
.file = f,
- .context_lines = options.context_lines,
+ .context_before = options.context_before,
+ .context_after = options.context_after,
.use_color = options.format == FORMAT_FANCY,
.print_line_numbers = options.format == FORMAT_FANCY,
};
match_t *m = NULL;
while ((m = next_match(defs, f, m, pattern, options.skip, options.ignorecase))) {
- printer_t err_pr = {.file = f, .context_lines = 1, .use_color = true, .print_line_numbers = true};
- if (print_errors(&err_pr, m) > 0)
+ if (print_errors(f, m) > 0)
exit(EXIT_FAILURE);
if (++matches == 1) {
@@ -482,6 +485,16 @@ static int process_git_files(def_t *defs, pat_t *pattern, int argc, char *argv[]
return found;
}
+//
+// Convert a context string to an integer
+//
+static int context_from_flag(const char *flag)
+{
+ if (streq(flag, "all")) return ALL_CONTEXT;
+ if (streq(flag, "none")) return NO_CONTEXT;
+ return (int)strtol(flag, NULL, 10);
+}
+
#define FLAG(f) (flag = get_flag(argv, f, &argv))
#define BOOLFLAG(f) get_boolflag(argv, f, &argv)
@@ -567,15 +580,11 @@ int main(int argc, char *argv[])
}
options.skip = either_pat(arg_file, options.skip, s);
} else if (FLAG("-C") || FLAG("--context")) {
- if (streq(flag, "all")) {
- options.context_lines = ALL_CONTEXT;
- } else if (streq(flag, "none")) {
- options.context_lines = 0;
- } else {
- options.context_lines = (int)strtol(flag, &flag, 10);
- if (flag && flag[0])
- errx(EXIT_FAILURE, "Unsupported flags after --context: %s", flag);
- }
+ options.context_before = options.context_after = context_from_flag(flag);
+ } else if (FLAG("-B") || FLAG("--before-context")) {
+ options.context_before = context_from_flag(flag);
+ } else if (FLAG("-A") || FLAG("--after-context")) {
+ options.context_after = context_from_flag(flag);
} else if (FLAG("-f") || FLAG("--format")) {
options.format = streq(flag, "fancy") ? FORMAT_FANCY : streq(flag, "plain") ? FORMAT_PLAIN : FORMAT_AUTO;
} else if (argv[0][0] == '-' && argv[0][1] && argv[0][1] != '-') { // single-char flags
@@ -600,8 +609,8 @@ int main(int argc, char *argv[])
for (argc = 0; argv[argc]; ++argc) ; // update argc
- if (options.context_lines == USE_DEFAULT_CONTEXT) options.context_lines = 1;
- if (options.context_lines < 0 && options.context_lines != ALL_CONTEXT) options.context_lines = 0;
+ if (options.context_before == USE_DEFAULT_CONTEXT) options.context_before = 0;
+ if (options.context_after == USE_DEFAULT_CONTEXT) options.context_after = 0;
if (options.format == FORMAT_AUTO)
options.format = isatty(STDOUT_FILENO) ? FORMAT_FANCY : FORMAT_PLAIN;
diff --git a/print.c b/print.c
index 6cccba2..1a3f11b 100644
--- a/print.c
+++ b/print.c
@@ -7,6 +7,7 @@
#include <stdlib.h>
#include <string.h>
+#include "files.h"
#include "match.h"
#include "print.h"
#include "types.h"
@@ -69,12 +70,12 @@ static void print_between(FILE *out, printer_t *pr, const char *start, const cha
//
static const char *context_before(printer_t *pr, const char *pos)
{
- if (pr->context_lines == -1) {
+ if (pr->context_before == ALL_CONTEXT) {
return pr->pos;
- } else if (pr->context_lines > 0) {
+ } else if (pr->context_before >= 0) {
size_t n = get_line_number(pr->file, pos);
- if (n >= (size_t)((pr->context_lines - 1) + 1))
- n -= (size_t)(pr->context_lines - 1);
+ 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);
@@ -91,10 +92,10 @@ static const char *context_before(printer_t *pr, const char *pos)
//
static const char *context_after(printer_t *pr, const char *pos)
{
- if (pr->context_lines == -1) {
+ if (pr->context_after == ALL_CONTEXT) {
return pr->file->end;
- } else if (pr->context_lines > 0) {
- size_t n = get_line_number(pr->file, pos) + (size_t)(pr->context_lines - 1);
+ } 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 {
@@ -221,7 +222,7 @@ void print_match(FILE *out, printer_t *pr, match_t *m)
if (!first) {
// When not printing context lines, print each match on its own
// line instead of jamming them all together:
- if (pr->context_lines == 0 && (!pr->needs_line_number || !pr->print_line_numbers)) {
+ if (pr->context_before == NO_CONTEXT && pr->context_after == NO_CONTEXT && (!pr->needs_line_number || !pr->print_line_numbers)) {
fprintf(out, "\n");
pr->needs_line_number = 1;
}
@@ -233,7 +234,7 @@ void print_match(FILE *out, printer_t *pr, match_t *m)
} else {
// Non-overlapping ranges:
print_between(out, pr, pr->pos, after_last, pr->use_color ? color_normal : NULL);
- if (pr->context_lines > 1)
+ if (pr->context_before > 0 || pr->context_after > 0)
fprintf(out, "\n"); // Gap between chunks
}
}
@@ -253,19 +254,19 @@ void print_match(FILE *out, printer_t *pr, match_t *m)
// Print any errors that are present in the given match object to stderr and
// return the number of errors found.
//
-int print_errors(printer_t *pr, match_t *m)
+int print_errors(file_t *f, match_t *m)
{
int ret = 0;
if (m->pat->type == BP_ERROR) {
fprintf(stderr, "\033[31;1m");
- printer_t tmp = {.file = pr->file}; // No bells and whistles
+ printer_t tmp = {.file = f, .context_before=NO_CONTEXT, .context_after=NO_CONTEXT}; // No bells and whistles
print_match(stderr, &tmp, m); // Error message
fprintf(stderr, "\033[0m\n");
- fprint_line(stderr, pr->file, m->start, m->end, " ");
+ fprint_line(stderr, f, m->start, m->end, " ");
return 1;
}
for (int i = 0; m->children && m->children[i]; i++)
- ret += print_errors(pr, m->children[i]);
+ ret += print_errors(f, m->children[i]);
return ret;
}
diff --git a/print.h b/print.h
index 5ad645c..2bc2948 100644
--- a/print.h
+++ b/print.h
@@ -7,11 +7,16 @@
#include <stdbool.h>
#include "types.h"
+#include "files.h"
+
+#define USE_DEFAULT_CONTEXT -3
+#define ALL_CONTEXT -2
+#define NO_CONTEXT -1
typedef struct {
file_t *file;
const char *pos;
- int context_lines;
+ int context_before, context_after;
bool needs_line_number:1;
bool use_color:1;
bool print_line_numbers:1;
@@ -20,7 +25,7 @@ typedef struct {
__attribute__((nonnull(1,2)))
void print_match(FILE *out, printer_t *pr, match_t *m);
__attribute__((nonnull))
-int print_errors(printer_t *pr, match_t *m);
+int print_errors(file_t *f, match_t *m);
#endif
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1