Raw
1 /*
2 * Copyright (C) 2005 Junio C Hamano
3 * Copyright (C) 2010 Google Inc.
4 */
5
6 #define DISABLE_SIGN_COMPARE_WARNINGS
7
8 #include "git-compat-util.h"
9 #include "diff.h"
10 #include "diffcore.h"
11 #include "xdiff-interface.h"
12 #include "kwset.h"
13 #include "oidset.h"
14 #include "pretty.h"
15 #include "quote.h"
16
17 typedef int (*pickaxe_fn)(mmfile_t *one, mmfile_t *two,
18 struct diff_options *o,
19 regex_t *regexp, kwset_t kws,
20 const struct range_set *ranges);
21
22 struct diffgrep_cb {
23 regex_t *regexp;
24 int hit;
25 };
26
27 static int diffgrep_consume(void *priv, char *line, unsigned long len)
28 {
29 struct diffgrep_cb *data = priv;
30 regmatch_t regmatch;
31
32 if (line[0] != '+' && line[0] != '-')
33 return 0;
34 if (data->hit)
35 BUG("Already matched in diffgrep_consume! Broken xdiff_emit_line_fn?");
36 if (!regexec_buf(data->regexp, line + 1, len - 1, 1,
37 &regmatch, 0)) {
38 data->hit = 1;
39 return 1;
40 }
41 return 0;
42 }
43
44 static int diff_grep(mmfile_t *one, mmfile_t *two,
45 struct diff_options *o,
46 regex_t *regexp, kwset_t kws UNUSED,
47 const struct range_set *ranges)
48 {
49 struct diffgrep_cb ecbdata;
50 xpparam_t xpp;
51 xdemitconf_t xecfg;
52 int ret;
53
54 /*
55 * We have both sides; need to run textual diff and see if the
56 * pattern appears on added/deleted lines. Under -L (ranges set),
57 * forward only the tracked range's lines so the match is scoped.
58 * -G needs only a hit/no-hit answer, so the line-number bookkeeping
59 * the filter does for -L patch and check output is irrelevant here.
60 */
61 memset(&xpp, 0, sizeof(xpp));
62 memset(&xecfg, 0, sizeof(xecfg));
63 ecbdata.regexp = regexp;
64 ecbdata.hit = 0;
65 xecfg.flags = XDL_EMIT_NO_HUNK_HDR;
66 xecfg.ctxlen = o->context;
67 xecfg.interhunkctxlen = o->interhunkcontext;
68
69 /*
70 * An xdiff error might be our "data->hit" from above. See the
71 * comment for xdiff_emit_line_fn in xdiff-interface.h
72 */
73 if (ranges)
74 ret = diff_emit_line_ranges(one, two, ranges, diffgrep_consume,
75 &ecbdata, &xpp, &xecfg);
76 else
77 ret = xdi_diff_outf(one, two, NULL, diffgrep_consume,
78 &ecbdata, &xpp, &xecfg);
79 if (ecbdata.hit)
80 return 1;
81 if (ret)
82 return ret;
83 return 0;
84 }
85
86 static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws,
87 unsigned int limit)
88 {
89 unsigned int cnt = 0;
90 unsigned long sz = mf->size;
91 const char *data = mf->ptr;
92
93 if (regexp) {
94 regmatch_t regmatch;
95 int flags = 0;
96
97 while (sz &&
98 !regexec_buf(regexp, data, sz, 1, &regmatch, flags)) {
99 flags |= REG_NOTBOL;
100 data += regmatch.rm_eo;
101 sz -= regmatch.rm_eo;
102 if (sz && regmatch.rm_so == regmatch.rm_eo) {
103 data++;
104 sz--;
105 }
106 cnt++;
107
108 if (limit && cnt == limit)
109 return cnt;
110 }
111
112 } else { /* Classic exact string match */
113 while (sz) {
114 struct kwsmatch kwsm;
115 size_t offset = kwsexec(kws, data, sz, &kwsm);
116 if (offset == -1)
117 break;
118 sz -= offset + kwsm.size[0];
119 data += offset + kwsm.size[0];
120 cnt++;
121
122 if (limit && cnt == limit)
123 return cnt;
124 }
125 }
126 return cnt;
127 }
128
129 static int has_changes(mmfile_t *one, mmfile_t *two,
130 struct diff_options *o UNUSED,
131 regex_t *regexp, kwset_t kws,
132 const struct range_set *ranges UNUSED)
133 {
134 /*
135 * -S counts needle occurrences in each whole blob. Scoping this to
136 * a -L range is left to a follow-up; for now -S ignores the range.
137 */
138 unsigned int c1 = one ? contains(one, regexp, kws, 0) : 0;
139 unsigned int c2 = two ? contains(two, regexp, kws, c1 + 1) : 0;
140 return c1 != c2;
141 }
142
143 static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
144 regex_t *regexp, kwset_t kws, pickaxe_fn fn)
145 {
146 struct userdiff_driver *textconv_one = NULL;
147 struct userdiff_driver *textconv_two = NULL;
148 mmfile_t mf1, mf2;
149 const struct range_set *ranges;
150 int ret;
151
152 /* ignore unmerged */
153 if (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two))
154 return 0;
155
156 if (o->objfind) {
157 return (DIFF_FILE_VALID(p->one) &&
158 oidset_contains(o->objfind, &p->one->oid)) ||
159 (DIFF_FILE_VALID(p->two) &&
160 oidset_contains(o->objfind, &p->two->oid));
161 }
162
163 if (o->flags.allow_textconv) {
164 textconv_one = get_textconv(o->repo, p->one);
165 textconv_two = get_textconv(o->repo, p->two);
166 }
167
168 /*
169 * If we have an unmodified pair, we know that the count will be the
170 * same and don't even have to load the blobs. Unless textconv is in
171 * play, _and_ we are using two different textconv filters (e.g.,
172 * because a pair is an exact rename with different textconv attributes
173 * for each side, which might generate different content).
174 */
175 if (textconv_one == textconv_two && diff_unmodified_pair(p))
176 return 0;
177
178 if ((o->pickaxe_opts & DIFF_PICKAXE_KIND_G) &&
179 !o->flags.text &&
180 ((!textconv_one && diff_filespec_is_binary(o->repo, p->one)) ||
181 (!textconv_two && diff_filespec_is_binary(o->repo, p->two))))
182 return 0;
183
184 mf1.size = fill_textconv(o->repo, textconv_one, p->one, &mf1.ptr);
185 mf2.size = fill_textconv(o->repo, textconv_two, p->two, &mf2.ptr);
186
187 /*
188 * -L scopes the search to the tracked range, but the range is in
189 * original-file line coordinates that do not map onto textconv
190 * output, so search the whole file when textconv is in play.
191 */
192 ranges = (textconv_one || textconv_two) ? NULL : p->line_ranges;
193 ret = fn(&mf1, &mf2, o, regexp, kws, ranges);
194
195 if (textconv_one)
196 free(mf1.ptr);
197 if (textconv_two)
198 free(mf2.ptr);
199 diff_free_filespec_data(p->one);
200 diff_free_filespec_data(p->two);
201
202 return ret;
203 }
204
205 static void pickaxe(struct diff_queue_struct *q, struct diff_options *o,
206 regex_t *regexp, kwset_t kws, pickaxe_fn fn)
207 {
208 int i;
209 struct diff_queue_struct outq = DIFF_QUEUE_INIT;
210
211 if (o->pickaxe_opts & DIFF_PICKAXE_ALL) {
212 /* Showing the whole changeset if needle exists */
213 for (i = 0; i < q->nr; i++) {
214 struct diff_filepair *p = q->queue[i];
215 if (pickaxe_match(p, o, regexp, kws, fn))
216 return; /* do not munge the queue */
217 }
218
219 /*
220 * Otherwise we will clear the whole queue by copying
221 * the empty outq at the end of this function, but
222 * first clear the current entries in the queue.
223 */
224 for (i = 0; i < q->nr; i++)
225 diff_free_filepair(q->queue[i]);
226 } else {
227 /* Showing only the filepairs that have the needle */
228 for (i = 0; i < q->nr; i++) {
229 struct diff_filepair *p = q->queue[i];
230 if (pickaxe_match(p, o, regexp, kws, fn))
231 diff_q(&outq, p);
232 else
233 diff_free_filepair(p);
234 }
235 }
236
237 free(q->queue);
238 *q = outq;
239 }
240
241 static void regcomp_or_die(regex_t *regex, const char *needle, int cflags)
242 {
243 int err = regcomp(regex, needle, cflags);
244 if (err) {
245 /* The POSIX.2 people are surely sick */
246 char errbuf[1024];
247 regerror(err, regex, errbuf, 1024);
248 die("invalid regex: %s", errbuf);
249 }
250 }
251
252 void diffcore_pickaxe(struct diff_options *o)
253 {
254 const char *needle = o->pickaxe;
255 int opts = o->pickaxe_opts;
256 regex_t regex, *regexp = NULL;
257 kwset_t kws = NULL;
258 pickaxe_fn fn;
259
260 if (opts & ~DIFF_PICKAXE_KIND_OBJFIND &&
261 (!needle || !*needle))
262 BUG("should have needle under -G or -S");
263 if (opts & (DIFF_PICKAXE_REGEX | DIFF_PICKAXE_KIND_G)) {
264 int cflags = REG_EXTENDED | REG_NEWLINE;
265 if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE)
266 cflags |= REG_ICASE;
267 regcomp_or_die(&regex, needle, cflags);
268 regexp = &regex;
269
270 if (opts & DIFF_PICKAXE_KIND_G)
271 fn = diff_grep;
272 else if (opts & DIFF_PICKAXE_REGEX)
273 fn = has_changes;
274 else
275 /*
276 * We don't need to check the combination of
277 * -G and --pickaxe-regex, by the time we get
278 * here diff.c has already died if they're
279 * combined. See the usage tests in
280 * t4209-log-pickaxe.sh.
281 */
282 BUG("unreachable");
283 } else if (opts & DIFF_PICKAXE_KIND_S) {
284 if (o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE &&
285 has_non_ascii(needle)) {
286 struct strbuf sb = STRBUF_INIT;
287 int cflags = REG_NEWLINE | REG_ICASE;
288
289 basic_regex_quote_buf(&sb, needle);
290 regcomp_or_die(&regex, sb.buf, cflags);
291 strbuf_release(&sb);
292 regexp = &regex;
293 } else {
294 kws = kwsalloc(o->pickaxe_opts & DIFF_PICKAXE_IGNORE_CASE
295 ? tolower_trans_tbl : NULL);
296 kwsincr(kws, needle, strlen(needle));
297 kwsprep(kws);
298 }
299 fn = has_changes;
300 } else if (opts & DIFF_PICKAXE_KIND_OBJFIND) {
301 fn = NULL;
302 } else {
303 BUG("unknown pickaxe_opts flag");
304 }
305
306 pickaxe(&diff_queued_diff, o, regexp, kws, fn);
307
308 if (regexp)
309 regfree(regexp);
310 if (kws)
311 kwsfree(kws);
312 return;
313 }