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