Subject: [PATCH] grep -A/-B/-Cinfinity to get full context

From: Jonathan Nieder <[email-masked]>
Date: Fri, 20 Aug 2010 06:55:27 -0500

Just a proof of concept.

Cc: René Scharfe <[email-masked]>
Signed-off-by: Jonathan Nieder <[email-masked]>
---
Jakub Narebski wrote:

> If there were more options that use <n> == 0 to actually mean unlimited
> (infinity), perhaps it would be better to extend parseopt to provide for
> such situation, e.g. OPT_INT_INF or something.

Something like this, maybe.

 Documentation/technical/api-parse-options.txt |    6 ++++++
 builtin/grep.c                                |    8 ++++++--
 grep.c                                        |    8 +++++---
 grep.h                                        |    4 ++--
 parse-options.c                               |   21 +++++++++++++++++++++
 parse-options.h                               |    4 ++++
 6 files changed, 44 insertions(+), 7 deletions(-)

diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 312e3b2..f152a3f 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -160,6 +160,12 @@ There are some macros to easily define options:
 	Introduce an option with integer argument.
 	The integer is put into `int_var`.
 
+`OPT_INT_INF(short, long, &int_var, inf_val, description)`::
+	Introduce an option with integer argument.
+	The integer is put into `int_var`.  If the special value
+	"inf" (or "infinity") is used as an argument, then `inf_val`
+	is put into `int_var`.
+
 `OPT_DATE(short, long, &int_var, description)`::
 	Introduce an option with date argument, see `approxidate()`.
 	The timestamp is put into `int_var`.
diff --git a/builtin/grep.c b/builtin/grep.c
index 597f76b..9fbc985 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -750,6 +750,10 @@ static int context_callback(const struct option *opt, const char *arg,
 		grep_opt->pre_context = grep_opt->post_context = 0;
 		return 0;
 	}
+	if (!strcasecmp(arg, "inf") || !strcasecmp(arg, "infinity")) {
+		grep_opt->pre_context = grep_opt->post_context = -1;
+		return 0;
+	}
 	value = strtol(arg, (char **)&endp, 10);
 	if (*endp) {
 		return error("switch `%c' expects a numerical value",
@@ -890,9 +894,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 		OPT_CALLBACK('C', NULL, &opt, "n",
 			"show <n> context lines before and after matches",
 			context_callback),
-		OPT_INTEGER('B', NULL, &opt.pre_context,
+		OPT_INT_INF('B', NULL, &opt.pre_context, -1,
 			"show <n> context lines before matches"),
-		OPT_INTEGER('A', NULL, &opt.post_context,
+		OPT_INT_INF('A', NULL, &opt.post_context, -1,
 			"show <n> context lines after matches"),
 		OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
 			context_callback),
diff --git a/grep.c b/grep.c
index 82fb349..793d41f 100644
--- a/grep.c
+++ b/grep.c
@@ -687,7 +687,7 @@ static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
 	unsigned cur = lno, from = 1, funcname_lno = 0;
 	int funcname_needed = opt->funcname;
 
-	if (opt->pre_context < lno)
+	if (opt->pre_context >= 0 && opt->pre_context < lno)
 		from = lno - opt->pre_context;
 	if (from <= opt->last_shown)
 		from = opt->last_shown + 1;
@@ -861,7 +861,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
 		 */
 		if (try_lookahead
 		    && !(last_hit
-			 && lno <= last_hit + opt->post_context)
+			 && (opt->post_context < 0
+			     || lno <= last_hit + opt->post_context))
 		    && look_ahead(opt, &left, &lno, &bol))
 			break;
 		eol = end_of_line(bol, &left);
@@ -916,7 +917,8 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
 			last_hit = lno;
 		}
 		else if (last_hit &&
-			 lno <= last_hit + opt->post_context) {
+			 (opt->post_context < 0 ||
+			  lno <= last_hit + opt->post_context)) {
 			/* If the last hit is within the post context,
 			 * we need to show this line.
 			 */
diff --git a/grep.h b/grep.h
index efa8cff..13aae94 100644
--- a/grep.h
+++ b/grep.h
@@ -95,8 +95,8 @@ struct grep_opt {
 	char color_selected[COLOR_MAXLEN];
 	char color_sep[COLOR_MAXLEN];
 	int regflags;
-	unsigned pre_context;
-	unsigned post_context;
+	int pre_context;
+	int post_context;
 	unsigned last_shown;
 	int show_hunk_mark;
 	void *priv;
diff --git a/parse-options.c b/parse-options.c
index 0fa79bc..b299125 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -612,6 +612,27 @@ int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
 	return 0;
 }
 
+int parse_opt_infinity_cb(const struct option *opt, const char *arg,
+			  int unset)
+{
+	const char *endp;
+
+	if (unset) {
+		*(int *)opt->value = 0;
+		return 0;
+	}
+	if (!arg)
+		return opterror(opt, "expects a numerical value", 0);
+	if (!strcasecmp(arg, "inf") || !strcasecmp(arg, "infinity")) {
+		*(int *)opt->value = opt->defval;
+		return 0;
+	}
+	*(int *)opt->value = strtol(arg, (char **)&endp, 10);
+	if (*endp)
+		return opterror(opt, "expects a numerical value", 0);
+	return 0;
+}
+
 int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
 			    int unset)
 {
diff --git a/parse-options.h b/parse-options.h
index 7435cdb..6ae041a 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -126,6 +126,9 @@ struct option {
 #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
 #define OPT_UYN(s, l, v, h)         { OPTION_CALLBACK, (s), (l), (v), NULL, \
 				      (h), PARSE_OPT_NOARG, &parse_opt_tertiary }
+#define OPT_INT_INF(s, l, v, i, h) \
+	{ OPTION_CALLBACK, (s), (l), (v), "n", (h), 0, \
+	  parse_opt_infinity_cb, (intptr_t)(i) }
 #define OPT_DATE(s, l, v, h) \
 	{ OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \
 	  parse_opt_approxidate_cb }
@@ -193,6 +196,7 @@ extern int parse_options_concat(struct option *dst, size_t, struct option *src);
 /*----- some often used options -----*/
 extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
 extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
+extern int parse_opt_infinity_cb(const struct option *, const char *, int);
 extern int parse_opt_color_flag_cb(const struct option *, const char *, int);
 extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
 extern int parse_opt_with_commit(const struct option *, const char *, int);
-- 
1.7.2.2.536.g42dab.dirty