git
/
line-range.c
297 строк · 6.7 Кб
1#include "git-compat-util.h"2#include "line-range.h"3#include "xdiff-interface.h"4#include "userdiff.h"5
6/*
7* Parse one item in the -L option
8*
9* 'begin' is applicable only to relative range anchors. Absolute anchors
10* ignore this value.
11*
12* When parsing "-L A,B", parse_loc() is called once for A and once for B.
13*
14* When parsing A, 'begin' must be a negative number, the absolute value of
15* which is the line at which relative start-of-range anchors should be
16* based. Beginning of file is represented by -1.
17*
18* When parsing B, 'begin' must be the positive line number immediately
19* following the line computed for 'A'.
20*/
21static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,22void *data, long lines, long begin, long *ret)23{
24char *term;25const char *line;26long num;27int reg_error;28regex_t regexp;29regmatch_t match[1];30
31/* Allow "-L <something>,+20" to mean starting at <something>32* for 20 lines, or "-L <something>,-5" for 5 lines ending at
33* <something>.
34*/
35if (1 <= begin && (spec[0] == '+' || spec[0] == '-')) {36num = strtol(spec + 1, &term, 10);37if (term != spec + 1) {38if (!ret)39return term;40if (num == 0)41die("-L invalid empty range");42if (spec[0] == '-')43num = 0 - num;44if (0 < num)45*ret = begin + num - 2;46else if (!num)47*ret = begin;48else49*ret = begin + num > 0 ? begin + num : 1;50return term;51}52return spec;53}54num = strtol(spec, &term, 10);55if (term != spec) {56if (ret) {57if (num <= 0)58die("-L invalid line number: %ld", num);59*ret = num;60}61return term;62}63
64if (begin < 0) {65if (spec[0] != '^')66begin = -begin;67else {68begin = 1;69spec++;70}71}72
73if (spec[0] != '/')74return spec;75
76/* it could be a regexp of form /.../ */77for (term = (char *) spec + 1; *term && *term != '/'; term++) {78if (*term == '\\')79term++;80}81if (*term != '/')82return spec;83
84/* in the scan-only case we are not interested in the regex */85if (!ret)86return term+1;87
88/* try [spec+1 .. term-1] as regexp */89*term = 0;90begin--; /* input is in human terms */91line = nth_line(data, begin);92
93if (!(reg_error = regcomp(®exp, spec + 1, REG_NEWLINE)) &&94!(reg_error = regexec(®exp, line, 1, match, 0))) {95const char *cp = line + match[0].rm_so;96const char *nline;97
98while (begin++ < lines) {99nline = nth_line(data, begin);100if (line <= cp && cp < nline)101break;102line = nline;103}104*ret = begin;105regfree(®exp);106*term++ = '/';107return term;108}109else {110char errbuf[1024];111regerror(reg_error, ®exp, errbuf, 1024);112die("-L parameter '%s' starting at line %ld: %s",113spec + 1, begin + 1, errbuf);114}115}
116
117static int match_funcname(xdemitconf_t *xecfg, const char *bol, const char *eol)118{
119if (xecfg) {120char buf[1];121return xecfg->find_func(bol, eol - bol, buf, 1,122xecfg->find_func_priv) >= 0;123}124
125if (bol == eol)126return 0;127if (isalpha(*bol) || *bol == '_' || *bol == '$')128return 1;129return 0;130}
131
132static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char *start,133regex_t *regexp)134{
135int reg_error;136regmatch_t match[1];137while (*start) {138const char *bol, *eol;139reg_error = regexec(regexp, start, 1, match, 0);140if (reg_error == REG_NOMATCH)141return NULL;142else if (reg_error) {143char errbuf[1024];144regerror(reg_error, regexp, errbuf, 1024);145die("-L parameter: regexec() failed: %s", errbuf);146}147/* determine extent of line matched */148bol = start+match[0].rm_so;149eol = start+match[0].rm_eo;150while (bol > start && *--bol != '\n')151; /* nothing */152if (*bol == '\n')153bol++;154while (*eol && *eol != '\n')155eol++;156if (*eol == '\n')157eol++;158/* is it a funcname line? */159if (match_funcname(xecfg, (char*) bol, (char*) eol))160return bol;161start = eol;162}163return NULL;164}
165
166static const char *parse_range_funcname(167const char *arg, nth_line_fn_t nth_line_cb,168void *cb_data, long lines, long anchor, long *begin, long *end,169const char *path, struct index_state *istate)170{
171char *pattern;172const char *term;173struct userdiff_driver *drv;174xdemitconf_t *xecfg = NULL;175const char *start;176const char *p;177int reg_error;178regex_t regexp;179
180if (*arg == '^') {181anchor = 1;182arg++;183}184
185assert(*arg == ':');186term = arg+1;187while (*term && *term != ':') {188if (*term == '\\' && *(term+1))189term++;190term++;191}192if (term == arg+1)193return NULL;194if (!begin) /* skip_range_arg case */195return term;196
197pattern = xstrndup(arg+1, term-(arg+1));198
199anchor--; /* input is in human terms */200start = nth_line_cb(cb_data, anchor);201
202drv = userdiff_find_by_path(istate, path);203if (drv && drv->funcname.pattern) {204const struct userdiff_funcname *pe = &drv->funcname;205CALLOC_ARRAY(xecfg, 1);206xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);207}208
209reg_error = regcomp(®exp, pattern, REG_NEWLINE);210if (reg_error) {211char errbuf[1024];212regerror(reg_error, ®exp, errbuf, 1024);213die("-L parameter '%s': %s", pattern, errbuf);214}215
216p = find_funcname_matching_regexp(xecfg, (char*) start, ®exp);217if (!p)218die("-L parameter '%s' starting at line %ld: no match",219pattern, anchor + 1);220*begin = 0;221while (p > nth_line_cb(cb_data, *begin))222(*begin)++;223
224if (*begin >= lines)225die("-L parameter '%s' matches at EOF", pattern);226
227*end = *begin+1;228while (*end < lines) {229const char *bol = nth_line_cb(cb_data, *end);230const char *eol = nth_line_cb(cb_data, *end+1);231if (match_funcname(xecfg, bol, eol))232break;233(*end)++;234}235
236regfree(®exp);237if (xecfg)238xdiff_clear_find_func(xecfg);239free(xecfg);240free(pattern);241
242/* compensate for 1-based numbering */243(*begin)++;244
245return term;246}
247
248int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,249void *cb_data, long lines, long anchor,250long *begin, long *end,251const char *path, struct index_state *istate)252{
253*begin = *end = 0;254
255if (anchor < 1)256anchor = 1;257if (anchor > lines)258anchor = lines + 1;259
260if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':')) {261arg = parse_range_funcname(arg, nth_line_cb, cb_data,262lines, anchor, begin, end,263path, istate);264if (!arg || *arg)265return -1;266return 0;267}268
269arg = parse_loc(arg, nth_line_cb, cb_data, lines, -anchor, begin);270
271if (*arg == ',')272arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end);273
274if (*arg)275return -1;276
277if (*begin && *end && *end < *begin) {278SWAP(*end, *begin);279}280
281return 0;282}
283
284const char *skip_range_arg(const char *arg, struct index_state *istate)285{
286if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':'))287return parse_range_funcname(arg, NULL, NULL,2880, 0, NULL, NULL,289NULL, istate);290
291arg = parse_loc(arg, NULL, NULL, 0, -1, NULL);292
293if (*arg == ',')294arg = parse_loc(arg+1, NULL, NULL, 0, 0, NULL);295
296return arg;297}
298