Re-indent code messed up by name-change.
[wiggle/upstream.git] / wiggle.c
blob712e7fe4bbe0ee3b062fc9b0ed14a99b7c1d8daa
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 * Wiggle is a tool for working with patches that don't quite apply properly.
27 * It provides functionality similar to 'diff' and 'merge' but can
28 * work at the level of individual words thus allowing the merging of
29 * two changes that affect the same line, but not the same parts of that line.
31 * Wiggle can also read patch and merge files. Unlike 'merge' it does not
32 * need to be given three separate files, but can be given a file and a patch
33 * and it will extract the pieces of the two other files that it needs from
34 * the patch.
36 * Wiggle performs one of three core function:
37 * --extract -x extract part of a patch or merge file
38 * --diff -d report differences between two files
39 * --merge -m merge the changes between two files into a third file
41 * This is also a --browse (-B) mode which provides interactive access
42 * to the merger.
44 * To perform these, wiggle requires 1, 2, or 3 input streams respectively.
45 * I can get these from individual files, from a diff (unified or context) or
46 * from a merge file.
48 * For merge:
49 * If one file is given, it is a merge file (output of 'merge').
50 * If two files are given, the second is assumed to be a patch,
51 * the first is a normal file.
52 * If three files are given, they are taken to be normal files.
54 * For diff:
55 * If one file is given, it is a patch
56 * If two files are given, they are normal files.
58 * For extract:
59 * Only one file can be given. -p indicates it is a patch,
60 * 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 calculation and merging is performed on lines (-l) or words (-w).
65 * Each 'word' is either 1/all alphnumeric (or '_'), 2/ all space or tab,
66 * or 3/ any other single character.
68 * In the case of -w, an initial diff is computed based on non-trivial words
69 * which includes alhpanumeric words and newlines.
71 * This diff is computed from the ends of the file and is used to find
72 * a suitable starting point and range. Then a more precise diff is
73 * computed over that restricted range
75 * Other options available are:
76 * --replace -r replace first file with result of merge.
77 * --help -h provide help
78 * --version -v version
80 * Defaults are --merge --words
83 #define _GNU_SOURCE
84 #include "wiggle.h"
85 #include <errno.h>
86 #include <fcntl.h>
87 #include <unistd.h>
88 #include <stdlib.h>
89 #include <stdio.h>
90 #include <ctype.h>
91 #include <sys/stat.h>
93 static void printsep(struct elmnt e1, struct elmnt e2)
95 int a, b, c, d, e, f;
96 sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
97 sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
98 printf("@@ -%d,%d +%d,%d @@%s", b, c, e, f, e1.start+18);
101 static int extract(int argc, char *argv[], int ispatch, int which)
103 /* extract a branch of a diff or diff3 or merge output
104 * We need one file
106 struct stream f, flist[3];
108 if (argc == 0) {
109 fprintf(stderr,
110 "%s: no file given for --extract\n", wiggle_Cmd);
111 return 2;
113 if (argc > 1) {
114 fprintf(stderr,
115 "%s: only give one file for --extract\n", wiggle_Cmd);
116 return 2;
118 f = wiggle_load_file(argv[0]);
119 if (f.body == NULL) {
120 fprintf(stderr,
121 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
122 argv[0], strerror(errno));
123 return 2;
125 if (ispatch) {
126 if (wiggle_split_patch(f, &flist[0], &flist[1]) == 0) {
127 fprintf(stderr,
128 "%s: No chunk found in patch: %s\n", wiggle_Cmd,
129 argv[0]);
130 return 0;
132 } else {
133 if (!wiggle_split_merge(f, &flist[0], &flist[1], &flist[2])) {
134 fprintf(stderr,
135 "%s: merge file %s looks bad.\n", wiggle_Cmd,
136 argv[0]);
137 return 2;
140 if (flist[which-'1'].body == NULL) {
141 fprintf(stderr,
142 "%s: %s has no -%c component.\n", wiggle_Cmd,
143 argv[0], which);
144 return 2;
145 } else {
146 if (write(1, flist[which-'1'].body,
147 flist[which-'1'].len)
148 != flist[which-'1'].len)
149 return 2;
151 return 0;
154 static int do_diff_lines(struct file fl[2], struct csl *csl)
156 int a, b;
157 int exit_status = 0;
158 a = b = 0;
159 while (a < fl[0].elcnt || b < fl[1].elcnt) {
160 if (a < csl->a) {
161 if (fl[0].list[a].start[0]) {
162 printf("-");
163 wiggle_printword(stdout,
164 fl[0].list[a]);
166 a++;
167 exit_status = 1;
168 } else if (b < csl->b) {
169 if (fl[1].list[b].start[0]) {
170 printf("+");
171 wiggle_printword(stdout,
172 fl[1].list[b]);
174 b++;
175 exit_status = 1;
176 } else {
177 if (fl[0].list[a].start[0] == '\0')
178 printsep(fl[0].list[a],
179 fl[1].list[b]);
180 else {
181 printf(" ");
182 wiggle_printword(stdout,
183 fl[0].list[a]);
185 a++;
186 b++;
187 if (a >= csl->a+csl->len)
188 csl++;
191 return exit_status;
194 static int do_diff_words(struct file fl[2], struct csl *csl)
196 int a, b;
197 int exit_status = 0;
198 int sol = 1; /* start of line */
199 a = b = 0;
200 while (a < fl[0].elcnt || b < fl[1].elcnt) {
201 if (a < csl->a) {
202 exit_status = 1;
203 if (sol) {
204 int a1;
205 /* If we remove a
206 * whole line, output
207 * +line else clear
208 * sol and retry */
209 sol = 0;
210 for (a1 = a; a1 < csl->a ; a1++)
211 if (ends_line(fl[0].list[a1])) {
212 sol = 1;
213 break;
215 if (sol) {
216 printf("-");
217 for (; a < csl->a ; a++) {
218 wiggle_printword(stdout, fl[0].list[a]);
219 if (ends_line(fl[0].list[a])) {
220 a++;
221 break;
224 } else
225 printf("|");
227 if (!sol) {
228 printf("<<<--");
229 do {
230 if (sol)
231 printf("|");
232 wiggle_printword(stdout, fl[0].list[a]);
233 sol = ends_line(fl[0].list[a]);
234 a++;
235 } while (a < csl->a);
236 printf("%s-->>>", sol ? "|" : "");
237 sol = 0;
239 } else if (b < csl->b) {
240 exit_status = 1;
241 if (sol) {
242 int b1;
243 sol = 0;
244 for (b1 = b; b1 < csl->b; b1++)
245 if (ends_line(fl[1].list[b1])) {
246 sol = 1;
247 break;
249 if (sol) {
250 printf("+");
251 for (; b < csl->b ; b++) {
252 wiggle_printword(stdout, fl[1].list[b]);
253 if (ends_line(fl[1].list[b])) {
254 b++;
255 break;
258 } else
259 printf("|");
261 if (!sol) {
262 printf("<<<++");
263 do {
264 if (sol)
265 printf("|");
266 wiggle_printword(stdout, fl[1].list[b]);
267 sol = ends_line(fl[1].list[b]);
268 b++;
269 } while (b < csl->b);
270 printf("%s++>>>", sol ? "|" : "");
271 sol = 0;
273 } else {
274 if (sol) {
275 int a1;
276 sol = 0;
277 for (a1 = a; a1 < csl->a+csl->len; a1++)
278 if (ends_line(fl[0].list[a1]))
279 sol = 1;
280 if (sol) {
281 if (fl[0].list[a].start[0]) {
282 printf(" ");
283 for (; a < csl->a+csl->len; a++, b++) {
284 wiggle_printword(stdout, fl[0].list[a]);
285 if (ends_line(fl[0].list[a])) {
286 a++, b++;
287 break;
290 } else {
291 printsep(fl[0].list[a], fl[1].list[b]);
292 a++; b++;
294 } else
295 printf("|");
297 if (!sol) {
298 wiggle_printword(stdout, fl[0].list[a]);
299 if (ends_line(fl[0].list[a]))
300 sol = 1;
301 a++;
302 b++;
304 if (a >= csl->a+csl->len)
305 csl++;
308 return exit_status;
311 static int do_diff(int argc, char *argv[], int obj, int ispatch,
312 int which, int reverse, int shortest)
314 /* create a diff (line or char) of two streams */
315 struct stream f, flist[3];
316 int chunks1 = 0, chunks2 = 0, chunks3 = 0;
317 int exit_status = 0;
318 struct file fl[2];
319 struct csl *csl;
321 switch (argc) {
322 case 0:
323 fprintf(stderr, "%s: no file given for --diff\n", wiggle_Cmd);
324 return 2;
325 case 1:
326 f = wiggle_load_file(argv[0]);
327 if (f.body == NULL) {
328 fprintf(stderr,
329 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
330 argv[0], strerror(errno));
331 return 2;
333 chunks1 = chunks2 =
334 wiggle_split_patch(f, &flist[0], &flist[1]);
335 if (!flist[0].body || !flist[1].body) {
336 fprintf(stderr,
337 "%s: couldn't parse patch %s\n", wiggle_Cmd,
338 argv[0]);
339 return 2;
341 break;
342 case 2:
343 flist[0] = wiggle_load_file(argv[0]);
344 if (flist[0].body == NULL) {
345 fprintf(stderr,
346 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
347 argv[0], strerror(errno));
348 return 2;
350 if (ispatch) {
351 f = wiggle_load_file(argv[1]);
352 if (f.body == NULL) {
353 fprintf(stderr,
354 "%s: cannot load patch '%s' - %s\n", wiggle_Cmd,
355 argv[1], strerror(errno));
356 return 2;
358 if (which == '2')
359 chunks2 = chunks3 =
360 wiggle_split_patch(f, &flist[2],
361 &flist[1]);
362 else
363 chunks2 = chunks3 =
364 wiggle_split_patch(f, &flist[1],
365 &flist[2]);
367 } else
368 flist[1] = wiggle_load_file(argv[1]);
369 if (flist[1].body == NULL) {
370 fprintf(stderr,
371 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
372 argv[1], strerror(errno));
373 return 2;
375 break;
376 default:
377 fprintf(stderr,
378 "%s: too many files given for --diff\n", wiggle_Cmd);
379 return 2;
381 if (reverse) {
382 f = flist[0];
383 flist[0] = flist[1];
384 flist[1] = f;
386 fl[0] = wiggle_split_stream(flist[0], obj);
387 fl[1] = wiggle_split_stream(flist[1], obj);
388 if (!(obj & WholeWord) && fl[0].elcnt > 50000 && fl[1].elcnt > 50000) {
389 /* Too big - use fewer words if possible */
390 free(fl[0].list);
391 free(fl[1].list);
392 obj |= WholeWord;
393 fl[0] = wiggle_split_stream(flist[0], obj);
394 fl[1] = wiggle_split_stream(flist[1], obj);
396 if (chunks2 && !chunks1)
397 csl = wiggle_pdiff(fl[0], fl[1], chunks2);
398 else
399 csl = wiggle_diff_patch(fl[0], fl[1], shortest);
400 if ((obj & ByMask) == ByLine) {
401 if (!chunks1)
402 printf("@@ -1,%d +1,%d @@\n",
403 fl[0].elcnt, fl[1].elcnt);
404 exit_status = do_diff_lines(fl, csl);
405 } else {
406 if (!chunks1) {
407 /* count lines in each file */
408 int l1, l2, i;
409 l1 = l2 = 0;
410 for (i = 0 ; i < fl[0].elcnt ; i++)
411 if (ends_line(fl[0].list[i]))
412 l1++;
413 for (i = 0 ; i < fl[1].elcnt ; i++)
414 if (ends_line(fl[1].list[i]))
415 l2++;
416 printf("@@ -1,%d +1,%d @@\n", l1, l2);
418 exit_status = do_diff_words(fl, csl);
420 return exit_status;
423 static int do_merge(int argc, char *argv[], int obj, int blanks,
424 int reverse, int replace, char *outfilename,
425 int ignore, int show_wiggles,
426 int quiet, int shortest)
428 /* merge three files, A B C, so changed between B and C get made to A
430 struct stream f, flist[3];
431 struct file fl[3];
432 int i;
433 int chunks1 = 0, chunks2 = 0, chunks3 = 0;
434 char *replacename = NULL, *orignew = NULL;
435 struct csl *csl1, *csl2;
436 struct ci ci;
437 FILE *outfile = stdout;
439 switch (argc) {
440 case 0:
441 fprintf(stderr, "%s: no files given for --merge\n", wiggle_Cmd);
442 return 2;
443 case 3:
444 case 2:
445 case 1:
446 for (i = 0; i < argc; i++) {
447 flist[i] = wiggle_load_file(argv[i]);
448 if (flist[i].body == NULL) {
449 fprintf(stderr, "%s: cannot load file '%s' - %s\n",
450 wiggle_Cmd,
451 argv[i], strerror(errno));
452 return 2;
455 break;
456 default:
457 fprintf(stderr, "%s: too many files given for --merge\n",
458 wiggle_Cmd);
459 return 2;
461 switch (argc) {
462 case 1: /* a merge file */
463 f = flist[0];
464 if (!wiggle_split_merge(f, &flist[0], &flist[1], &flist[2])) {
465 fprintf(stderr, "%s: merge file %s looks bad.\n",
466 wiggle_Cmd,
467 argv[0]);
468 return 2;
470 break;
471 case 2: /* a file and a patch */
472 f = flist[1];
473 chunks2 = chunks3 = wiggle_split_patch(f, &flist[1], &flist[2]);
474 break;
475 case 3: /* three separate files */
476 break;
478 if (reverse) {
479 f = flist[1];
480 flist[1] = flist[2];
481 flist[2] = f;
484 for (i = 0; i < 3; i++) {
485 if (flist[i].body == NULL) {
486 fprintf(stderr, "%s: file %d missing\n", wiggle_Cmd, i);
487 return 2;
490 if (outfilename) {
491 outfile = fopen(outfilename, "w");
492 if (!outfile) {
493 fprintf(stderr, "%s: could not create %s\n",
494 wiggle_Cmd, outfilename);
495 return 2;
497 } else if (replace) {
498 int fd;
499 replacename = wiggle_xmalloc(strlen(argv[0]) + 20);
500 orignew = wiggle_xmalloc(strlen(argv[0]) + 20);
501 strcpy(replacename, argv[0]);
502 strcpy(orignew, argv[0]);
503 strcat(orignew, ".porig");
504 if (open(orignew, O_RDONLY) >= 0 ||
505 errno != ENOENT) {
506 fprintf(stderr, "%s: %s already exists\n",
507 wiggle_Cmd,
508 orignew);
509 free(replacename);
510 free(orignew);
511 return 2;
513 strcat(replacename, "XXXXXX");
514 fd = mkstemp(replacename);
515 if (fd == -1) {
516 fprintf(stderr,
517 "%s: could not create temporary file for %s\n",
518 wiggle_Cmd,
519 replacename);
520 free(replacename);
521 free(orignew);
522 return 2;
524 outfile = fdopen(fd, "w");
527 if (obj == 'l')
528 blanks |= ByLine;
529 else
530 blanks |= ByWord;
531 fl[0] = wiggle_split_stream(flist[0], blanks);
532 fl[1] = wiggle_split_stream(flist[1], blanks);
533 fl[2] = wiggle_split_stream(flist[2], blanks);
534 if (!(blanks & WholeWord) &&
535 fl[1].elcnt > 50000 &&
536 (fl[0].elcnt > 50000 || fl[2].elcnt > 50000)) {
537 /* Too many words */
538 free(fl[0].list);
539 free(fl[1].list);
540 free(fl[2].list);
541 blanks |= WholeWord;
542 fl[0] = wiggle_split_stream(flist[0], blanks);
543 fl[1] = wiggle_split_stream(flist[1], blanks);
544 fl[2] = wiggle_split_stream(flist[2], blanks);
547 if (chunks2 && !chunks1)
548 csl1 = wiggle_pdiff(fl[0], fl[1], chunks2);
549 else
550 csl1 = wiggle_diff(fl[0], fl[1], shortest);
551 csl2 = wiggle_diff_patch(fl[1], fl[2], shortest);
553 ci = wiggle_make_merger(fl[0], fl[1], fl[2], csl1, csl2,
554 obj == 'w', ignore, show_wiggles > 1);
555 wiggle_print_merge(outfile, &fl[0], &fl[1], &fl[2],
556 obj == 'w', ci.merger, NULL, 0, 0);
557 if (!quiet && ci.conflicts)
558 fprintf(stderr,
559 "%d unresolved conflict%s found\n",
560 ci.conflicts,
561 ci.conflicts == 1 ? "" : "s");
562 if (!quiet && ci.ignored)
563 fprintf(stderr,
564 "%d already-applied change%s ignored\n",
565 ci.ignored,
566 ci.ignored == 1 ? "" : "s");
568 if (outfilename)
569 fclose(outfile);
570 else if (replace) {
571 struct stat statbuf;
573 if (stat(argv[0], &statbuf) != 0) {
574 fprintf(stderr,
575 "%s: failed to stat original file. - %s\n",
576 wiggle_Cmd, strerror(errno));
577 free(replacename);
578 free(orignew);
579 return 2;
581 if (fchmod(fileno(outfile), statbuf.st_mode) != 0) {
582 fprintf(stderr,
583 "%s: failed to change permission of new file. - %s\n",
584 wiggle_Cmd, strerror(errno));
585 free(replacename);
586 free(orignew);
587 return 2;
589 fclose(outfile);
590 if (rename(argv[0], orignew) == 0 &&
591 rename(replacename, argv[0]) == 0)
592 /* all ok */;
593 else {
594 fprintf(stderr,
595 "%s: failed to move new file into place.\n",
596 wiggle_Cmd);
597 free(replacename);
598 free(orignew);
599 return 2;
602 free(replacename);
603 free(orignew);
604 if (show_wiggles)
605 return ci.conflicts + ci.wiggles > 0;
606 else
607 return ci.conflicts > 0;
610 static int multi_merge(int argc, char *argv[], int obj, int blanks,
611 int reverse, int ignore, int show_wiggles,
612 int replace, int strip,
613 int quiet, int shortest)
615 FILE *f;
616 char *filename;
617 struct plist *pl;
618 int num_patches;
619 int rv = 0;
620 int i;
622 if (!replace) {
623 fprintf(stderr,
624 "%s: -p in merge mode requires -r\n",
625 wiggle_Cmd);
626 return 2;
628 if (argc != 1) {
629 fprintf(stderr,
630 "%s: -p in merge mode requires exactly one file\n",
631 wiggle_Cmd);
632 return 2;
634 filename = argv[0];
635 f = fopen(filename, "r");
636 if (!f) {
637 fprintf(stderr, "%s: cannot open %s\n",
638 wiggle_Cmd, filename);
639 return 2;
641 wiggle_check_dir(filename, fileno(f));
642 pl = wiggle_parse_patch(f, NULL, &num_patches);
643 fclose(f);
644 if (wiggle_set_prefix(pl, num_patches, strip) == 0) {
645 fprintf(stderr, "%s: aborting\n", wiggle_Cmd);
646 return 2;
648 for (i = 0; i < num_patches; i++) {
649 char *name;
650 char *av[2];
651 asprintf(&name, "_wiggle_:%d:%d:%s",
652 pl[i].start, pl[i].end, filename);
653 av[0] = pl[i].file;
654 av[1] = name;
655 rv |= do_merge(2, av, obj, blanks, reverse, 1, NULL, ignore,
656 show_wiggles, quiet, shortest);
658 return rv;
661 int main(int argc, char *argv[])
663 int opt;
664 int option_index;
665 int mode = 0;
666 int obj = 0;
667 int replace = 0;
668 int backup = 1;
669 int which = 0;
670 int ispatch = 0;
671 int reverse = 0;
672 int verbose = 0, quiet = 0;
673 int strip = -1;
674 int exit_status = 0;
675 int ignore = 1;
676 int shortest = 0;
677 int show_wiggles = 0;
678 char *helpmsg;
679 char *trace;
680 char *outfile = NULL;
681 int selftest = 0;
682 int ignore_blanks = 0;
684 trace = getenv("WIGGLE_TRACE");
685 if (trace && *trace)
686 wiggle_do_trace = 1;
688 while ((opt = getopt_long(argc, argv,
689 short_options, long_options,
690 &option_index)) != -1)
691 switch (opt) {
692 case 'h':
693 helpmsg = Help;
694 switch (mode) {
695 case 'x':
696 helpmsg = HelpExtract;
697 break;
698 case 'd':
699 helpmsg = HelpDiff;
700 break;
701 case 'm':
702 helpmsg = HelpMerge;
703 break;
704 case 'B':
705 helpmsg = HelpBrowse;
706 break;
708 fputs(helpmsg, stderr);
709 exit(0);
711 case 'V':
712 fputs(Version, stderr);
713 exit(0);
714 case ':':
715 case '?':
716 default:
717 fputs(Usage, stderr);
718 exit(2);
720 case 'B':
721 case 'x':
722 case 'd':
723 case 'm':
724 if (mode == 0) {
725 mode = opt;
726 continue;
728 if (mode == 'B' && opt == 'd') {
729 /* Browse/diff mode */
730 ispatch = 2;
731 continue;
733 fprintf(stderr,
734 "%s: mode is '%c' - cannot set to '%c'\n",
735 wiggle_Cmd, mode, opt);
736 exit(2);
738 case NON_SPACE:
739 ignore_blanks |= WholeWord;
740 continue;
742 case SHORTEST:
743 shortest = 1;
744 continue;
746 case 'w':
747 case 'l':
748 if (obj == 0 || obj == opt) {
749 obj = opt;
750 continue;
752 fprintf(stderr,
753 "%s: cannot select both words and lines.\n", wiggle_Cmd);
754 exit(2);
756 case 'r':
757 replace = 1;
758 continue;
759 case NO_BACKUP:
760 backup = 0;
761 continue;
762 case 'o':
763 outfile = optarg;
764 replace = 1;
765 continue;
766 case 'R':
767 reverse = 1;
768 continue;
770 case 'b':
771 ignore_blanks |= IgnoreBlanks;
772 continue;
774 case 'i':
775 ignore = 0;
776 continue;
777 case 'W':
778 show_wiggles = 2;
779 ignore = 0;
780 continue;
781 case REPORT_WIGGLES:
782 show_wiggles = 1;
783 continue;
785 case '1':
786 case '2':
787 case '3':
788 if (which == 0 || which == opt) {
789 which = opt;
790 continue;
792 fprintf(stderr,
793 "%s: can only select one of -1, -2, -3\n", wiggle_Cmd);
794 exit(2);
796 case 'p': /* 'patch' or 'strip' */
797 if (optarg)
798 strip = atol(optarg);
799 ispatch = 1;
800 continue;
802 case 'v':
803 verbose++;
804 continue;
805 case 'q':
806 quiet = 1;
807 continue;
809 case SELF_TEST:
810 selftest = 1;
811 continue;
813 if (!mode)
814 mode = 'm';
816 if (mode == 'B') {
817 vpatch(argc-optind, argv+optind, ispatch,
818 strip, reverse, replace, outfile, selftest,
819 ignore_blanks, backup);
820 /* should not return */
821 exit(1);
824 if (obj && mode == 'x') {
825 fprintf(stderr,
826 "%s: cannot specify --line or --word with --extract\n",
827 wiggle_Cmd);
828 exit(2);
830 if (mode != 'm' && !obj)
831 obj = 'w';
832 if (ispatch && outfile) {
833 fprintf(stderr, "%s: --output incompatible with --patch\n",
834 wiggle_Cmd);
835 exit(2);
837 if (replace && mode != 'm') {
838 fprintf(stderr,
839 "%s: --replace or --output only allowed with --merge\n", wiggle_Cmd);
840 exit(2);
842 if (mode == 'x' && !which) {
843 fprintf(stderr,
844 "%s: must specify -1, -2 or -3 with --extract\n", wiggle_Cmd);
845 exit(2);
847 if (mode != 'x' && mode != 'd' && which) {
848 fprintf(stderr,
849 "%s: -1, -2 or -3 only allowed with --extract or --diff\n",
850 wiggle_Cmd);
851 exit(2);
854 if (ispatch && which == '3') {
855 fprintf(stderr,
856 "%s: cannot extract -3 from a patch.\n", wiggle_Cmd);
857 exit(2);
860 switch (mode) {
861 case 'x':
862 exit_status = extract(argc-optind, argv+optind, ispatch, which);
863 break;
864 case 'd':
865 exit_status = do_diff(argc-optind, argv+optind,
866 (obj == 'l' ? ByLine : ByWord)
867 | ignore_blanks,
868 ispatch, which, reverse, shortest);
869 break;
870 case 'm':
871 if (ispatch)
872 exit_status = multi_merge(argc-optind,
873 argv+optind, obj,
874 ignore_blanks,
875 reverse, ignore,
876 show_wiggles,
877 replace, strip,
878 quiet, shortest);
879 else
880 exit_status = do_merge(
881 argc-optind, argv+optind,
882 obj, ignore_blanks, reverse, replace,
883 outfile,
884 ignore, show_wiggles, quiet, shortest);
885 break;
887 exit(exit_status);