Add heuristic to take shortcut when too slow.
[wiggle/upstream.git] / extract.c
blobac61421fccc8a4b9b87e16bde2e4fa74dadaa9a3
1 /*
2 * wiggle - apply rejected patches
4 * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
5 * Copyright (C) 2010-2013 Neil Brown <neilb@suse.de>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program.
21 * Author: Neil Brown
22 * Email: <neilb@suse.de>
26 * split patch or merge files.
29 #include "wiggle.h"
30 #include <stdlib.h>
32 /* skip 'cp' past the new '\n', or all the way to 'end' */
33 static void skip_eol(char **cp, char *end)
35 char *c = *cp;
36 while (c < end && *c != '\n')
37 c++;
38 if (c < end)
39 c++;
40 *cp = c;
43 /* copy one line, or to end, from 'cp' into the stream, extending
44 * the stream.
46 static void copyline(struct stream *s, char **cp, char *end)
48 char *from = *cp;
49 char *to = s->body+s->len;
51 while (from < end && *from != '\n')
52 *to++ = *from++;
53 if (from < end)
54 *to++ = *from++;
55 s->len = to-s->body;
56 *cp = from;
59 int split_patch(struct stream f, struct stream *f1, struct stream *f2)
61 struct stream r1, r2;
62 int chunks = 0;
63 char *cp, *end;
64 int state = 0;
65 int acnt = 0, bcnt = 0;
66 int a, b, c, d;
67 int lineno = 0;
68 char before[100], after[100];
69 char func[100];
71 f1->body = f2->body = NULL;
73 r1.body = xmalloc(f.len);
74 r2.body = xmalloc(f.len);
75 r1.len = r2.len = 0;
76 func[0] = 0;
78 cp = f.body;
79 end = f.body+f.len;
80 while (cp < end) {
81 /* state:
82 * 0 not in a patch
83 * 1 first half of context
84 * 2 second half of context
85 * 3 unified
87 lineno++;
88 switch (state) {
89 case 0:
90 if (sscanf(cp, "@@ -%99s +%99s @@%99[^\n]", before, after, func) >= 2) {
91 int ok = 1;
92 if (sscanf(before, "%d,%d", &a, &b) == 2)
93 acnt = b;
94 else if (sscanf(before, "%d", &a) == 1)
95 acnt = 1;
96 else
97 ok = 0;
99 if (sscanf(after, "%d,%d", &c, &d) == 2)
100 bcnt = d;
101 else if (sscanf(after, "%d", &c) == 1)
102 bcnt = 1;
103 else
104 ok = 0;
105 if (ok)
106 state = 3;
107 else
108 state = 0;
109 } else if (sscanf(cp, "*** %d,%d ****", &a, &b) == 2) {
110 acnt = b-a+1;
111 state = 1;
112 } else if (sscanf(cp, "--- %d,%d ----", &c, &d) == 2) {
113 bcnt = d-c+1;
114 state = 2;
115 } else
116 sscanf(cp, "***************%99[^\n]", func);
118 skip_eol(&cp, end);
119 if (state == 1 || state == 3) {
120 char *f;
121 int slen;
122 /* Reserve enough space for 3 integers separated
123 * by a single space, and prefixed and terminated
124 * with a null character.
126 char buf[(3*12)+1];
127 buf[0] = 0;
128 chunks++;
129 slen = sprintf(buf+1, "%5d %5d %5d", chunks, a, acnt)+1;
130 memcpy(r1.body+r1.len, buf, slen);
131 r1.len += slen;
132 f = func;
133 while (*f == ' ')
134 f++;
135 if (*f) {
136 r1.body[r1.len++] = ' ';
137 strcpy(r1.body + r1.len, f);
138 r1.len += strlen(f);
140 r1.body[r1.len++] = '\n';
141 r1.body[r1.len++] = '\0';
143 if (state == 2 || state == 3) {
144 int slen;
145 /* Reserve enough space for 3 integers separated
146 * by a single space, prefixed with a null character
147 * and terminated with a new line and null character.
149 char buf[(3*12)+2];
150 buf[0] = 0;
151 slen = sprintf(buf+1, "%5d %5d %5d\n", chunks, c, bcnt)+2;
152 memcpy(r2.body+r2.len, buf, slen);
153 r2.len += slen;
155 if (state)
156 func[0] = 0;
157 break;
158 case 1:
159 if ((*cp == ' ' || *cp == '!' || *cp == '-' || *cp == '+')
160 && cp[1] == ' ') {
161 cp += 2;
162 copyline(&r1, &cp, end);
163 acnt--;
164 if (acnt == 0)
165 state = 0;
166 } else {
167 fprintf(stderr, "%s: bad context patch at line %d\n",
168 Cmd, lineno);
169 return 0;
171 break;
172 case 2:
173 if ((*cp == ' ' || *cp == '!' || *cp == '-' || *cp == '+')
174 && cp[1] == ' ') {
175 cp += 2;
176 copyline(&r2, &cp, end);
177 bcnt--;
178 if (bcnt == 0)
179 state = 0;
180 } else {
181 fprintf(stderr, "%s: bad context patch/2 at line %d\n",
182 Cmd, lineno);
183 return 0;
185 break;
186 case 3:
187 if (*cp == ' ') {
188 char *cp2;
189 cp++;
190 cp2 = cp;
191 copyline(&r1, &cp, end);
192 copyline(&r2, &cp2, end);
193 acnt--; bcnt--;
194 } else if (*cp == '-') {
195 cp++;
196 copyline(&r1, &cp, end);
197 acnt--;
198 } else if (*cp == '+') {
199 cp++;
200 copyline(&r2, &cp, end);
201 bcnt--;
202 } else if (*cp == '\n') {
203 /* Empty line - treat like " \n" - a blank line in both */
204 char *cp2 = cp;
205 copyline(&r1, &cp, end);
206 copyline(&r2, &cp2, end);
207 acnt --; bcnt--;
208 } else {
209 fprintf(stderr, "%s: bad unified patch at line %d\n",
210 Cmd, lineno);
211 return 0;
213 if (acnt <= 0 && bcnt <= 0)
214 state = 0;
215 break;
218 if (r1.len > f.len || r2.len > f.len)
219 abort();
220 *f1 = r1;
221 *f2 = r2;
222 return chunks;
226 * extract parts of a "diff3 -m" or "wiggle -m" output
228 int split_merge(struct stream f, struct stream *f1, struct stream *f2, struct stream *f3)
230 int state = 0;
231 char *cp, *end;
232 struct stream r1, r2, r3;
233 f1->body = NULL;
234 f2->body = NULL;
236 r1.body = xmalloc(f.len);
237 r2.body = xmalloc(f.len);
238 r3.body = xmalloc(f.len);
239 r1.len = r2.len = r3.len = 0;
241 cp = f.body;
242 end = f.body+f.len;
243 while (cp < end) {
244 /* state:
245 * 0 not in conflict
246 * 1 in file 1 of conflict
247 * 2 in file 2 of conflict
248 * 3 in file 3 of conflict
249 * 4 in file 2 but expecting 1/3 next
250 * 5 in file 1/3
252 int len = end-cp;
253 switch (state) {
254 case 0:
255 if (len >= 8 &&
256 strncmp(cp, "<<<<<<<", 7) == 0 &&
257 (cp[7] == ' ' || cp[7] == '\n')
259 char *peek;
260 state = 1;
261 skip_eol(&cp, end);
262 /* diff3 will do something a bit strange in
263 * the 1st and 3rd sections are the same.
264 * it reports
265 * <<<<<<<
266 * 2nd
267 * =======
268 * 1st and 3rd
269 * >>>>>>>
270 * Without a ||||||| at all.
271 * so to know if we are in '1' or '2', skip forward
272 * having a peek.
274 peek = cp;
275 while (peek < end) {
276 if (end-peek >= 8 &&
277 (peek[7] == ' ' || peek[7] == '\n')) {
278 if (strncmp(peek, "|||||||", 7) == 0 ||
279 strncmp(peek, ">>>>>>>", 7) == 0)
280 break;
281 else if (strncmp(peek, "=======", 7) == 0) {
282 state = 4;
283 break;
286 skip_eol(&peek, end);
288 } else {
289 char *cp2 = cp;
290 copyline(&r1, &cp2, end);
291 cp2 = cp;
292 copyline(&r2, &cp2, end);
293 copyline(&r3, &cp, end);
295 break;
296 case 1:
297 if (len >= 8 &&
298 strncmp(cp, "|||||||", 7) == 0 &&
299 (cp[7] == ' ' || cp[7] == '\n')
301 state = 2;
302 skip_eol(&cp, end);
303 } else
304 copyline(&r1, &cp, end);
305 break;
306 case 2:
307 if (len >= 8 &&
308 strncmp(cp, "=======", 7) == 0 &&
309 (cp[7] == ' ' || cp[7] == '\n')
311 state = 3;
312 skip_eol(&cp, end);
313 } else
314 copyline(&r2, &cp, end);
315 break;
316 case 3:
317 if (len >= 8 &&
318 strncmp(cp, ">>>>>>>", 7) == 0 &&
319 (cp[7] == ' ' || cp[7] == '\n')
321 state = 0;
322 skip_eol(&cp, end);
323 } else
324 copyline(&r3, &cp, end);
325 break;
326 case 4:
327 if (len >= 8 &&
328 strncmp(cp, "=======", 7) == 0 &&
329 (cp[7] == ' ' || cp[7] == '\n')
331 state = 5;
332 skip_eol(&cp, end);
333 } else
334 copyline(&r2, &cp, end);
335 break;
336 case 5:
337 if (len >= 8 &&
338 strncmp(cp, ">>>>>>>", 7) == 0 &&
339 (cp[7] == ' ' || cp[7] == '\n')
341 state = 0;
342 skip_eol(&cp, end);
343 } else {
344 char *t = cp;
345 copyline(&r1, &t, end);
346 copyline(&r3, &cp, end);
348 break;
351 if (cp > end)
352 abort();
353 *f1 = r1;
354 *f2 = r2;
355 *f3 = r3;
356 return state == 0;