Browse Source
We want to use the same style of -L n,m argument for 'git log -L' as for git-blame. Refactor the argument parsing of the range arguments from builtin/blame.c to the (new) file that will hold the 'git log -L' logic. To accommodate different data structures in blame and log -L, the file contents are abstracted away; parse_range_arg takes a callback that it uses to get the contents of a line of the (notional) file. The new test is for a case that made me pause during debugging: the 'blame -L with invalid end' test was the only one that noticed an outright failure to parse the end *at all*. So make a more explicit test for that. Signed-off-by: Bo Yang <struggleyb.nku@gmail.com> Signed-off-by: Thomas Rast <trast@student.ethz.ch> Signed-off-by: Junio C Hamano <gitster@pobox.com>maint


7 changed files with 151 additions and 109 deletions
@ -0,0 +1,18 @@ |
|||||||
|
- number |
||||||
|
+ |
||||||
|
If <start> or <end> is a number, it specifies an |
||||||
|
absolute line number (lines count from 1). |
||||||
|
+ |
||||||
|
|
||||||
|
- /regex/ |
||||||
|
+ |
||||||
|
This form will use the first line matching the given |
||||||
|
POSIX regex. If <end> is a regex, it will search |
||||||
|
starting at the line given by <start>. |
||||||
|
+ |
||||||
|
|
||||||
|
- +offset or -offset |
||||||
|
+ |
||||||
|
This is only valid for <end> and will specify a number |
||||||
|
of lines before or after the line given by <start>. |
||||||
|
+ |
@ -0,0 +1,92 @@ |
|||||||
|
#include "git-compat-util.h" |
||||||
|
#include "line-range.h" |
||||||
|
|
||||||
|
/* |
||||||
|
* Parse one item in the -L option |
||||||
|
*/ |
||||||
|
static 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; |
||||||
|
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(data, begin); |
||||||
|
|
||||||
|
if (!(reg_error = regcomp(®exp, spec + 1, REG_NEWLINE)) && |
||||||
|
!(reg_error = regexec(®exp, line, 1, match, 0))) { |
||||||
|
const char *cp = line + match[0].rm_so; |
||||||
|
const char *nline; |
||||||
|
|
||||||
|
while (begin++ < lines) { |
||||||
|
nline = nth_line(data, begin); |
||||||
|
if (line <= cp && cp < nline) |
||||||
|
break; |
||||||
|
line = nline; |
||||||
|
} |
||||||
|
*ret = begin; |
||||||
|
regfree(®exp); |
||||||
|
*term++ = '/'; |
||||||
|
return term; |
||||||
|
} |
||||||
|
else { |
||||||
|
char errbuf[1024]; |
||||||
|
regerror(reg_error, ®exp, errbuf, 1024); |
||||||
|
die("-L parameter '%s': %s", spec + 1, errbuf); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb, |
||||||
|
void *cb_data, long lines, long *begin, long *end) |
||||||
|
{ |
||||||
|
arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin); |
||||||
|
|
||||||
|
if (*arg == ',') |
||||||
|
arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end); |
||||||
|
|
||||||
|
if (*arg) |
||||||
|
return -1; |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
#ifndef LINE_RANGE_H |
||||||
|
#define LINE_RANGE_H |
||||||
|
|
||||||
|
/* |
||||||
|
* Parse one item in an -L begin,end option w.r.t. the notional file |
||||||
|
* object 'cb_data' consisting of 'lines' lines. |
||||||
|
* |
||||||
|
* The 'nth_line_cb' callback is used to determine the start of the |
||||||
|
* line 'lno' inside the 'cb_data'. The caller is expected to already |
||||||
|
* have a suitable map at hand to make this a constant-time lookup. |
||||||
|
* |
||||||
|
* Returns 0 in case of success and -1 if there was an error. The |
||||||
|
* actual range is stored in *begin and *end. The counting starts |
||||||
|
* at 1! In case of error, the caller should show usage message. |
||||||
|
*/ |
||||||
|
|
||||||
|
typedef const char *(*nth_line_fn_t)(void *data, long lno); |
||||||
|
|
||||||
|
extern int parse_range_arg(const char *arg, |
||||||
|
nth_line_fn_t nth_line_cb, |
||||||
|
void *cb_data, long lines, |
||||||
|
long *begin, long *end); |
||||||
|
|
||||||
|
#endif /* LINE_RANGE_H */ |
Loading…
Reference in new issue