line-range.con commit blame: inline one-line function into its lone caller (7539357)
   1#include "git-compat-util.h"
   2#include "line-range.h"
   3#include "xdiff-interface.h"
   4#include "strbuf.h"
   5#include "userdiff.h"
   6
   7/*
   8 * Parse one item in the -L option
   9 */
  10static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
  11                             void *data, long lines, long begin, long *ret)
  12{
  13        char *term;
  14        const char *line;
  15        long num;
  16        int reg_error;
  17        regex_t regexp;
  18        regmatch_t match[1];
  19
  20        /* Allow "-L <something>,+20" to mean starting at <something>
  21         * for 20 lines, or "-L <something>,-5" for 5 lines ending at
  22         * <something>.
  23         */
  24        if (1 <= begin && (spec[0] == '+' || spec[0] == '-')) {
  25                num = strtol(spec + 1, &term, 10);
  26                if (term != spec + 1) {
  27                        if (!ret)
  28                                return term;
  29                        if (num == 0)
  30                                die("-L invalid empty range");
  31                        if (spec[0] == '-')
  32                                num = 0 - num;
  33                        if (0 < num)
  34                                *ret = begin + num - 2;
  35                        else if (!num)
  36                                *ret = begin;
  37                        else
  38                                *ret = begin + num;
  39                        return term;
  40                }
  41                return spec;
  42        }
  43        num = strtol(spec, &term, 10);
  44        if (term != spec) {
  45                if (ret)
  46                        *ret = num;
  47                return term;
  48        }
  49        if (spec[0] != '/')
  50                return spec;
  51
  52        /* it could be a regexp of form /.../ */
  53        for (term = (char *) spec + 1; *term && *term != '/'; term++) {
  54                if (*term == '\\')
  55                        term++;
  56        }
  57        if (*term != '/')
  58                return spec;
  59
  60        /* in the scan-only case we are not interested in the regex */
  61        if (!ret)
  62                return term+1;
  63
  64        /* try [spec+1 .. term-1] as regexp */
  65        *term = 0;
  66        begin--; /* input is in human terms */
  67        line = nth_line(data, begin);
  68
  69        if (!(reg_error = regcomp(&regexp, spec + 1, REG_NEWLINE)) &&
  70            !(reg_error = regexec(&regexp, line, 1, match, 0))) {
  71                const char *cp = line + match[0].rm_so;
  72                const char *nline;
  73
  74                while (begin++ < lines) {
  75                        nline = nth_line(data, begin);
  76                        if (line <= cp && cp < nline)
  77                                break;
  78                        line = nline;
  79                }
  80                *ret = begin;
  81                regfree(&regexp);
  82                *term++ = '/';
  83                return term;
  84        }
  85        else {
  86                char errbuf[1024];
  87                regerror(reg_error, &regexp, errbuf, 1024);
  88                die("-L parameter '%s': %s", spec + 1, errbuf);
  89        }
  90}
  91
  92static int match_funcname(xdemitconf_t *xecfg, const char *bol, const char *eol)
  93{
  94        if (xecfg) {
  95                char buf[1];
  96                return xecfg->find_func(bol, eol - bol, buf, 1,
  97                                        xecfg->find_func_priv) >= 0;
  98        }
  99
 100        if (bol == eol)
 101                return 0;
 102        if (isalpha(*bol) || *bol == '_' || *bol == '$')
 103                return 1;
 104        return 0;
 105}
 106
 107static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char *start,
 108                                                 regex_t *regexp)
 109{
 110        int reg_error;
 111        regmatch_t match[1];
 112        while (1) {
 113                const char *bol, *eol;
 114                reg_error = regexec(regexp, start, 1, match, 0);
 115                if (reg_error == REG_NOMATCH)
 116                        return NULL;
 117                else if (reg_error) {
 118                        char errbuf[1024];
 119                        regerror(reg_error, regexp, errbuf, 1024);
 120                        die("-L parameter: regexec() failed: %s", errbuf);
 121                }
 122                /* determine extent of line matched */
 123                bol = start+match[0].rm_so;
 124                eol = start+match[0].rm_eo;
 125                while (bol > start && *bol != '\n')
 126                        bol--;
 127                if (*bol == '\n')
 128                        bol++;
 129                while (*eol && *eol != '\n')
 130                        eol++;
 131                if (*eol == '\n')
 132                        eol++;
 133                /* is it a funcname line? */
 134                if (match_funcname(xecfg, (char*) bol, (char*) eol))
 135                        return bol;
 136                start = eol;
 137        }
 138}
 139
 140static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_cb,
 141                                        void *cb_data, long lines, long *begin, long *end,
 142                                        const char *path)
 143{
 144        char *pattern;
 145        const char *term;
 146        struct userdiff_driver *drv;
 147        xdemitconf_t *xecfg = NULL;
 148        const char *start;
 149        const char *p;
 150        int reg_error;
 151        regex_t regexp;
 152
 153        assert(*arg == ':');
 154        term = arg+1;
 155        while (*term && *term != ':') {
 156                if (*term == '\\' && *(term+1))
 157                        term++;
 158                term++;
 159        }
 160        if (term == arg+1)
 161                return NULL;
 162        if (!begin) /* skip_range_arg case */
 163                return term;
 164
 165        pattern = xstrndup(arg+1, term-(arg+1));
 166
 167        start = nth_line_cb(cb_data, 0);
 168
 169        drv = userdiff_find_by_path(path);
 170        if (drv && drv->funcname.pattern) {
 171                const struct userdiff_funcname *pe = &drv->funcname;
 172                xecfg = xcalloc(1, sizeof(*xecfg));
 173                xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
 174        }
 175
 176        reg_error = regcomp(&regexp, pattern, REG_NEWLINE);
 177        if (reg_error) {
 178                char errbuf[1024];
 179                regerror(reg_error, &regexp, errbuf, 1024);
 180                die("-L parameter '%s': %s", pattern, errbuf);
 181        }
 182
 183        p = find_funcname_matching_regexp(xecfg, (char*) start, &regexp);
 184        if (!p)
 185                die("-L parameter '%s': no match", pattern);
 186        *begin = 0;
 187        while (p > nth_line_cb(cb_data, *begin))
 188                (*begin)++;
 189
 190        if (*begin >= lines)
 191                die("-L parameter '%s' matches at EOF", pattern);
 192
 193        *end = *begin+1;
 194        while (*end < lines) {
 195                const char *bol = nth_line_cb(cb_data, *end);
 196                const char *eol = nth_line_cb(cb_data, *end+1);
 197                if (match_funcname(xecfg, bol, eol))
 198                        break;
 199                (*end)++;
 200        }
 201
 202        regfree(&regexp);
 203        free(xecfg);
 204        free(pattern);
 205
 206        /* compensate for 1-based numbering */
 207        (*begin)++;
 208
 209        return term;
 210}
 211
 212int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
 213                    void *cb_data, long lines, long *begin, long *end,
 214                    const char *path)
 215{
 216        *begin = *end = 0;
 217
 218        if (*arg == ':') {
 219                arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path);
 220                if (!arg || *arg)
 221                        return -1;
 222                return 0;
 223        }
 224
 225        arg = parse_loc(arg, nth_line_cb, cb_data, lines, 1, begin);
 226
 227        if (*arg == ',')
 228                arg = parse_loc(arg + 1, nth_line_cb, cb_data, lines, *begin + 1, end);
 229
 230        if (*arg)
 231                return -1;
 232
 233        if (*begin && *end && *end < *begin) {
 234                long tmp;
 235                tmp = *end; *end = *begin; *begin = tmp;
 236        }
 237
 238        return 0;
 239}
 240
 241const char *skip_range_arg(const char *arg)
 242{
 243        if (*arg == ':')
 244                return parse_range_funcname(arg, NULL, NULL, 0, NULL, NULL, NULL);
 245
 246        arg = parse_loc(arg, NULL, NULL, 0, -1, NULL);
 247
 248        if (*arg == ',')
 249                arg = parse_loc(arg+1, NULL, NULL, 0, 0, NULL);
 250
 251        return arg;
 252}