Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
git-mirror
/
git
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
0
Pull requests
0
Actions
Projects
0
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Security
Insights
Files
a14f6ca
Documentation
arm
compat
contrib
git-gui
gitk-git
gitweb
mozilla-sha1
perl
ppc
t
templates
xdiff
.gitattributes
.gitignore
.mailmap
COPYING
GIT-VERSION-GEN
INSTALL
Makefile
README
RelNotes
abspath.c
alias.c
alloc.c
archive-tar.c
archive-zip.c
archive.c
archive.h
attr.c
attr.h
base85.c
blob.c
blob.h
branch.c
branch.h
builtin-add.c
builtin-annotate.c
builtin-apply.c
builtin-archive.c
builtin-blame.c
builtin-branch.c
builtin-bundle.c
builtin-cat-file.c
builtin-check-attr.c
builtin-check-ref-format.c
builtin-checkout-index.c
builtin-checkout.c
builtin-clean.c
builtin-clone.c
builtin-commit-tree.c
builtin-commit.c
builtin-config.c
builtin-count-objects.c
builtin-describe.c
builtin-diff-files.c
builtin-diff-index.c
builtin-diff-tree.c
builtin-diff.c
builtin-fast-export.c
builtin-fetch--tool.c
builtin-fetch-pack.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-for-each-ref.c
builtin-fsck.c
builtin-gc.c
builtin-grep.c
builtin-http-fetch.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-ls-remote.c
builtin-ls-tree.c
builtin-mailinfo.c
builtin-mailsplit.c
builtin-merge-base.c
builtin-merge-file.c
builtin-merge-ours.c
builtin-merge-recursive.c
builtin-merge.c
builtin-mv.c
builtin-name-rev.c
builtin-pack-objects.c
builtin-pack-refs.c
builtin-prune-packed.c
builtin-prune.c
builtin-push.c
builtin-read-tree.c
builtin-reflog.c
builtin-remote.c
builtin-rerere.c
builtin-reset.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-revert.c
builtin-rm.c
builtin-send-pack.c
builtin-shortlog.c
builtin-show-branch.c
builtin-show-ref.c
builtin-stripspace.c
builtin-symbolic-ref.c
builtin-tag.c
builtin-tar-tree.c
builtin-unpack-objects.c
builtin-update-index.c
builtin-update-ref.c
builtin-upload-archive.c
builtin-verify-pack.c
builtin-verify-tag.c
builtin-write-tree.c
builtin.h
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h
check-builtins.sh
check-racy.c
check_bindir
color.c
color.h
combine-diff.c
command-list.txt
commit.c
commit.h
config.c
config.mak.in
configure.ac
connect.c
convert.c
copy.c
csum-file.c
csum-file.h
ctype.c
daemon.c
date.c
decorate.c
decorate.h
delta.h
diff-delta.c
diff-lib.c
diff-no-index.c
diff.c
diff.h
diffcore-break.c
diffcore-delta.c
diffcore-order.c
diffcore-pickaxe.c
diffcore-rename.c
diffcore.h
dir.c
dir.h
dump-cache-tree.c
editor.c
entry.c
environment.c
exec_cmd.c
exec_cmd.h
fast-import.c
fetch-pack.h
fixup-builtins
fsck.c
fsck.h
generate-cmdlist.sh
git-add--interactive.perl
git-am.sh
git-archimport.perl
git-bisect.sh
git-compat-util.h
git-cvsexportcommit.perl
git-cvsimport.perl
git-cvsserver.perl
git-filter-branch.sh
git-instaweb.sh
git-lost-found.sh
git-merge-octopus.sh
git-merge-one-file.sh
git-merge-resolve.sh
git-mergetool.sh
git-parse-remote.sh
git-pull.sh
git-quiltimport.sh
git-rebase--interactive.sh
git-rebase.sh
git-relink.perl
git-repack.sh
git-request-pull.sh
git-send-email.perl
git-sh-setup.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git-web--browse.sh
git.c
git.spec.in
graph.c
graph.h
grep.c
grep.h
hash-object.c
hash.c
hash.h
help.c
http-push.c
http-walker.c
http.c
http.h
ident.c
imap-send.c
index-pack.c
interpolate.c
interpolate.h
list-objects.c
list-objects.h
ll-merge.c
ll-merge.h
lockfile.c
log-tree.c
log-tree.h
mailmap.c
mailmap.h
match-trees.c
merge-file.c
merge-index.c
merge-recursive.h
merge-tree.c
mktag.c
mktree.c
name-hash.c
object.c
object.h
pack-check.c
pack-redundant.c
pack-refs.c
pack-refs.h
pack-revindex.c
pack-revindex.h
pack-write.c
pack.h
pager.c
parse-options.c
parse-options.h
patch-delta.c
patch-id.c
patch-ids.c
patch-ids.h
path.c
pkt-line.c
pkt-line.h
pretty.c
progress.c
progress.h
quote.c
quote.h
reachable.c
reachable.h
read-cache.c
receive-pack.c
reflog-walk.c
reflog-walk.h
refs.c
refs.h
remote.c
remote.h
rerere.c
rerere.h
revision.c
revision.h
run-command.c
run-command.h
send-pack.h
server-info.c
setup.c
sha1-lookup.c
sha1-lookup.h
sha1_file.c
sha1_name.c
shallow.c
shell.c
shortlog.h
show-index.c
sideband.c
sideband.h
strbuf.c
strbuf.h
string-list.c
string-list.h
symlinks.c
tag.c
tag.h
tar.h
test-chmtime.c
test-date.c
test-delta.c
test-genrandom.c
test-match-trees.c
test-parse-options.c
test-path-utils.c
test-sha1.c
test-sha1.sh
thread-utils.c
thread-utils.h
trace.c
transport.c
transport.h
tree-diff.c
tree-walk.c
tree-walk.h
tree.c
tree.h
unpack-file.c
unpack-trees.c
unpack-trees.h
update-server-info.c
upload-pack.c
usage.c
utf8.c
utf8.h
var.c
walker.c
walker.h
wrapper.c
write_or_die.c
ws.c
wt-status.c
wt-status.h
xdiff-interface.c
xdiff-interface.h
Breadcrumbs
git
/
grep.c
Blame
Blame
Latest commit
History
History
617 lines (558 loc) · 13.7 KB
Breadcrumbs
git
/
grep.c
Top
File metadata and controls
Code
Blame
617 lines (558 loc) · 13.7 KB
Raw
#include "cache.h" #include "grep.h" #include "xdiff-interface.h" void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat) { struct grep_pat *p = xcalloc(1, sizeof(*p)); p->pattern = pat; p->origin = "header"; p->no = 0; p->token = GREP_PATTERN_HEAD; p->field = field; *opt->pattern_tail = p; opt->pattern_tail = &p->next; p->next = NULL; } void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t) { struct grep_pat *p = xcalloc(1, sizeof(*p)); p->pattern = pat; p->origin = origin; p->no = no; p->token = t; *opt->pattern_tail = p; opt->pattern_tail = &p->next; p->next = NULL; } static void compile_regexp(struct grep_pat *p, struct grep_opt *opt) { int err = regcomp(&p->regexp, p->pattern, opt->regflags); if (err) { char errbuf[1024]; char where[1024]; if (p->no) sprintf(where, "In '%s' at %d, ", p->origin, p->no); else if (p->origin) sprintf(where, "%s, ", p->origin); else where[0] = 0; regerror(err, &p->regexp, errbuf, 1024); regfree(&p->regexp); die("%s'%s': %s", where, p->pattern, errbuf); } } static struct grep_expr *compile_pattern_or(struct grep_pat **); static struct grep_expr *compile_pattern_atom(struct grep_pat **list) { struct grep_pat *p; struct grep_expr *x; p = *list; switch (p->token) { case GREP_PATTERN: /* atom */ case GREP_PATTERN_HEAD: case GREP_PATTERN_BODY: x = xcalloc(1, sizeof (struct grep_expr)); x->node = GREP_NODE_ATOM; x->u.atom = p; *list = p->next; return x; case GREP_OPEN_PAREN: *list = p->next; x = compile_pattern_or(list); if (!x) return NULL; if (!*list || (*list)->token != GREP_CLOSE_PAREN) die("unmatched parenthesis"); *list = (*list)->next; return x; default: return NULL; } } static struct grep_expr *compile_pattern_not(struct grep_pat **list) { struct grep_pat *p; struct grep_expr *x; p = *list; switch (p->token) { case GREP_NOT: if (!p->next) die("--not not followed by pattern expression"); *list = p->next; x = xcalloc(1, sizeof (struct grep_expr)); x->node = GREP_NODE_NOT; x->u.unary = compile_pattern_not(list); if (!x->u.unary) die("--not followed by non pattern expression"); return x; default: return compile_pattern_atom(list); } } static struct grep_expr *compile_pattern_and(struct grep_pat **list) { struct grep_pat *p; struct grep_expr *x, *y, *z; x = compile_pattern_not(list); p = *list; if (p && p->token == GREP_AND) { if (!p->next) die("--and not followed by pattern expression"); *list = p->next; y = compile_pattern_and(list); if (!y) die("--and not followed by pattern expression"); z = xcalloc(1, sizeof (struct grep_expr)); z->node = GREP_NODE_AND; z->u.binary.left = x; z->u.binary.right = y; return z; } return x; } static struct grep_expr *compile_pattern_or(struct grep_pat **list) { struct grep_pat *p; struct grep_expr *x, *y, *z; x = compile_pattern_and(list); p = *list; if (x && p && p->token != GREP_CLOSE_PAREN) { y = compile_pattern_or(list); if (!y) die("not a pattern expression %s", p->pattern); z = xcalloc(1, sizeof (struct grep_expr)); z->node = GREP_NODE_OR; z->u.binary.left = x; z->u.binary.right = y; return z; } return x; } static struct grep_expr *compile_pattern_expr(struct grep_pat **list) { return compile_pattern_or(list); } void compile_grep_patterns(struct grep_opt *opt) { struct grep_pat *p; if (opt->all_match) opt->extended = 1; for (p = opt->pattern_list; p; p = p->next) { switch (p->token) { case GREP_PATTERN: /* atom */ case GREP_PATTERN_HEAD: case GREP_PATTERN_BODY: if (!opt->fixed) compile_regexp(p, opt); break; default: opt->extended = 1; break; } } if (!opt->extended) return; /* Then bundle them up in an expression. * A classic recursive descent parser would do. */ p = opt->pattern_list; opt->pattern_expression = compile_pattern_expr(&p); if (p) die("incomplete pattern expression: %s", p->pattern); } static void free_pattern_expr(struct grep_expr *x) { switch (x->node) { case GREP_NODE_ATOM: break; case GREP_NODE_NOT: free_pattern_expr(x->u.unary); break; case GREP_NODE_AND: case GREP_NODE_OR: free_pattern_expr(x->u.binary.left); free_pattern_expr(x->u.binary.right); break; } free(x); } void free_grep_patterns(struct grep_opt *opt) { struct grep_pat *p, *n; for (p = opt->pattern_list; p; p = n) { n = p->next; switch (p->token) { case GREP_PATTERN: /* atom */ case GREP_PATTERN_HEAD: case GREP_PATTERN_BODY: regfree(&p->regexp); break; default: break; } free(p); } if (!opt->extended) return; free_pattern_expr(opt->pattern_expression); } static char *end_of_line(char *cp, unsigned long *left) { unsigned long l = *left; while (l && *cp != '\n') { l--; cp++; } *left = l; return cp; } static int word_char(char ch) { return isalnum(ch) || ch == '_'; } static void show_line(struct grep_opt *opt, const char *bol, const char *eol, const char *name, unsigned lno, char sign) { if (opt->pathname) printf("%s%c", name, sign); if (opt->linenum) printf("%d%c", lno, sign); printf("%.*s\n", (int)(eol-bol), bol); } static int fixmatch(const char *pattern, char *line, regmatch_t *match) { char *hit = strstr(line, pattern); if (!hit) { match->rm_so = match->rm_eo = -1; return REG_NOMATCH; } else { match->rm_so = hit - line; match->rm_eo = match->rm_so + strlen(pattern); return 0; } } static int strip_timestamp(char *bol, char **eol_p) { char *eol = *eol_p; int ch; while (bol < --eol) { if (*eol != '>') continue; *eol_p = ++eol; ch = *eol; *eol = '\0'; return ch; } return 0; } static struct { const char *field; size_t len; } header_field[] = { { "author ", 7 }, { "committer ", 10 }, }; static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol, enum grep_context ctx) { int hit = 0; int at_true_bol = 1; int saved_ch = 0; regmatch_t pmatch[10]; if ((p->token != GREP_PATTERN) && ((p->token == GREP_PATTERN_HEAD) != (ctx == GREP_CONTEXT_HEAD))) return 0; if (p->token == GREP_PATTERN_HEAD) { const char *field; size_t len; assert(p->field < ARRAY_SIZE(header_field)); field = header_field[p->field].field; len = header_field[p->field].len; if (strncmp(bol, field, len)) return 0; bol += len; saved_ch = strip_timestamp(bol, &eol); } again: if (!opt->fixed) { regex_t *exp = &p->regexp; hit = !regexec(exp, bol, ARRAY_SIZE(pmatch), pmatch, 0); } else { hit = !fixmatch(p->pattern, bol, pmatch); } if (hit && opt->word_regexp) { if ((pmatch[0].rm_so < 0) || (eol - bol) <= pmatch[0].rm_so || (pmatch[0].rm_eo < 0) || (eol - bol) < pmatch[0].rm_eo) die("regexp returned nonsense"); /* Match beginning must be either beginning of the * line, or at word boundary (i.e. the last char must * not be a word char). Similarly, match end must be * either end of the line, or at word boundary * (i.e. the next char must not be a word char). */ if ( ((pmatch[0].rm_so == 0 && at_true_bol) || !word_char(bol[pmatch[0].rm_so-1])) && ((pmatch[0].rm_eo == (eol-bol)) || !word_char(bol[pmatch[0].rm_eo])) ) ; else hit = 0; if (!hit && pmatch[0].rm_so + bol + 1 < eol) { /* There could be more than one match on the * line, and the first match might not be * strict word match. But later ones could be! */ bol = pmatch[0].rm_so + bol + 1; at_true_bol = 0; goto again; } } if (p->token == GREP_PATTERN_HEAD && saved_ch) *eol = saved_ch; return hit; } static int match_expr_eval(struct grep_opt *o, struct grep_expr *x, char *bol, char *eol, enum grep_context ctx, int collect_hits) { int h = 0; switch (x->node) { case GREP_NODE_ATOM: h = match_one_pattern(o, x->u.atom, bol, eol, ctx); break; case GREP_NODE_NOT: h = !match_expr_eval(o, x->u.unary, bol, eol, ctx, 0); break; case GREP_NODE_AND: if (!collect_hits) return (match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0) && match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0)); h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); h &= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0); break; case GREP_NODE_OR: if (!collect_hits) return (match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0) || match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 0)); h = match_expr_eval(o, x->u.binary.left, bol, eol, ctx, 0); x->u.binary.left->hit |= h; h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1); break; default: die("Unexpected node type (internal error) %d\n", x->node); } if (collect_hits) x->hit |= h; return h; } static int match_expr(struct grep_opt *opt, char *bol, char *eol, enum grep_context ctx, int collect_hits) { struct grep_expr *x = opt->pattern_expression; return match_expr_eval(opt, x, bol, eol, ctx, collect_hits); } static int match_line(struct grep_opt *opt, char *bol, char *eol, enum grep_context ctx, int collect_hits) { struct grep_pat *p; if (opt->extended) return match_expr(opt, bol, eol, ctx, collect_hits); /* we do not call with collect_hits without being extended */ for (p = opt->pattern_list; p; p = p->next) { if (match_one_pattern(opt, p, bol, eol, ctx)) return 1; } return 0; } static int grep_buffer_1(struct grep_opt *opt, const char *name, char *buf, unsigned long size, int collect_hits) { char *bol = buf; unsigned long left = size; unsigned lno = 1; struct pre_context_line { char *bol; char *eol; } *prev = NULL, *pcl; unsigned last_hit = 0; unsigned last_shown = 0; int binary_match_only = 0; const char *hunk_mark = ""; unsigned count = 0; enum grep_context ctx = GREP_CONTEXT_HEAD; if (buffer_is_binary(buf, size)) { switch (opt->binary) { case GREP_BINARY_DEFAULT: binary_match_only = 1; break; case GREP_BINARY_NOMATCH: return 0; /* Assume unmatch */ break; default: break; } } if (opt->pre_context) prev = xcalloc(opt->pre_context, sizeof(*prev)); if (opt->pre_context || opt->post_context) hunk_mark = "--\n"; while (left) { char *eol, ch; int hit; 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 (binary_match_only) { printf("Binary file %s matches\n", name); return 1; } if (opt->name_only) { printf("%s\n", name); return 1; } /* Hit at this line. If we haven't shown the * pre-context lines, we would need to show them. * When asked to do "count", this still show * the context which is nonsense, but the user * deserves to get that ;-). */ if (opt->pre_context) { unsigned from; if (opt->pre_context < lno) from = lno - opt->pre_context; else from = 1; if (from <= last_shown) from = last_shown + 1; if (last_shown && from != last_shown + 1) printf(hunk_mark); while (from < lno) { pcl = &prev[lno-from-1]; show_line(opt, pcl->bol, pcl->eol, name, from, '-'); from++; } last_shown = lno-1; } if (last_shown && lno != last_shown + 1) printf(hunk_mark); if (!opt->count) show_line(opt, bol, eol, name, lno, ':'); last_shown = last_hit = lno; } else if (last_hit && lno <= last_hit + opt->post_context) { /* If the last hit is within the post context, * we need to show this line. */ if (last_shown && lno != last_shown + 1) printf(hunk_mark); show_line(opt, bol, eol, name, lno, '-'); last_shown = lno; } if (opt->pre_context) { memmove(prev+1, prev, (opt->pre_context-1) * sizeof(*prev)); prev->bol = bol; prev->eol = eol; } next_line: bol = eol + 1; if (!left) break; left--; lno++; } free(prev); 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 */ printf("%s\n", name); return 1; } /* 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) printf("%s:%u\n", name, count); return !!last_hit; } static void clr_hit_marker(struct grep_expr *x) { /* All-hit markers are meaningful only at the very top level * OR node. */ while (1) { x->hit = 0; if (x->node != GREP_NODE_OR) return; x->u.binary.left->hit = 0; x = x->u.binary.right; } } static int chk_hit_marker(struct grep_expr *x) { /* Top level nodes have hit markers. See if they all are hits */ while (1) { if (x->node != GREP_NODE_OR) return x->hit; if (!x->u.binary.left->hit) return 0; x = x->u.binary.right; } } int grep_buffer(struct grep_opt *opt, const char *name, char *buf, unsigned long size) { /* * we do not have to do the two-pass grep when we do not check * buffer-wide "all-match". */ if (!opt->all_match) return grep_buffer_1(opt, name, buf, size, 0); /* Otherwise the toplevel "or" terms hit a bit differently. * We first clear hit markers from them. */ clr_hit_marker(opt->pattern_expression); grep_buffer_1(opt, name, buf, size, 1); if (!chk_hit_marker(opt->pattern_expression)) return 0; return grep_buffer_1(opt, name, buf, size, 0); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
You can’t perform that action at this time.