Refactor parse_loc
[git] / line-range.c
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) {
24                         if (spec[0] == '-')
25                                 num = 0 - num;
26                         if (0 < num)
27                                 *ret = begin + num - 2;
28                         else if (!num)
29                                 *ret = begin;
30                         else
31                                 *ret = begin + num;
32                         return term;
33                 }
34                 return spec;
35         }
36         num = strtol(spec, &term, 10);
37         if (term != spec) {
38                 *ret = num;
39                 return term;
40         }
41         if (spec[0] != '/')
42                 return spec;
43
44         /* it could be a regexp of form /.../ */
45         for (term = (char *) spec + 1; *term && *term != '/'; term++) {
46                 if (*term == '\\')
47                         term++;
48         }
49         if (*term != '/')
50                 return spec;
51
52         /* try [spec+1 .. term-1] as regexp */
53         *term = 0;
54         begin--; /* input is in human terms */
55         line = nth_line(data, begin);
56
57         if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
58             !(reg_error = regexec(&regexp, line, 1, match, 0))) {
59                 const char *cp = line + match[0].rm_so;
60                 const char *nline;
61
62                 while (begin++ < lines) {
63                         nline = nth_line(data, begin);
64                         if (line <= cp && cp < nline)
65                                 break;
66                         line = nline;
67                 }
68                 *ret = begin;
69                 regfree(&regexp);
70                 *term++ = '/';
71                 return term;
72         }
73         else {
74                 char errbuf[1024];
75                 regerror(reg_error, &regexp, errbuf, 1024);
76                 die("-L parameter '%s': %s", spec + 1, errbuf);
77         }
78 }
79
80 int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
81                     void *cb_data, long lines, long *begin, long *end)
82 {
83         arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin);
84
85         if (*arg == ',')
86                 arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end);
87
88         if (*arg)
89                 return -1;
90
91         return 0;
92 }