Remove all trailing blanks.
[wiggle/upstream.git] / wiggle.c
blob975e39e4915f2e70628fb1d84c6600e7f145058d
1 /*
2 * wiggle - apply rejected patches
4 * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * Author: Neil Brown
22 * Email: <neilb@cse.unsw.edu.au>
23 * Paper: Neil Brown
24 * School of Computer Science and Engineering
25 * The University of New South Wales
26 * Sydney, 2052
27 * Australia
31 * Wiggle is a tool for working with patches that don't quite apply properly.
32 * It provides functionality similar to 'diff' and 'merge' but can
33 * work at the level of individual words thus allowing the merging of
34 * two changes that affect the same line, but not the same parts of that line.
36 * Wiggle can also read patch and merge files. Unlike 'merge' it does not
37 * need to be given three separate files, but can be given a file and a patch
38 * and it will extract the pieces of the two two other files that it needs from
39 * the patch.
41 * Wiggle performs one of three core function:
42 * --extract -x extract part of a patch or merge file
43 * --diff -d report differences between two files
44 * --merge -m merge the changes between two files into a third file
46 * To perform these, wiggle requires 1, 2, or 3 input streams respectively.
47 * I can get there from individual files, from a diff (unified or context) or
48 * from a merge file.
50 * For merge:
51 * If one file is given, it is a merge file (output of 'merge').
52 * If two files are given, the second is assumed to be a patch, the first is a normal file.
53 * If three files are given, they are taken to be normal files.
55 * For diff:
56 * If one file is given, it is a patch
57 * If two files are given, they are normal files.
59 * For extract:
60 * Only one file can be given. -p indicates it is a patch, otherwise it is a merge.
61 * One of the flags -1 -2 or -3 must also be given and they indicate which
62 * part of the patch or merge to extract.
64 * Difference calculate and merging is performed on lines (-l) or words (-w).
65 * In the case of -w, an initial diff is computed based on non-trivial words.
66 * i.e. spaces are ignored
67 * This diff is computed from the ends of the file and is used to find a suitable
68 * starting point and range. Then a more precise diff is computed over that
69 * restricted range
71 * Other options available are:
72 * --replace -r replace first file with result of merge.
73 * --help -h provide help
74 * --version -v version
76 * Defaults are --merge --words
80 #include "wiggle.h"
81 #include <errno.h>
82 #include <string.h>
83 #include <fcntl.h>
84 #include <unistd.h>
85 #include <stdlib.h>
86 #include <ctype.h>
88 char *Cmd = "wiggle";
90 void die()
92 fprintf(stderr,"wiggle: fatal error\n");
93 abort();
94 exit(3);
97 void printword(FILE *f, struct elmnt e)
99 if (e.start[0])
100 fprintf(f, "%.*s", e.len, e.start);
101 else {
102 int a,b,c;
103 sscanf(e.start+1, "%d %d %d", &a, &b, &c);
104 fprintf(f, "*** %d,%d **** %d\n", b,c,a);
108 static void printsep(struct elmnt e1, struct elmnt e2)
110 int a,b,c,d,e,f;
111 sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
112 sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
113 printf("@@ -%d,%d +%d,%d @@\n", b,c,e,f);
117 /* Remove any entries from the common-sublist that are
118 * just spaces, tabs, or newlines
120 void cleanlist(struct file a, struct file b, struct csl *list)
122 struct csl *new = list;
124 while (list->len) {
125 int i;
126 int ap;
127 for( ap = list->a; ap< list->a+list->len; ap++) {
128 for (i=0; i<a.list[ap].len; i++) {
129 char c = a.list[ap].start[i];
130 if (isalnum(c))
131 break;
133 if (i != a.list[ap].len)
134 break;
136 if (ap == list->a+list->len)
137 list++;
138 else
139 *new++ = *list++;
141 *new = *list;
144 int main(int argc, char *argv[])
146 int opt;
147 int option_index;
148 int mode = 0;
149 int obj = 0;
150 int replace = 0;
151 char *replacename=NULL, *orignew=NULL;
152 int which = 0;
153 int ispatch = 0;
154 int reverse = 0;
155 int verbose=0, quiet=0;
156 int i;
157 int strip = -1;
158 int chunks1=0, chunks2=0, chunks3=0;
159 int exit_status = 0;
160 FILE *outfile = stdout;
161 char *helpmsg;
162 char *base0;
164 struct stream f, flist[3];
165 struct file fl[3];
166 struct csl *csl1, *csl2;
168 base0 = strrchr(argv[0], '/');
169 if (base0) base0++; else base0=argv[0];
170 #if 0
171 /* The name 'vpatch' seems to be used elsewhere */
172 if (strcmp(base0, "vpatch")==0) {
173 Cmd = base0;
174 mode = 'B';
176 #endif
177 while ((opt = getopt_long(argc, argv,
178 short_options(mode), long_options,
179 &option_index)) != -1)
180 switch(opt) {
181 case 'h':
182 helpmsg = Help;
183 switch(mode) {
184 case 'x': helpmsg = HelpExtract; break;
185 case 'd': helpmsg = HelpDiff; break;
186 case 'm': helpmsg = HelpMerge; break;
187 case 'B': helpmsg = HelpBrowse; break;
189 fputs(helpmsg, stderr);
190 exit(0);
192 case 'V':
193 fputs(Version, stderr);
194 exit(0);
195 case ':':
196 case '?':
197 default:
198 fputs(Usage, stderr);
199 exit(2);
201 case 'B':
202 case 'x':
203 case 'd':
204 case 'm':
205 if (mode ==0){
206 mode = opt;
207 continue;
209 fprintf(stderr, "wiggle: mode is '%c' - cannot set to '%c'\n",
210 mode, opt);
211 exit(2);
213 case 'w':
214 case 'l':
215 if (obj == 0 || obj == opt) {
216 obj = opt;
217 continue;
219 fprintf(stderr, "wiggle: cannot select both words and lines.\n");
220 exit(2);
222 case 'r':
223 replace = 1;
224 continue;
225 case 'R':
226 reverse = 1;
227 continue;
229 case '1':
230 case '2':
231 case '3':
232 if (which == 0 || which == opt) {
233 which = opt;
234 continue;
236 fprintf(stderr, "wiggle: can only select one of -1, -2, -3\n");
237 exit(2);
239 case 'p':
240 if (mode == 'B')
241 strip = atol(optarg?optarg:"0");
242 else if (optarg) {
243 fprintf(stderr, "wiggle: SORRY, PARSE ERROR\n");
244 exit(2);
245 } else
246 ispatch = 1;
247 continue;
249 case 'v': verbose++; continue;
250 case 'q': quiet=1 ; continue;
252 if (!mode)
253 mode = 'm';
255 if (mode == 'B') {
256 vpatch(argc-optind, argv+optind, strip, reverse, replace);
257 /* should not return */
258 exit(1);
261 if (obj && mode == 'x') {
262 fprintf(stderr,"wiggle: cannot specify --line or --word with --extract\n");
263 exit(2);
265 if (mode != 'm' && !obj) obj = 'w';
266 if (replace && mode != 'm') {
267 fprintf(stderr, "wiggle: --replace only allowed with --merge\n");
268 exit(2);
270 if (mode == 'x' && !which) {
271 fprintf(stderr, "wiggle: must specify -1, -2 or -3 with --extract\n");
272 exit(2);
274 if (mode != 'x' && mode != 'd' && which) {
275 fprintf(stderr, "wiggle: -1, -2 or -3 only allowed with --extract or --diff\n");
276 exit(2);
278 if (ispatch && (mode != 'x' && mode != 'd')) {
279 fprintf(stderr, "wiggle: --patch only allowed with --extract or --diff\n");
280 exit(2);
282 if (ispatch && which == '3') {
283 fprintf(stderr, "wiggle: cannot extract -3 from a patch.\n");
284 exit(2);
287 switch(mode) {
288 case 'x':
289 /* extract a branch of a diff or diff3 or merge output
290 * We need one file
292 if (optind == argc) {
293 fprintf(stderr, "wiggle: no file given for --extract\n");
294 exit(2);
296 if (optind < argc-1) {
297 fprintf(stderr, "wiggle: only give one file for --extract\n");
298 exit(2);
300 f = load_file(argv[optind]);
301 if (f.body==NULL) {
302 fprintf(stderr, "wiggle: cannot load file '%s' - %s\n",
303 argv[optind], strerror(errno));
304 exit(2);
306 if (ispatch)
307 chunks1 = chunks2 = split_patch(f, &flist[0], &flist[1]);
308 else {
309 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
310 fprintf(stderr, "wiggle: merge file %s looks bad.\n",
311 argv[optind]);
312 exit(2);
315 if (flist[which-'1'].body == NULL) {
316 fprintf(stderr, "wiggle: %s has no -%c component.\n",
317 argv[optind], which);
318 exit(2);
319 } else {
320 write(1, flist[which-'1'].body, flist[which-'1'].len);
323 break;
324 case 'd':
325 /* create a diff (line or char) of two streams */
326 switch (argc-optind) {
327 case 0:
328 fprintf(stderr, "wiggle: no file given for --diff\n");
329 exit(2);
330 case 1:
331 f = load_file(argv[optind]);
332 if (f.body == NULL) {
333 fprintf(stderr, "wiggle: cannot load file '%s' - %s\n",
334 argv[optind], strerror(errno));
335 exit(2);
337 chunks1 = chunks2 = split_patch(f, &flist[0], &flist[1]);
338 if (!flist[0].body || !flist[1].body) {
339 fprintf(stderr, "wiggle: couldn't parse patch %s\n",
340 argv[optind]);
341 exit(2);
343 break;
344 case 2:
345 flist[0] = load_file(argv[optind]);
346 if (flist[0].body == NULL) {
347 fprintf(stderr, "wiggle: cannot load file '%s' - %s\n",
348 argv[optind], strerror(errno));
349 exit(2);
351 if (ispatch) {
352 f = load_file(argv[optind+1]);
353 if (f.body == NULL) {
354 fprintf(stderr, "wiggle: cannot load patch '%s' - %s\n",
355 argv[optind], strerror(errno));
356 exit(2);
358 if (which == '2')
359 chunks2 = chunks3 = split_patch(f, &flist[2], &flist[1]);
360 else
361 chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]);
363 } else
364 flist[1] = load_file(argv[optind+1]);
365 if (flist[1].body == NULL) {
366 fprintf(stderr, "wiggle: cannot load file '%s' - %s\n",
367 argv[optind+1], strerror(errno));
368 exit(2);
370 break;
371 default:
372 fprintf(stderr, "wiggle: too many files given for --diff\n");
373 exit(2);
375 if (reverse) {
376 f=flist[1];
377 flist[1] = flist[2];
378 flist[2]= f;
380 if (obj == 'l') {
381 int a,b;
382 fl[0] = split_stream(flist[0], ByLine, 0);
383 fl[1] = split_stream(flist[1], ByLine, 0);
384 if (chunks2 && ! chunks1)
385 csl1 = pdiff(fl[0], fl[1], chunks2);
386 else
387 csl1 = diff(fl[0], fl[1]);
389 if (!chunks1)
390 printf("@@ -1,%d +1,%d @@\n", fl[0].elcnt, fl[1].elcnt);
391 a = b = 0;
392 while (a<fl[0].elcnt || b < fl[1].elcnt) {
393 if (a < csl1->a) {
394 if (fl[0].list[a].start[0]) {
395 printf("-");
396 printword(stdout, fl[0].list[a]);
398 a++;
399 exit_status++;
400 } else if (b < csl1->b) {
401 if (fl[1].list[b].start[0]) {
402 printf("+");
403 printword(stdout, fl[1].list[b]);
405 b++;
406 exit_status++;
407 } else {
408 if (fl[0].list[a].start[0] == '\0')
409 printsep(fl[0].list[a], fl[1].list[b]);
410 else {
411 printf(" ");
412 printword(stdout, fl[0].list[a]);
414 a++;
415 b++;
416 if (a >= csl1->a+csl1->len)
417 csl1++;
420 } else {
421 int a,b;
422 int sol = 1; /* start of line */
423 fl[0] = split_stream(flist[0], ByWord, 0);
424 fl[1] = split_stream(flist[1], ByWord, 0);
425 if (chunks2 && !chunks1)
426 csl1 = pdiff(fl[0], fl[1], chunks2);
427 else
428 csl1 = diff(fl[0], fl[1]);
430 if (!chunks1) {
431 /* count lines in each file */
432 int l1, l2, i;
433 l1=l2=0;
434 for (i=0;i<fl[0].elcnt;i++)
435 if (ends_line(fl[0].list[i]))
436 l1++;
437 for (i=0;i<fl[1].elcnt;i++)
438 if (ends_line(fl[1].list[i]))
439 l2++;
440 printf("@@ -1,%d +1,%d @@\n", l1,l2);
442 a = b = 0;
443 while (a < fl[0].elcnt || b < fl[1].elcnt) {
444 if (a < csl1->a) {
445 exit_status++;
446 if (sol) {
447 int a1;
448 /* If we remove a whole line, output +line
449 * else clear sol and retry */
450 sol = 0;
451 for (a1=a; a1<csl1->a;a1++)
452 if (ends_line(fl[0].list[a1])) {
453 sol=1;
454 break;
456 if (sol) {
457 printf("-");
458 for (; a<csl1->a; a++) {
459 printword(stdout, fl[0].list[a]);
460 if (ends_line(fl[0].list[a])) {
461 a++;
462 break;
465 } else printf("|");
467 if (!sol) {
468 printf("<<<--");
469 do {
470 if (sol) printf("|");
471 printword(stdout, fl[0].list[a]);
472 sol = ends_line(fl[0].list[a]);
473 a++;
474 } while (a < csl1->a);
475 printf("%s-->>>", sol?"|":"");
476 sol=0;
478 } else if (b < csl1->b) {
479 exit_status++;
480 if (sol) {
481 int b1;
482 sol = 0;
483 for (b1=b; b1<csl1->b;b1++)
484 if(ends_line(fl[1].list[b1])) {
485 sol=1;
486 break;
488 if (sol) {
489 printf("+");
490 for(; b<csl1->b ; b++) {
491 printword(stdout, fl[1].list[b]);
492 if(ends_line(fl[1].list[b])) {
493 b++;
494 break;
497 } else printf("|");
499 if (!sol) {
500 printf("<<<++");
501 do {
502 if (sol) printf("|");
503 printword(stdout, fl[1].list[b]);
504 sol = ends_line(fl[1].list[b]);
505 b++;
506 } while (b < csl1->b);
507 printf("%s++>>>",sol?"|":"");
508 sol=0;
510 } else {
511 if (sol) {
512 int a1;
513 sol = 0;
514 for (a1=a; a1<csl1->a+csl1->len; a1++)
515 if (ends_line(fl[0].list[a1]))
516 sol=1;
517 if (sol) {
518 if (fl[0].list[a].start[0]) {
519 printf(" ");
520 for(; a<csl1->a+csl1->len; a++,b++) {
521 printword(stdout, fl[0].list[a]);
522 if (ends_line(fl[0].list[a])) {
523 a++,b++;
524 break;
527 } else {
528 printsep(fl[0].list[a], fl[1].list[b]);
529 a++; b++;
532 else printf("|");
534 if (!sol) {
535 printword(stdout, fl[0].list[a]);
536 if (ends_line(fl[0].list[a]))
537 sol=1;
538 a++;
539 b++;
541 if (a >= csl1->a+csl1->len)
542 csl1++;
547 break;
548 case 'm':
549 /* merge three files, A B C, so changed between B and C get made to A
551 switch (argc-optind) {
552 case 0:
553 fprintf(stderr, "wiggle: no files given for --merge\n");
554 exit(2);
555 case 3:
556 case 2:
557 case 1:
558 for (i=0; i< argc-optind; i++) {
559 flist[i] = load_file(argv[optind+i]);
560 if (flist[i].body == NULL) {
561 fprintf(stderr, "wiggle: cannot load file '%s' - %s\n",
562 argv[optind+i], strerror(errno));
563 exit(2);
566 break;
567 default:
568 fprintf(stderr, "wiggle: too many files given for --merge\n");
569 exit(2);
571 switch(argc-optind) {
572 case 1: /* a merge file */
573 f = flist[0];
574 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
575 fprintf(stderr,"wiggle: merge file %s looks bad.\n",
576 argv[optind]);
577 exit(2);
579 break;
580 case 2: /* a file and a patch */
581 f = flist[1];
582 chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]);
583 break;
584 case 3: /* three separate files */
585 break;
587 if (reverse) {
588 f=flist[1];
589 flist[1] = flist[2];
590 flist[2]= f;
593 for (i=0; i<3; i++) {
594 if (flist[i].body==NULL) {
595 fprintf(stderr, "wiggle: file %d missing\n", i);
596 exit(2);
599 if (replace) {
600 int fd;
601 replacename = malloc(strlen(argv[optind])+ 20);
602 if (!replacename) die();
603 orignew = malloc(strlen(argv[optind])+20);
604 if (!orignew) die();
605 strcpy(replacename, argv[optind]);
606 strcpy(orignew, argv[optind]);
607 strcat(orignew, ".porig");
608 if (open(orignew, O_RDONLY) >= 0 ||
609 errno != ENOENT) {
610 fprintf(stderr,"wiggle: %s already exists\n",
611 orignew);
612 exit(2);
614 strcat(replacename,"XXXXXX");
615 fd = mkstemp(replacename);
616 if (fd == -1) {
617 fprintf(stderr,"wiggle: could not create temporary file for %s\n",
618 replacename);
619 exit(2);
621 outfile = fdopen(fd, "w");
625 if (obj == 'l') {
626 fl[0] = split_stream(flist[0], ByLine, 0);
627 fl[1] = split_stream(flist[1], ByLine, 0);
628 fl[2] = split_stream(flist[2], ByLine, 0);
629 } else {
630 fl[0] = split_stream(flist[0], ByWord, 0);
631 fl[1] = split_stream(flist[1], ByWord, 0);
632 fl[2] = split_stream(flist[2], ByWord, 0);
634 if (chunks2 && !chunks1)
635 csl1 = pdiff(fl[0], fl[1], chunks2);
636 else
637 csl1 = diff(fl[0], fl[1]);
638 csl2 = diff(fl[1], fl[2]);
640 #if 0
641 cleanlist(fl[0],fl[1],csl1);
642 cleanlist(fl[1],fl[2],csl2);
643 #endif
646 struct ci ci;
648 ci = print_merge2(outfile, &fl[0], &fl[1], &fl[2],
649 csl1, csl2, obj=='w');
650 if (!quiet && ci.conflicts)
651 fprintf(stderr, "%d unresolved conflict%s found\n", ci.conflicts, ci.conflicts==1?"":"s");
652 if (!quiet && ci.ignored)
653 fprintf(stderr, "%d already-applied change%s ignored\n", ci.ignored, ci.ignored==1?"":"s");
654 exit_status = (ci.conflicts > 0);
656 if (replace) {
657 fclose(outfile);
658 if (rename(argv[optind], orignew) ==0 &&
659 rename(replacename, argv[optind]) ==0)
660 /* all ok */;
661 else {
662 fprintf(stderr, "wiggle: failed to move new file into place.\n");
663 exit(2);
666 break;
669 exit(exit_status);