25ed3412f8
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>
93 lines
2.0 KiB
C
93 lines
2.0 KiB
C
#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;
|
|
}
|