From f9daa44d212189ee504ee2459171943ea36fbf1c Mon Sep 17 00:00:00 2001 From: Bo Yang Date: Wed, 11 Aug 2010 23:03:29 +0800 Subject: [PATCH] Refactor parse_loc Both 'git blame -L' and 'git log -L' parse the same style of line number arguments, so put the 'parse_loc' function to line.c and export it. The caller of parse_loc should provide a callback function which is used to calculate the start position of the nth line. Other parts such as regexp search, line number parsing are abstracted and re-used. Note that, we can use '$' to specify the last line of a file. Signed-off-by: Bo Yang Signed-off-by: Junio C Hamano --- builtin/blame.c | 89 +++++-------------------------------------------- line.c | 52 ++++++++++++++++++----------- line.h | 5 +++ 3 files changed, 46 insertions(+), 100 deletions(-) diff --git a/builtin/blame.c b/builtin/blame.c index 01e62fdeb0..17b71cd3af 100644 --- a/builtin/blame.c +++ b/builtin/blame.c @@ -21,6 +21,7 @@ #include "parse-options.h" #include "utf8.h" #include "userdiff.h" +#include "line.h" static char blame_usage[] = "git blame [options] [rev-opts] [rev] [--] file"; @@ -541,11 +542,16 @@ static void dup_entry(struct blame_entry *dst, struct blame_entry *src) dst->score = 0; } -static const char *nth_line(struct scoreboard *sb, int lno) +static const char *nth_line(struct scoreboard *sb, long lno) { return sb->final_buf + sb->lineno[lno]; } +static const char *nth_line_cb(void *data, long lno) +{ + return nth_line((struct scoreboard *)data, lno); +} + /* * It is known that lines between tlno to same came from parent, and e * has an overlap with that range. it also is known that parent's @@ -1906,83 +1912,6 @@ static const char *add_prefix(const char *prefix, const char *path) return prefix_path(prefix, prefix ? strlen(prefix) : 0, path); } -/* - * Parsing of (comma separated) one item in the -L option - */ -static const char *parse_loc(const char *spec, - struct scoreboard *sb, long lno, - long begin, long *ret) -{ - char *term; - const char *line; - long num; - int reg_error; - regex_t regexp; - regmatch_t match[1]; - - /* Allow "-L ,+20" to mean starting at - * for 20 lines, or "-L ,-5" for 5 lines ending at - * . - */ - if (1 < begin && (spec[0] == '+' || spec[0] == '-')) { - num = strtol(spec + 1, &term, 10); - if (term != spec + 1) { - if (spec[0] == '-') - num = 0 - num; - if (0 < num) - *ret = begin + num - 2; - else if (!num) - *ret = begin; - else - *ret = begin + num; - return term; - } - return spec; - } - num = strtol(spec, &term, 10); - if (term != spec) { - *ret = num; - return term; - } - if (spec[0] != '/') - return spec; - - /* it could be a regexp of form /.../ */ - for (term = (char *) spec + 1; *term && *term != '/'; term++) { - if (*term == '\\') - term++; - } - if (*term != '/') - return spec; - - /* try [spec+1 .. term-1] as regexp */ - *term = 0; - begin--; /* input is in human terms */ - line = nth_line(sb, begin); - - if (!(reg_error = regcomp(®exp, spec + 1, REG_NEWLINE)) && - !(reg_error = regexec(®exp, line, 1, match, 0))) { - const char *cp = line + match[0].rm_so; - const char *nline; - - while (begin++ < lno) { - nline = nth_line(sb, begin); - if (line <= cp && cp < nline) - break; - line = nline; - } - *ret = begin; - regfree(®exp); - *term++ = '/'; - return term; - } - else { - char errbuf[1024]; - regerror(reg_error, ®exp, errbuf, 1024); - die("-L parameter '%s': %s", spec + 1, errbuf); - } -} - /* * Parsing of -L option */ @@ -1993,9 +1922,9 @@ static void prepare_blame_range(struct scoreboard *sb, { const char *term; - term = parse_loc(bottomtop, sb, lno, 1, bottom); + term = parse_loc(bottomtop, nth_line_cb, sb, lno, 1, bottom); if (*term == ',') { - term = parse_loc(term + 1, sb, lno, *bottom + 1, top); + term = parse_loc(term + 1, nth_line_cb, sb, lno, *bottom + 1, top); if (*term) usage(blame_usage); } diff --git a/line.c b/line.c index 775f222444..e15ed5f61a 100644 --- a/line.c +++ b/line.c @@ -95,25 +95,29 @@ static void fill_line_ends(struct diff_filespec *spec, long *lines, *line_ends = ends; } -static const char *nth_line(struct diff_filespec *spec, long line, - long lines, unsigned long *line_ends) +struct nth_line_cb { + struct diff_filespec *spec; + long lines; + unsigned long *line_ends; +}; + +static const char *nth_line(void *data, long line) { - assert(line < lines); - assert(spec && spec->data); + struct nth_line_cb *d = data; + assert(d && line < d->lines); + assert(d->spec && d->spec->data); if (line == 0) - return (char *)spec->data; + return (char *)d->spec->data; else - return (char *)spec->data + line_ends[line] + 1; + return (char *)d->spec->data + d->line_ends[line] + 1; } /* - * copied from blame.c, indeed, we can even to use this to test - * whether line log works. :) + * Parsing of (comma separated) one item in the -L option */ -static const char *parse_loc(const char *spec, struct diff_filespec *file, - long lines, unsigned long *line_ends, - long begin, long *ret) +const char *parse_loc(const char *spec, nth_line_fn_t nth_line, + void *data, long lines, long begin, long *ret) { char *term; const char *line; @@ -122,6 +126,13 @@ static const char *parse_loc(const char *spec, struct diff_filespec *file, regex_t regexp; regmatch_t match[1]; + /* Catch the '$' matcher, now it is used to match the last + * line of the file. */ + if (spec[0] == '$') { + *ret = lines; + return spec + 1; + } + /* Allow "-L ,+20" to mean starting at * for 20 lines, or "-L ,-5" for 5 lines ending at * . @@ -160,7 +171,7 @@ static const char *parse_loc(const char *spec, struct diff_filespec *file, /* try [spec+1 .. term-1] as regexp */ *term = 0; begin--; /* input is in human terms */ - line = nth_line(file, begin, lines, line_ends); + line = nth_line(data, begin); if (!(reg_error = regcomp(®exp, spec + 1, REG_NEWLINE)) && !(reg_error = regexec(®exp, line, 1, match, 0))) { @@ -168,7 +179,7 @@ static const char *parse_loc(const char *spec, struct diff_filespec *file, const char *nline; while (begin++ < lines) { - nline = nth_line(file, begin, lines, line_ends); + nline = nth_line(data, begin); if (line <= cp && cp < nline) break; line = nline; @@ -188,10 +199,11 @@ static void parse_range(long lines, unsigned long *line_ends, struct line_range *r, struct diff_filespec *spec) { const char *term; + struct nth_line_cb data = {spec, lines, line_ends}; - term = parse_loc(r->arg, spec, lines, line_ends, 1, &r->start); + term = parse_loc(r->arg, nth_line, &data, lines - 1, 1, &r->start); if (*term == ',') { - term = parse_loc(term + 1, spec, lines, line_ends, + term = parse_loc(term + 1, nth_line, &data, lines - 1, r->start + 1, &r->end); if (*term) die("-L parameter's argument should be ,"); @@ -200,16 +212,16 @@ static void parse_range(long lines, unsigned long *line_ends, if (*term) die("-L parameter's argument should be ,"); + if (r->start < 1) + r->start = 1; + if (r->end >= lines) + r->end = lines - 1; + if (r->start > r->end) { long tmp = r->start; r->start = r->end; r->end = tmp; } - - if (r->start < 1) - r->start = 1; - if (r->end >= lines) - r->end = lines - 1; } static void parse_lines(struct commit *commit, struct diff_line_range *r) diff --git a/line.h b/line.h index a04af86327..5bde82810f 100644 --- a/line.h +++ b/line.h @@ -8,6 +8,8 @@ struct commit; struct diff_line_range; struct diff_options; +typedef const char *(*nth_line_fn_t)(void *data, long lno); + struct print_range { int start, end; /* Line range of post-image */ int pstart, pend; /* Line range of pre-image */ @@ -125,4 +127,7 @@ extern void add_line_range(struct rev_info *revs, struct commit *commit, extern struct diff_line_range *lookup_line_range(struct rev_info *revs, struct commit *commit); +const char *parse_loc(const char *spec, nth_line_fn_t nth_line, + void *data, long lines, long begin, long *ret); + #endif