Merge branch 'rs/memmem' into maint
[git] / diffcore-pickaxe.c
1 /*
2  * Copyright (C) 2005 Junio C Hamano
3  */
4 #include "cache.h"
5 #include "diff.h"
6 #include "diffcore.h"
7
8 static unsigned int contains(struct diff_filespec *one,
9                              const char *needle, unsigned long len,
10                              regex_t *regexp)
11 {
12         unsigned int cnt;
13         unsigned long sz;
14         const char *data;
15         if (diff_populate_filespec(one, 0))
16                 return 0;
17         if (!len)
18                 return 0;
19
20         sz = one->size;
21         data = one->data;
22         cnt = 0;
23
24         if (regexp) {
25                 regmatch_t regmatch;
26                 int flags = 0;
27
28                 while (*data && !regexec(regexp, data, 1, &regmatch, flags)) {
29                         flags |= REG_NOTBOL;
30                         data += regmatch.rm_so;
31                         if (*data) data++;
32                         cnt++;
33                 }
34
35         } else { /* Classic exact string match */
36                 while (sz) {
37                         const char *found = memmem(data, sz, needle, len);
38                         if (!found)
39                                 break;
40                         sz -= found - data + len;
41                         data = found + len;
42                         cnt++;
43                 }
44         }
45         diff_free_filespec_data(one);
46         return cnt;
47 }
48
49 void diffcore_pickaxe(const char *needle, int opts)
50 {
51         struct diff_queue_struct *q = &diff_queued_diff;
52         unsigned long len = strlen(needle);
53         int i, has_changes;
54         regex_t regex, *regexp = NULL;
55         struct diff_queue_struct outq;
56         outq.queue = NULL;
57         outq.nr = outq.alloc = 0;
58
59         if (opts & DIFF_PICKAXE_REGEX) {
60                 int err;
61                 err = regcomp(&regex, needle, REG_EXTENDED | REG_NEWLINE);
62                 if (err) {
63                         /* The POSIX.2 people are surely sick */
64                         char errbuf[1024];
65                         regerror(err, &regex, errbuf, 1024);
66                         regfree(&regex);
67                         die("invalid pickaxe regex: %s", errbuf);
68                 }
69                 regexp = &regex;
70         }
71
72         if (opts & DIFF_PICKAXE_ALL) {
73                 /* Showing the whole changeset if needle exists */
74                 for (i = has_changes = 0; !has_changes && i < q->nr; i++) {
75                         struct diff_filepair *p = q->queue[i];
76                         if (!DIFF_FILE_VALID(p->one)) {
77                                 if (!DIFF_FILE_VALID(p->two))
78                                         continue; /* ignore unmerged */
79                                 /* created */
80                                 if (contains(p->two, needle, len, regexp))
81                                         has_changes++;
82                         }
83                         else if (!DIFF_FILE_VALID(p->two)) {
84                                 if (contains(p->one, needle, len, regexp))
85                                         has_changes++;
86                         }
87                         else if (!diff_unmodified_pair(p) &&
88                                  contains(p->one, needle, len, regexp) !=
89                                  contains(p->two, needle, len, regexp))
90                                 has_changes++;
91                 }
92                 if (has_changes)
93                         return; /* not munge the queue */
94
95                 /* otherwise we will clear the whole queue
96                  * by copying the empty outq at the end of this
97                  * function, but first clear the current entries
98                  * in the queue.
99                  */
100                 for (i = 0; i < q->nr; i++)
101                         diff_free_filepair(q->queue[i]);
102         }
103         else
104                 /* Showing only the filepairs that has the needle */
105                 for (i = 0; i < q->nr; i++) {
106                         struct diff_filepair *p = q->queue[i];
107                         has_changes = 0;
108                         if (!DIFF_FILE_VALID(p->one)) {
109                                 if (!DIFF_FILE_VALID(p->two))
110                                         ; /* ignore unmerged */
111                                 /* created */
112                                 else if (contains(p->two, needle, len, regexp))
113                                         has_changes = 1;
114                         }
115                         else if (!DIFF_FILE_VALID(p->two)) {
116                                 if (contains(p->one, needle, len, regexp))
117                                         has_changes = 1;
118                         }
119                         else if (!diff_unmodified_pair(p) &&
120                                  contains(p->one, needle, len, regexp) !=
121                                  contains(p->two, needle, len, regexp))
122                                 has_changes = 1;
123
124                         if (has_changes)
125                                 diff_q(&outq, p);
126                         else
127                                 diff_free_filepair(p);
128                 }
129
130         if (opts & DIFF_PICKAXE_REGEX) {
131                 regfree(&regex);
132         }
133
134         free(q->queue);
135         *q = outq;
136         return;
137 }