]>
Commit | Line | Data |
---|---|---|
25ed3412 BY |
1 | #include "git-compat-util.h" |
2 | #include "line-range.h" | |
3 | ||
4 | /* | |
5 | * Parse one item in the -L option | |
6 | */ | |
7 | static const char *parse_loc(const char *spec, nth_line_fn_t nth_line, | |
8 | void *data, long lines, long begin, long *ret) | |
9 | { | |
10 | char *term; | |
11 | const char *line; | |
12 | long num; | |
13 | int reg_error; | |
14 | regex_t regexp; | |
15 | regmatch_t match[1]; | |
16 | ||
17 | /* Allow "-L <something>,+20" to mean starting at <something> | |
18 | * for 20 lines, or "-L <something>,-5" for 5 lines ending at | |
19 | * <something>. | |
20 | */ | |
21 | if (1 < begin && (spec[0] == '+' || spec[0] == '-')) { | |
22 | num = strtol(spec + 1, &term, 10); | |
23 | if (term != spec + 1) { | |
12da1d1f TR |
24 | if (!ret) |
25 | return term; | |
25ed3412 BY |
26 | if (spec[0] == '-') |
27 | num = 0 - num; | |
28 | if (0 < num) | |
29 | *ret = begin + num - 2; | |
30 | else if (!num) | |
31 | *ret = begin; | |
32 | else | |
33 | *ret = begin + num; | |
34 | return term; | |
35 | } | |
36 | return spec; | |
37 | } | |
38 | num = strtol(spec, &term, 10); | |
39 | if (term != spec) { | |
12da1d1f TR |
40 | if (ret) |
41 | *ret = num; | |
25ed3412 BY |
42 | return term; |
43 | } | |
44 | if (spec[0] != '/') | |
45 | return spec; | |
46 | ||
47 | /* it could be a regexp of form /.../ */ | |
48 | for (term = (char *) spec + 1; *term && *term != '/'; term++) { | |
49 | if (*term == '\\') | |
50 | term++; | |
51 | } | |
52 | if (*term != '/') | |
53 | return spec; | |
54 | ||
12da1d1f TR |
55 | /* in the scan-only case we are not interested in the regex */ |
56 | if (!ret) | |
57 | return term+1; | |
58 | ||
25ed3412 BY |
59 | /* try [spec+1 .. term-1] as regexp */ |
60 | *term = 0; | |
61 | begin--; /* input is in human terms */ | |
62 | line = nth_line(data, begin); | |
63 | ||
64 | if (!(reg_error = regcomp(®exp, spec + 1, REG_NEWLINE)) && | |
65 | !(reg_error = regexec(®exp, line, 1, match, 0))) { | |
66 | const char *cp = line + match[0].rm_so; | |
67 | const char *nline; | |
68 | ||
69 | while (begin++ < lines) { | |
70 | nline = nth_line(data, begin); | |
71 | if (line <= cp && cp < nline) | |
72 | break; | |
73 | line = nline; | |
74 | } | |
75 | *ret = begin; | |
76 | regfree(®exp); | |
77 | *term++ = '/'; | |
78 | return term; | |
79 | } | |
80 | else { | |
81 | char errbuf[1024]; | |
82 | regerror(reg_error, ®exp, errbuf, 1024); | |
83 | die("-L parameter '%s': %s", spec + 1, errbuf); | |
84 | } | |
85 | } | |
86 | ||
87 | int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb, | |
88 | void *cb_data, long lines, long *begin, long *end) | |
89 | { | |
90 | arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin); | |
91 | ||
92 | if (*arg == ',') | |
93 | arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end); | |
94 | ||
95 | if (*arg) | |
96 | return -1; | |
97 | ||
98 | return 0; | |
99 | } | |
12da1d1f TR |
100 | |
101 | const char *skip_range_arg(const char *arg) | |
102 | { | |
103 | arg = parse_loc(arg, NULL, NULL, 0, -1, NULL); | |
104 | ||
105 | if (*arg == ',') | |
106 | arg = parse_loc(arg+1, NULL, NULL, 0, 0, NULL); | |
107 | ||
108 | return arg; | |
109 | } |