static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char *start, regex_t *regexp) { int reg_error; regmatch_t match[1]; while (1) { const char *bol, *eol; reg_error = regexec(regexp, start, 1, match, 0); if (reg_error == REG_NOMATCH) return NULL; else if (reg_error) { char errbuf[1024]; regerror(reg_error, regexp, errbuf, 1024); die("-L parameter: regexec() failed: %s", errbuf); } /* determine extent of line matched */ bol = start+match[0].rm_so; eol = start+match[0].rm_eo; while (bol > start && *bol != '\n') bol--; if (*bol == '\n') bol++; while (*eol && *eol != '\n') eol++; if (*eol == '\n') eol++; /* is it a funcname line? */ if (match_funcname(xecfg, (char*) bol, (char*) eol)) return bol; start = eol; } }
static void show_pre_context(struct grep_opt *opt, struct grep_source *gs, char *bol, char *end, unsigned lno) { unsigned cur = lno, from = 1, funcname_lno = 0; int funcname_needed = !!opt->funcname; if (opt->funcbody && !match_funcname(opt, gs, bol, end)) funcname_needed = 2; if (opt->pre_context < lno) from = lno - opt->pre_context; if (from <= opt->last_shown) from = opt->last_shown + 1; /* Rewind. */ while (bol > gs->buf && cur > (funcname_needed == 2 ? opt->last_shown + 1 : from)) { char *eol = --bol; while (bol > gs->buf && bol[-1] != '\n') bol--; cur--; if (funcname_needed && match_funcname(opt, gs, bol, eol)) { funcname_lno = cur; funcname_needed = 0; } } /* We need to look even further back to find a function signature. */ if (opt->funcname && funcname_needed) show_funcname_line(opt, gs, bol, cur); /* Back forward. */ while (cur < lno) { char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-'; while (*eol != '\n') eol++; show_line(opt, bol, eol, gs->name, cur, sign); bol = eol + 1; cur++; } }
static void show_funcname_line(struct grep_opt *opt, const char *name, char *buf, char *bol, unsigned lno) { while (bol > buf) { char *eol = --bol; while (bol > buf && bol[-1] != '\n') bol--; lno--; if (lno <= opt->last_shown) break; if (match_funcname(opt, bol, eol)) { show_line(opt, bol, eol, name, lno, '='); break; } } }
static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_cb, void *cb_data, long lines, long *begin, long *end, const char *path) { char *pattern; const char *term; struct userdiff_driver *drv; xdemitconf_t *xecfg = NULL; const char *start; const char *p; int reg_error; regex_t regexp; assert(*arg == ':'); term = arg+1; while (*term && *term != ':') { if (*term == '\\' && *(term+1)) term++; term++; } if (term == arg+1) return NULL; if (!begin) /* skip_range_arg case */ return term; pattern = xstrndup(arg+1, term-(arg+1)); start = nth_line_cb(cb_data, 0); drv = userdiff_find_by_path(path); if (drv && drv->funcname.pattern) { const struct userdiff_funcname *pe = &drv->funcname; xecfg = xcalloc(1, sizeof(*xecfg)); xdiff_set_find_func(xecfg, pe->pattern, pe->cflags); } reg_error = regcomp(®exp, pattern, REG_NEWLINE); if (reg_error) { char errbuf[1024]; regerror(reg_error, ®exp, errbuf, 1024); die("-L parameter '%s': %s", pattern, errbuf); } p = find_funcname_matching_regexp(xecfg, (char*) start, ®exp); if (!p) die("-L parameter '%s': no match", pattern); *begin = 0; while (p > nth_line_cb(cb_data, *begin)) (*begin)++; if (*begin >= lines) die("-L parameter '%s' matches at EOF", pattern); *end = *begin+1; while (*end < lines) { const char *bol = nth_line_cb(cb_data, *end); const char *eol = nth_line_cb(cb_data, *end+1); if (match_funcname(xecfg, bol, eol)) break; (*end)++; } regfree(®exp); free(xecfg); free(pattern); /* compensate for 1-based numbering */ (*begin)++; return term; }
static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int collect_hits) { char *bol; unsigned long left; unsigned lno = 1; unsigned last_hit = 0; int binary_match_only = 0; unsigned count = 0; int try_lookahead = 0; int show_function = 0; struct userdiff_driver *textconv = NULL; enum grep_context ctx = GREP_CONTEXT_HEAD; xdemitconf_t xecfg; if (!opt->output) opt->output = std_output; if (opt->pre_context || opt->post_context || opt->file_break || opt->funcbody) { /* Show hunk marks, except for the first file. */ if (opt->last_shown) opt->show_hunk_mark = 1; /* * If we're using threads then we can't easily identify * the first file. Always put hunk marks in that case * and skip the very first one later in work_done(). */ if (opt->output != std_output) opt->show_hunk_mark = 1; } opt->last_shown = 0; if (opt->allow_textconv) { grep_source_load_driver(gs); /* * We might set up the shared textconv cache data here, which * is not thread-safe. */ grep_attr_lock(); textconv = userdiff_get_textconv(gs->driver); grep_attr_unlock(); } /* * We know the result of a textconv is text, so we only have to care * about binary handling if we are not using it. */ if (!textconv) { switch (opt->binary) { case GREP_BINARY_DEFAULT: if (grep_source_is_binary(gs)) binary_match_only = 1; break; case GREP_BINARY_NOMATCH: if (grep_source_is_binary(gs)) return 0; /* Assume unmatch */ break; case GREP_BINARY_TEXT: break; default: die("bug: unknown binary handling mode"); } } memset(&xecfg, 0, sizeof(xecfg)); opt->priv = &xecfg; try_lookahead = should_lookahead(opt); if (fill_textconv_grep(textconv, gs) < 0) return 0; bol = gs->buf; left = gs->size; while (left) { char *eol, ch; int hit; /* * look_ahead() skips quickly to the line that possibly * has the next hit; don't call it if we need to do * something more than just skipping the current line * in response to an unmatch for the current line. E.g. * inside a post-context window, we will show the current * line as a context around the previous hit when it * doesn't hit. */ if (try_lookahead && !(last_hit && (show_function || lno <= last_hit + opt->post_context)) && look_ahead(opt, &left, &lno, &bol)) break; eol = end_of_line(bol, &left); ch = *eol; *eol = 0; if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol)) ctx = GREP_CONTEXT_BODY; hit = match_line(opt, bol, eol, ctx, collect_hits); *eol = ch; if (collect_hits) goto next_line; /* "grep -v -e foo -e bla" should list lines * that do not have either, so inversion should * be done outside. */ if (opt->invert) hit = !hit; if (opt->unmatch_name_only) { if (hit) return 0; goto next_line; } if (hit) { count++; if (opt->status_only) return 1; if (opt->name_only) { show_name(opt, gs->name); return 1; } if (opt->count) goto next_line; if (binary_match_only) { opt->output(opt, "Binary file ", 12); output_color(opt, gs->name, strlen(gs->name), opt->color_filename); opt->output(opt, " matches\n", 9); return 1; } /* Hit at this line. If we haven't shown the * pre-context lines, we would need to show them. */ if (opt->pre_context || opt->funcbody) show_pre_context(opt, gs, bol, eol, lno); else if (opt->funcname) show_funcname_line(opt, gs, bol, lno); show_line(opt, bol, eol, gs->name, lno, ':'); last_hit = lno; if (opt->funcbody) show_function = 1; goto next_line; } if (show_function && match_funcname(opt, gs, bol, eol)) show_function = 0; if (show_function || (last_hit && lno <= last_hit + opt->post_context)) { /* If the last hit is within the post context, * we need to show this line. */ show_line(opt, bol, eol, gs->name, lno, '-'); } next_line: bol = eol + 1; if (!left) break; left--; lno++; } if (collect_hits) return 0; if (opt->status_only) return 0; if (opt->unmatch_name_only) { /* We did not see any hit, so we want to show this */ show_name(opt, gs->name); return 1; } xdiff_clear_find_func(&xecfg); opt->priv = NULL; /* NEEDSWORK: * The real "grep -c foo *.c" gives many "bar.c:0" lines, * which feels mostly useless but sometimes useful. Maybe * make it another option? For now suppress them. */ if (opt->count && count) { char buf[32]; if (opt->pathname) { output_color(opt, gs->name, strlen(gs->name), opt->color_filename); output_sep(opt, ':'); } snprintf(buf, sizeof(buf), "%u\n", count); opt->output(opt, buf, strlen(buf)); return 1; } return !!last_hit; }