diffcore-pickaxe.c 6.54 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, struct diff_options *o,
Fredrik K's avatar
Fredrik K committed
148
			     regex_t *regexp, kwset_t kws)
149
{
150
	unsigned int cnt;
151
	unsigned long sz;
152
	const char *data;
153
	if (!o->pickaxe[0])
154
		return 0;
155 156
	if (diff_populate_filespec(one, 0))
		return 0;
157

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

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

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

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

193 194
static int has_changes(struct diff_filepair *p, struct diff_options *o,
		       regex_t *regexp, kwset_t kws)
195 196 197 198 199
{
	if (!DIFF_FILE_VALID(p->one)) {
		if (!DIFF_FILE_VALID(p->two))
			return 0; /* ignore unmerged */
		/* created */
200
		return contains(p->two, o, regexp, kws) != 0;
201 202
	}
	if (!DIFF_FILE_VALID(p->two))
203
		return contains(p->one, o, regexp, kws) != 0;
204
	if (!diff_unmodified_pair(p)) {
205 206
		return contains(p->one, o, regexp, kws) !=
		       contains(p->two, o, regexp, kws);
207 208 209 210
	}
	return 0;
}

211
static void diffcore_pickaxe_count(struct diff_options *o)
212
{
213 214
	const char *needle = o->pickaxe;
	int opts = o->pickaxe_opts;
215
	struct diff_queue_struct *q = &diff_queued_diff;
216
	unsigned long len = strlen(needle);
217
	int i;
218
	regex_t regex, *regexp = NULL;
Fredrik K's avatar
Fredrik K committed
219
	kwset_t kws = NULL;
220
	struct diff_queue_struct outq;
Bo Yang's avatar
Bo Yang committed
221
	DIFF_QUEUE_CLEAR(&outq);
222

223 224 225 226 227 228 229 230 231 232 233
	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
234 235 236 237
	} else {
		kws = kwsalloc(NULL);
		kwsincr(kws, needle, len);
		kwsprep(kws);
238 239
	}

240 241
	if (opts & DIFF_PICKAXE_ALL) {
		/* Showing the whole changeset if needle exists */
René Scharfe's avatar
René Scharfe committed
242
		for (i = 0; i < q->nr; i++) {
243
			struct diff_filepair *p = q->queue[i];
244
			if (has_changes(p, o, regexp, kws))
René Scharfe's avatar
René Scharfe committed
245
				goto out; /* do not munge the queue */
246
		}
247 248 249 250 251 252 253 254 255

		/* 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
256
	else
257 258 259
		/* Showing only the filepairs that has the needle */
		for (i = 0; i < q->nr; i++) {
			struct diff_filepair *p = q->queue[i];
260
			if (has_changes(p, o, regexp, kws))
Junio C Hamano's avatar
Junio C Hamano committed
261
				diff_q(&outq, p);
262 263
			else
				diff_free_filepair(p);
264
		}
265

René Scharfe's avatar
René Scharfe committed
266 267 268 269
	free(q->queue);
	*q = outq;

 out:
270
	if (opts & DIFF_PICKAXE_REGEX)
271
		regfree(&regex);
Fredrik K's avatar
Fredrik K committed
272 273
	else
		kwsfree(kws);
274 275
	return;
}
276 277 278 279 280

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)
281
		diffcore_pickaxe_grep(o);
282
	else
283
		diffcore_pickaxe_count(o);
284
}