git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH V5 04/17] Refactor parse_loc

From
Bo Yang <struggleyb.nku@gmail.com>
Date
Aug 11, 2010, 15:03 UTC
Message-ID
<1281539022-31616-5-git-send-email-struggleyb.nku@gmail.com>
In-Reply-To
<1281539022-31616-1-git-send-email-struggleyb.nku@gmail.com>

Both 'git blame -L' and 'git log -L' parse the same style of line number arguments, so put the 'parse_loc' function to line.c and export it.

The caller of parse_loc should provide a callback function which is used to calculate the start position of the nth line. Other parts such as regexp search, line number parsing are abstracted and re-used.

Note that, we can use '$' to specify the last line of a file.
Signed-off-by: Bo Yang <struggleyb.nku@gmail.com>
---
 builtin/blame.c |   89 +++++-------------------------------------------------
 line.c          |   52 ++++++++++++++++++++------------
 line.h          |    5 +++
 3 files changed, 46 insertions(+), 100 deletions(-)
diff --git a/builtin/blame.c b/builtin/blame.c
index 01e62fd..17b71cd 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -21,6 +21,7 @@
 #include "parse-options.h"
 #include "utf8.h"
 #include "userdiff.h"
+#include "line.h"
 
 static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file";
 
@@ -541,11 +542,16 @@ static void dup_entry(struct blame_entry *dst, struct blame_entry *src)
 	dst->score = 0;
 }
 
-static const char *nth_line(struct scoreboard *sb, int lno)
+static const char *nth_line(struct scoreboard *sb, long lno)
 {
 	return sb->final_buf + sb->lineno[lno];
 }
 
+static const char *nth_line_cb(void *data, long lno)
+{
+	return nth_line((struct scoreboard *)data, lno);
+}
+
 /*
  * It is known that lines between tlno to same came from parent, and e
  * has an overlap with that range.  it also is known that parent's
@@ -1907,83 +1913,6 @@ static const char *add_prefix(const char *prefix, const char *path)
 }
 
 /*
- * Parsing of (comma separated) one item in the -L option
- */
-static const char *parse_loc(const char *spec,
-			     struct scoreboard *sb, long lno,
-			     long begin, long *ret)
-{
-	char *term;
-	const char *line;
-	long num;
-	int reg_error;
-	regex_t regexp;
-	regmatch_t match[1];
-
-	/* Allow "-L <something>,+20" to mean starting at <something>
-	 * for 20 lines, or "-L <something>,-5" for 5 lines ending at
-	 * <something>.
-	 */
-	if (1 < begin && (spec[0] == '+' || spec[0] == '-')) {
-		num = strtol(spec + 1, &term, 10);
-		if (term != spec + 1) {
-			if (spec[0] == '-')
-				num = 0 - num;
-			if (0 < num)
-				*ret = begin + num - 2;
-			else if (!num)
-				*ret = begin;
-			else
-				*ret = begin + num;
-			return term;
-		}
-		return spec;
-	}
-	num = strtol(spec, &term, 10);
-	if (term != spec) {
-		*ret = num;
-		return term;
-	}
-	if (spec[0] != '/')
-		return spec;
-
-	/* it could be a regexp of form /.../ */
-	for (term = (char *) spec + 1; *term && *term != '/'; term++) {
-		if (*term == '\\')
-			term++;
-	}
-	if (*term != '/')
-		return spec;
-
-	/* try [spec+1 .. term-1] as regexp */
-	*term = 0;
-	begin--; /* input is in human terms */
-	line = nth_line(sb, begin);
-
-	if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
-	    !(reg_error = regexec(&regexp, line, 1, match, 0))) {
-		const char *cp = line + match[0].rm_so;
-		const char *nline;
-
-		while (begin++ < lno) {
-			nline = nth_line(sb, begin);
-			if (line <= cp && cp < nline)
-				break;
-			line = nline;
-		}
-		*ret = begin;
-		regfree(&regexp);
-		*term++ = '/';
-		return term;
-	}
-	else {
-		char errbuf[1024];
-		regerror(reg_error, &regexp, errbuf, 1024);
-		die("-L parameter '%s': %s", spec + 1, errbuf);
-	}
-}
-
-/*
  * Parsing of -L option
  */
 static void prepare_blame_range(struct scoreboard *sb,
@@ -1993,9 +1922,9 @@ static void prepare_blame_range(struct scoreboard *sb,
 {
 	const char *term;
 
-	term = parse_loc(bottomtop, sb, lno, 1, bottom);
+	term = parse_loc(bottomtop, nth_line_cb, sb, lno, 1, bottom);
 	if (*term == ',') {
-		term = parse_loc(term + 1, sb, lno, *bottom + 1, top);
+		term = parse_loc(term + 1, nth_line_cb, sb, lno, *bottom + 1, top);
 		if (*term)
 			usage(blame_usage);
 	}
diff --git a/line.c b/line.c
index e277fa6..6c5f69e 100644
--- a/line.c
+++ b/line.c
@@ -95,25 +95,29 @@ static void fill_line_ends(struct diff_filespec *spec, long *lines,
 	*line_ends = ends;
 }
 
-static const char *nth_line(struct diff_filespec *spec, long line,
-		long lines, unsigned long *line_ends)
+struct nth_line_cb {
+	struct diff_filespec *spec;
+	long lines;
+	unsigned long *line_ends;
+};
+
+static const char *nth_line(void *data, long line)
 {
-	assert(line < lines);
-	assert(spec && spec->data);
+	struct nth_line_cb *d = data;
+	assert(d && line < d->lines);
+	assert(d->spec && d->spec->data);
 
 	if (line == 0)
-		return (char *)spec->data;
+		return (char *)d->spec->data;
 	else
-		return (char *)spec->data + line_ends[line] + 1;
+		return (char *)d->spec->data + d->line_ends[line] + 1;
 }
 
 /*
- * copied from blame.c, indeed, we can even to use this to test
- * whether line log works. :)
+ * Parsing of (comma separated) one item in the -L option
  */
-static const char *parse_loc(const char *spec, struct diff_filespec *file,
-			     long lines, unsigned long *line_ends,
-			     long begin, long *ret)
+const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
+		void *data, long lines, long begin, long *ret)
 {
 	char *term;
 	const char *line;
@@ -122,6 +126,13 @@ static const char *parse_loc(const char *spec, struct diff_filespec *file,
 	regex_t regexp;
 	regmatch_t match[1];
 
+	/* Catch the '$' matcher, now it is used to match the last
+	 * line of the file. */
+	if (spec[0] == '$') {
+		*ret = lines;
+		return spec + 1;
+	}
+
 	/* Allow "-L <something>,+20" to mean starting at <something>
 	 * for 20 lines, or "-L <something>,-5" for 5 lines ending at
 	 * <something>.
@@ -160,7 +171,7 @@ static const char *parse_loc(const char *spec, struct diff_filespec *file,
 	/* try [spec+1 .. term-1] as regexp */
 	*term = 0;
 	begin--; /* input is in human terms */
-	line = nth_line(file, begin, lines, line_ends);
+	line = nth_line(data, begin);
 
 	if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
 	    !(reg_error = regexec(&regexp, line, 1, match, 0))) {
@@ -168,7 +179,7 @@ static const char *parse_loc(const char *spec, struct diff_filespec *file,
 		const char *nline;
 
 		while (begin++ < lines) {
-			nline = nth_line(file, begin, lines, line_ends);
+			nline = nth_line(data, begin);
 			if (line <= cp && cp < nline)
 				break;
 			line = nline;
@@ -188,10 +199,11 @@ static void parse_range(long lines, unsigned long *line_ends,
 		struct line_range *r, struct diff_filespec *spec)
 {
 	const char *term;
+	struct nth_line_cb data = {spec, lines, line_ends};
 
-	term = parse_loc(r->arg, spec, lines, line_ends, 1, &r->start);
+	term = parse_loc(r->arg, nth_line, &data, lines - 1, 1, &r->start);
 	if (*term == ',') {
-		term = parse_loc(term + 1, spec, lines, line_ends,
+		term = parse_loc(term + 1, nth_line, &data, lines - 1,
 			r->start + 1, &r->end);
 		if (*term)
 			die("-L parameter's argument should be <start>,<end>");
@@ -200,16 +212,16 @@ static void parse_range(long lines, unsigned long *line_ends,
 	if (*term)
 		die("-L parameter's argument should be <start>,<end>");
 
+	if (r->start < 1)
+		r->start = 1;
+	if (r->end >= lines)
+		r->end = lines - 1;
+
 	if (r->start > r->end) {
 		long tmp = r->start;
 		r->start = r->end;
 		r->end = tmp;
 	}
-
-	if (r->start < 1)
-		r->start = 1;
-	if (r->end >= lines)
-		r->end = lines - 1;
 }
 
 static void parse_lines(struct commit *commit, struct diff_line_range *r)
diff --git a/line.h b/line.h
index a04af86..5bde828 100644
--- a/line.h
+++ b/line.h
@@ -8,6 +8,8 @@ struct commit;
 struct diff_line_range;
 struct diff_options;
 
+typedef const char *(*nth_line_fn_t)(void *data, long lno);
+
 struct print_range {
 	int start, end;		/* Line range of post-image */
 	int pstart, pend;	/* Line range of pre-image */
@@ -125,4 +127,7 @@ extern void add_line_range(struct rev_info *revs, struct commit *commit,
 extern struct diff_line_range *lookup_line_range(struct rev_info *revs,
 		struct commit *commit);
 
+const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
+		void *data, long lines, long begin, long *ret);
+
 #endif
-- 
1.7.2.19.g79e5d
Previous: Bo YangNext: Bo Yang
Message 5 of 28 in “Reroll a version 5 of this series”
  1. 00/17 Reroll a version 5 of this seriesBo Yang, Aug 11, 2010
  2. 01/17 parse-options: enhance STOP_AT_NON_OPTIONBo Yang, Aug 11, 2010
  3. 02/17 parse-options: add two helper functionsBo Yang, Aug 11, 2010
  4. 03/17 Add the basic data structure for line level historyBo Yang, Aug 11, 2010
  5. 04/17 Refactor parse_locBo Yang, Aug 11, 2010
  6. 05/17 Parse the -L optionsBo Yang, Aug 11, 2010
  7. 06/17 Export three functions from diff.cBo Yang, Aug 11, 2010
  8. 07/17 Add range clone functionsBo Yang, Aug 11, 2010
  9. 08/17 map/take range to the parent of commitsBo Yang, Aug 11, 2010
  10. 09/17 Print the line logBo Yang, Aug 11, 2010
  11. 10/17 Hook line history into cmd_log, ensuring a topo-ordered walkBo Yang, Aug 11, 2010
  12. 11/17 Make rewrite_parents public to other part of gitBo Yang, Aug 11, 2010
  13. 12/17 Make graph_next_line external to other part of gitBo Yang, Aug 11, 2010
  14. 13/17 Add parent rewriting to line history browserBo Yang, Aug 11, 2010
  15. log -L crash (Re: [PATCH V5 13/17] Add parent rewriting to line history browser)Jonathan Nieder, Aug 30, 2010
  16. Bo YangSep 1, 2010
  17. log -L: do not free parents lists we might need againThomas Rast, Sep 11, 2010
  18. 14/17 Add --graph prefix before line history outputBo Yang, Aug 11, 2010
  19. 15/17 Add --full-line-diff optionBo Yang, Aug 11, 2010
  20. 16/17 Add tests for line history browserBo Yang, Aug 11, 2010
  21. Ævar Arnfjörð BjarmasonAug 12, 2010
  22. Bo YangAug 12, 2010
  23. Ævar Arnfjörð BjarmasonAug 12, 2010
  24. Junio C HamanoAug 12, 2010
  25. Junio C HamanoAug 12, 2010
  26. 17/17 Document line history browserBo Yang, Aug 11, 2010
  27. david@lang.hmAug 12, 2010
  28. Junio C HamanoAug 12, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.