]> git.neil.brown.name Git - wiggle.git/blob - extract.c
extract: allow blank lines in unified diffs.
[wiggle.git] / extract.c
1 /*
2  * wiggle - apply rejected patches
3  *
4  * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
5  * Copyright (C) 2010-2013 Neil Brown <neilb@suse.de>
6  *
7  *
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.
12  *
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.
17  *
18  *    You should have received a copy of the GNU General Public License
19  *    along with this program.
20  *
21  *    Author: Neil Brown
22  *    Email: <neilb@suse.de>
23  */
24
25 /*
26  * split patch or merge files.
27  */
28
29 #include        "wiggle.h"
30 #include        <stdlib.h>
31
32 /* skip 'cp' past the new '\n', or all the way to 'end' */
33 static void skip_eol(char **cp, char *end)
34 {
35         char *c = *cp;
36         while (c < end && *c != '\n')
37                 c++;
38         if (c < end)
39                 c++;
40         *cp = c;
41 }
42
43 /* copy one line, or to end, from 'cp' into the stream, extending
44  * the stream.
45  */
46 static void copyline(struct stream *s, char **cp, char *end)
47 {
48         char *from = *cp;
49         char *to = s->body+s->len;
50
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;
57 }
58
59 int split_patch(struct stream f, struct stream *f1, struct stream *f2)
60 {
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];
70
71         f1->body = f2->body = NULL;
72
73         r1.body = xmalloc(f.len);
74         r2.body = xmalloc(f.len);
75         r1.len = r2.len = 0;
76         func[0] = 0;
77
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
86                  */
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;
98
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);
117
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.
125                                  */
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);
139                                 }
140                                 r1.body[r1.len++] = '\n';
141                                 r1.body[r1.len++] = '\0';
142                         }
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.
148                                  */
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;
154                         }
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;
170                         }
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;
184                         }
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;
212                         }
213                         if (acnt <= 0 && bcnt <= 0)
214                                 state = 0;
215                         break;
216                 }
217         }
218         if (r1.len > f.len || r2.len > f.len)
219                 abort();
220         *f1 = r1;
221         *f2 = r2;
222         return chunks;
223 }
224
225 /*
226  * extract parts of a "diff3 -m" or "wiggle -m" output
227  */
228 int split_merge(struct stream f, struct stream *f1, struct stream *f2, struct stream *f3)
229 {
230         int state = 0;
231         char *cp, *end;
232         struct stream r1, r2, r3;
233         f1->body = NULL;
234         f2->body = NULL;
235
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;
240
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
251                  */
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')
258                                 ) {
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.
273                                  */
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;
284                                                 }
285                                         }
286                                         skip_eol(&peek, end);
287                                 }
288                         } else {
289                                 char *cp2 = cp;
290                                 copyline(&r1, &cp2, end);
291                                 cp2 = cp;
292                                 copyline(&r2, &cp2, end);
293                                 copyline(&r3, &cp, end);
294                         }
295                         break;
296                 case 1:
297                         if (len >= 8 &&
298                             strncmp(cp, "|||||||", 7) == 0 &&
299                             (cp[7] == ' ' || cp[7] == '\n')
300                                 ) {
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')
310                                 ) {
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')
320                                 ) {
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')
330                                 ) {
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')
340                                 ) {
341                                 state = 0;
342                                 skip_eol(&cp, end);
343                         } else {
344                                 char *t = cp;
345                                 copyline(&r1, &t, end);
346                                 copyline(&r3, &cp, end);
347                         }
348                         break;
349                 }
350         }
351         if (cp > end)
352                 abort();
353         *f1 = r1;
354         *f2 = r2;
355         *f3 = r3;
356         return state == 0;
357 }