diffcore-pickaxe.c 6.98 KB
Newer Older
1 2
/*
 * Copyright (C) 2005 Junio C Hamano
3
 * Copyright (C) 2010 Google Inc.
4 5 6 7
 */
#include "cache.h"
#include "diff.h"
#include "diffcore.h"
8
#include "xdiff-interface.h"
Fredrik K's avatar
Fredrik K committed
9
#include "kwset.h"
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

struct diffgrep_cb {
	regex_t *regexp;
	int hit;
};

static void diffgrep_consume(void *priv, char *line, unsigned long len)
{
	struct diffgrep_cb *data = priv;
	regmatch_t regmatch;
	int hold;

	if (line[0] != '+' && line[0] != '-')
		return;
	if (data->hit)
		/*
		 * NEEDSWORK: we should have a way to terminate the
		 * caller early.
		 */
		return;
	/* Yuck -- line ought to be "const char *"! */
	hold = line[len];
	line[len] = '\0';
	data->hit = !regexec(data->regexp, line + 1, 1, &regmatch, 0);
	line[len] = hold;
}

static void fill_one(struct diff_filespec *one,
		     mmfile_t *mf, struct userdiff_driver **textconv)
{
	if (DIFF_FILE_VALID(one)) {
		*textconv = get_textconv(one);
		mf->size = fill_textconv(*textconv, one, &mf->ptr);
	} else {
		memset(mf, 0, sizeof(*mf));
	}
}

static int diff_grep(struct diff_filepair *p, regex_t *regexp, struct diff_options *o)
{
	regmatch_t regmatch;
	struct userdiff_driver *textconv_one = NULL;
	struct userdiff_driver *textconv_two = NULL;
	mmfile_t mf1, mf2;
	int hit;

	if (diff_unmodified_pair(p))
		return 0;

	fill_one(p->one, &mf1, &textconv_one);
	fill_one(p->two, &mf2, &textconv_two);

	if (!mf1.ptr) {
		if (!mf2.ptr)
			return 0; /* ignore unmerged */
		/* created "two" -- does it have what we are looking for? */
		hit = !regexec(regexp, p->two->data, 1, &regmatch, 0);
	} else if (!mf2.ptr) {
		/* removed "one" -- did it have what we are looking for? */
		hit = !regexec(regexp, p->one->data, 1, &regmatch, 0);
	} else {
		/*
		 * We have both sides; need to run textual diff and see if
		 * the pattern appears on added/deleted lines.
		 */
		struct diffgrep_cb ecbdata;
		xpparam_t xpp;
		xdemitconf_t xecfg;

		memset(&xpp, 0, sizeof(xpp));
		memset(&xecfg, 0, sizeof(xecfg));
		ecbdata.regexp = regexp;
		ecbdata.hit = 0;
		xecfg.ctxlen = o->context;
		xecfg.interhunkctxlen = o->interhunkcontext;
		xdi_diff_outf(&mf1, &mf2, diffgrep_consume, &ecbdata,
			      &xpp, &xecfg);
		hit = ecbdata.hit;
	}
	if (textconv_one)
		free(mf1.ptr);
	if (textconv_two)
		free(mf2.ptr);
	return hit;
}

static void diffcore_pickaxe_grep(struct diff_options *o)
{
	struct diff_queue_struct *q = &diff_queued_diff;
René Scharfe's avatar
René Scharfe committed
99
	int i, err;
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
	regex_t regex;
	struct diff_queue_struct outq;
	outq.queue = NULL;
	outq.nr = outq.alloc = 0;

	err = regcomp(&regex, o->pickaxe, REG_EXTENDED | REG_NEWLINE);
	if (err) {
		char errbuf[1024];
		regerror(err, &regex, errbuf, 1024);
		regfree(&regex);
		die("invalid log-grep regex: %s", errbuf);
	}

	if (o->pickaxe_opts & DIFF_PICKAXE_ALL) {
		/* Showing the whole changeset if needle exists */
René Scharfe's avatar
René Scharfe committed
115
		for (i = 0; i < q->nr; i++) {
116 117
			struct diff_filepair *p = q->queue[i];
			if (diff_grep(p, &regex, o))
René Scharfe's avatar
René Scharfe committed
118
				goto out; /* do not munge the queue */
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
		}

		/*
		 * Otherwise we will clear the whole queue by copying
		 * the empty outq at the end of this function, but
		 * first clear the current entries in the queue.
		 */
		for (i = 0; i < q->nr; i++)
			diff_free_filepair(q->queue[i]);
	} else {
		/* Showing only the filepairs that has the needle */
		for (i = 0; i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
			if (diff_grep(p, &regex, o))
				diff_q(&outq, p);
			else
				diff_free_filepair(p);
		}
	}

	free(q->queue);
	*q = outq;
René Scharfe's avatar
René Scharfe committed
141 142 143

 out:
	regfree(&regex);
144 145
	return;
}
146

147
static unsigned int contains(struct diff_filespec *one,
148
			     const char *needle, unsigned long len,
Fredrik K's avatar
Fredrik K committed
149
			     regex_t *regexp, kwset_t kws)
150
{
151
	unsigned int cnt;
152
	unsigned long sz;
153
	const char *data;
154 155
	if (!len)
		return 0;
156 157
	if (diff_populate_filespec(one, 0))
		return 0;
158

159 160
	sz = one->size;
	data = one->data;
161 162
	cnt = 0;

163 164 165 166
	if (regexp) {
		regmatch_t regmatch;
		int flags = 0;

167
		assert(data[sz] == '\0');
168 169
		while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
			flags |= REG_NOTBOL;
170 171 172
			data += regmatch.rm_eo;
			if (*data && regmatch.rm_so == regmatch.rm_eo)
				data++;
173 174
			cnt++;
		}
175 176

	} else { /* Classic exact string match */
177
		while (sz) {
Fredrik K's avatar
Fredrik K committed
178 179 180
			size_t offset = kwsexec(kws, data, sz, NULL);
			const char *found;
			if (offset == -1)
181
				break;
Fredrik K's avatar
Fredrik K committed
182 183
			else
				found = data + offset;
184 185 186
			sz -= found - data + len;
			data = found + len;
			cnt++;
187
		}
188
	}
189
	diff_free_filespec_data(one);
190
	return cnt;
191 192
}

193
static void diffcore_pickaxe_count(struct diff_options *o)
194
{
195 196
	const char *needle = o->pickaxe;
	int opts = o->pickaxe_opts;
197
	struct diff_queue_struct *q = &diff_queued_diff;
198
	unsigned long len = strlen(needle);
199
	int i, has_changes;
200
	regex_t regex, *regexp = NULL;
Fredrik K's avatar
Fredrik K committed
201
	kwset_t kws = NULL;
202
	struct diff_queue_struct outq;
Bo Yang's avatar
Bo Yang committed
203
	DIFF_QUEUE_CLEAR(&outq);
204

205 206 207 208 209 210 211 212 213 214 215
	if (opts & DIFF_PICKAXE_REGEX) {
		int err;
		err = regcomp(&regex, needle, REG_EXTENDED | REG_NEWLINE);
		if (err) {
			/* The POSIX.2 people are surely sick */
			char errbuf[1024];
			regerror(err, &regex, errbuf, 1024);
			regfree(&regex);
			die("invalid pickaxe regex: %s", errbuf);
		}
		regexp = &regex;
Fredrik K's avatar
Fredrik K committed
216 217 218 219
	} else {
		kws = kwsalloc(NULL);
		kwsincr(kws, needle, len);
		kwsprep(kws);
220 221
	}

222 223 224 225 226 227 228 229
	if (opts & DIFF_PICKAXE_ALL) {
		/* Showing the whole changeset if needle exists */
		for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
			if (!DIFF_FILE_VALID(p->one)) {
				if (!DIFF_FILE_VALID(p->two))
					continue; /* ignore unmerged */
				/* created */
Fredrik K's avatar
Fredrik K committed
230
				if (contains(p->two, needle, len, regexp, kws))
231 232 233
					has_changes++;
			}
			else if (!DIFF_FILE_VALID(p->two)) {
Fredrik K's avatar
Fredrik K committed
234
				if (contains(p->one, needle, len, regexp, kws))
235 236 237
					has_changes++;
			}
			else if (!diff_unmodified_pair(p) &&
Fredrik K's avatar
Fredrik K committed
238 239
				 contains(p->one, needle, len, regexp, kws) !=
				 contains(p->two, needle, len, regexp, kws))
240
				has_changes++;
241
		}
242 243 244 245 246 247 248 249 250 251 252
		if (has_changes)
			return; /* not munge the queue */

		/* otherwise we will clear the whole queue
		 * by copying the empty outq at the end of this
		 * function, but first clear the current entries
		 * in the queue.
		 */
		for (i = 0; i < q->nr; i++)
			diff_free_filepair(q->queue[i]);
	}
Junio C Hamano's avatar
Junio C Hamano committed
253
	else
254 255 256 257 258 259 260 261
		/* Showing only the filepairs that has the needle */
		for (i = 0; i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
			has_changes = 0;
			if (!DIFF_FILE_VALID(p->one)) {
				if (!DIFF_FILE_VALID(p->two))
					; /* ignore unmerged */
				/* created */
Fredrik K's avatar
Fredrik K committed
262 263
				else if (contains(p->two, needle, len, regexp,
						  kws))
264 265 266
					has_changes = 1;
			}
			else if (!DIFF_FILE_VALID(p->two)) {
Fredrik K's avatar
Fredrik K committed
267
				if (contains(p->one, needle, len, regexp, kws))
268 269 270
					has_changes = 1;
			}
			else if (!diff_unmodified_pair(p) &&
Fredrik K's avatar
Fredrik K committed
271 272
				 contains(p->one, needle, len, regexp, kws) !=
				 contains(p->two, needle, len, regexp, kws))
273 274 275
				has_changes = 1;

			if (has_changes)
Junio C Hamano's avatar
Junio C Hamano committed
276
				diff_q(&outq, p);
277 278
			else
				diff_free_filepair(p);
279
		}
280

281
	if (opts & DIFF_PICKAXE_REGEX)
282
		regfree(&regex);
Fredrik K's avatar
Fredrik K committed
283 284
	else
		kwsfree(kws);
285

286 287 288 289
	free(q->queue);
	*q = outq;
	return;
}
290 291 292 293 294

void diffcore_pickaxe(struct diff_options *o)
{
	/* Might want to warn when both S and G are on; I don't care... */
	if (o->pickaxe_opts & DIFF_PICKAXE_KIND_G)
295
		diffcore_pickaxe_grep(o);
296
	else
297
		diffcore_pickaxe_count(o);
298
}