merge: fix a problem with unmatchable hunks.
[wiggle/upstream.git] / wiggle.c
blobefdd39649da2367a2a47b85caceb42ea1431393e
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 char *Cmd = "wiggle";
94 int do_trace = 0;
96 void die(char *reason)
98 fprintf(stderr, "%s: fatal error: %s failure\n", Cmd, reason);
99 exit(3);
102 void check_dir(char *name, int fd)
104 struct stat st;
105 if (fstat(fd, &st) != 0) {
106 fprintf(stderr, "%s: fatal: %s is strange\n", Cmd, name);
107 exit(3);
109 if (S_ISDIR(st.st_mode)) {
110 fprintf(stderr, "%s: %s is a directory\n", Cmd, name);
111 exit(3);
115 void *xmalloc(int size)
117 void *rv = malloc(size);
118 if (size && !rv) {
119 char *msg = "Failed to allocate memory - aborting\n";
120 write(2, msg, strlen(msg));
121 exit(3);
123 return rv;
126 void printword(FILE *f, struct elmnt e)
128 if (e.start[0])
129 fprintf(f, "%.*s", e.plen + e.prefix,
130 e.start - e.prefix);
131 else {
132 int a, b, c;
133 sscanf(e.start+1, "%d %d %d", &a, &b, &c);
134 fprintf(f, "*** %d,%d **** %d%s", b, c, a, e.start+18);
138 static void printsep(struct elmnt e1, struct elmnt e2)
140 int a, b, c, d, e, f;
141 sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
142 sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
143 printf("@@ -%d,%d +%d,%d @@%s", b, c, e, f, e1.start+18);
146 static int extract(int argc, char *argv[], int ispatch, int which)
148 /* extract a branch of a diff or diff3 or merge output
149 * We need one file
151 struct stream f, flist[3];
153 if (argc == 0) {
154 fprintf(stderr,
155 "%s: no file given for --extract\n", Cmd);
156 return 2;
158 if (argc > 1) {
159 fprintf(stderr,
160 "%s: only give one file for --extract\n", Cmd);
161 return 2;
163 f = load_file(argv[0]);
164 if (f.body == NULL) {
165 fprintf(stderr,
166 "%s: cannot load file '%s' - %s\n", Cmd,
167 argv[0], strerror(errno));
168 return 2;
170 if (ispatch) {
171 if (split_patch(f, &flist[0], &flist[1]) == 0) {
172 fprintf(stderr,
173 "%s: No chunk found in patch: %s\n", Cmd,
174 argv[0]);
175 return 0;
177 } else {
178 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
179 fprintf(stderr,
180 "%s: merge file %s looks bad.\n", Cmd,
181 argv[0]);
182 return 2;
185 if (flist[which-'1'].body == NULL) {
186 fprintf(stderr,
187 "%s: %s has no -%c component.\n", Cmd,
188 argv[0], which);
189 return 2;
190 } else {
191 if (write(1, flist[which-'1'].body,
192 flist[which-'1'].len)
193 != flist[which-'1'].len)
194 return 2;
196 return 0;
199 static int do_diff_lines(struct file fl[2], struct csl *csl)
201 int a, b;
202 int exit_status = 0;
203 a = b = 0;
204 while (a < fl[0].elcnt || b < fl[1].elcnt) {
205 if (a < csl->a) {
206 if (fl[0].list[a].start[0]) {
207 printf("-");
208 printword(stdout,
209 fl[0].list[a]);
211 a++;
212 exit_status = 1;
213 } else if (b < csl->b) {
214 if (fl[1].list[b].start[0]) {
215 printf("+");
216 printword(stdout,
217 fl[1].list[b]);
219 b++;
220 exit_status = 1;
221 } else {
222 if (fl[0].list[a].start[0] == '\0')
223 printsep(fl[0].list[a],
224 fl[1].list[b]);
225 else {
226 printf(" ");
227 printword(stdout,
228 fl[0].list[a]);
230 a++;
231 b++;
232 if (a >= csl->a+csl->len)
233 csl++;
236 return exit_status;
239 static int do_diff_words(struct file fl[2], struct csl *csl)
241 int a, b;
242 int exit_status = 0;
243 int sol = 1; /* start of line */
244 a = b = 0;
245 while (a < fl[0].elcnt || b < fl[1].elcnt) {
246 if (a < csl->a) {
247 exit_status = 1;
248 if (sol) {
249 int a1;
250 /* If we remove a
251 * whole line, output
252 * +line else clear
253 * sol and retry */
254 sol = 0;
255 for (a1 = a; a1 < csl->a ; a1++)
256 if (ends_line(fl[0].list[a1])) {
257 sol = 1;
258 break;
260 if (sol) {
261 printf("-");
262 for (; a < csl->a ; a++) {
263 printword(stdout, fl[0].list[a]);
264 if (ends_line(fl[0].list[a])) {
265 a++;
266 break;
269 } else
270 printf("|");
272 if (!sol) {
273 printf("<<<--");
274 do {
275 if (sol)
276 printf("|");
277 printword(stdout, fl[0].list[a]);
278 sol = ends_line(fl[0].list[a]);
279 a++;
280 } while (a < csl->a);
281 printf("%s-->>>", sol ? "|" : "");
282 sol = 0;
284 } else if (b < csl->b) {
285 exit_status = 1;
286 if (sol) {
287 int b1;
288 sol = 0;
289 for (b1 = b; b1 < csl->b; b1++)
290 if (ends_line(fl[1].list[b1])) {
291 sol = 1;
292 break;
294 if (sol) {
295 printf("+");
296 for (; b < csl->b ; b++) {
297 printword(stdout, fl[1].list[b]);
298 if (ends_line(fl[1].list[b])) {
299 b++;
300 break;
303 } else
304 printf("|");
306 if (!sol) {
307 printf("<<<++");
308 do {
309 if (sol)
310 printf("|");
311 printword(stdout, fl[1].list[b]);
312 sol = ends_line(fl[1].list[b]);
313 b++;
314 } while (b < csl->b);
315 printf("%s++>>>", sol ? "|" : "");
316 sol = 0;
318 } else {
319 if (sol) {
320 int a1;
321 sol = 0;
322 for (a1 = a; a1 < csl->a+csl->len; a1++)
323 if (ends_line(fl[0].list[a1]))
324 sol = 1;
325 if (sol) {
326 if (fl[0].list[a].start[0]) {
327 printf(" ");
328 for (; a < csl->a+csl->len; a++, b++) {
329 printword(stdout, fl[0].list[a]);
330 if (ends_line(fl[0].list[a])) {
331 a++, b++;
332 break;
335 } else {
336 printsep(fl[0].list[a], fl[1].list[b]);
337 a++; b++;
339 } else
340 printf("|");
342 if (!sol) {
343 printword(stdout, fl[0].list[a]);
344 if (ends_line(fl[0].list[a]))
345 sol = 1;
346 a++;
347 b++;
349 if (a >= csl->a+csl->len)
350 csl++;
353 return exit_status;
356 static int do_diff(int argc, char *argv[], int obj, int ispatch,
357 int which, int reverse, int shortest)
359 /* create a diff (line or char) of two streams */
360 struct stream f, flist[3];
361 int chunks1 = 0, chunks2 = 0, chunks3 = 0;
362 int exit_status = 0;
363 struct file fl[2];
364 struct csl *csl;
366 switch (argc) {
367 case 0:
368 fprintf(stderr, "%s: no file given for --diff\n", Cmd);
369 return 2;
370 case 1:
371 f = load_file(argv[0]);
372 if (f.body == NULL) {
373 fprintf(stderr,
374 "%s: cannot load file '%s' - %s\n", Cmd,
375 argv[0], strerror(errno));
376 return 2;
378 chunks1 = chunks2 =
379 split_patch(f, &flist[0], &flist[1]);
380 if (!flist[0].body || !flist[1].body) {
381 fprintf(stderr,
382 "%s: couldn't parse patch %s\n", Cmd,
383 argv[0]);
384 return 2;
386 break;
387 case 2:
388 flist[0] = load_file(argv[0]);
389 if (flist[0].body == NULL) {
390 fprintf(stderr,
391 "%s: cannot load file '%s' - %s\n", Cmd,
392 argv[0], strerror(errno));
393 return 2;
395 if (ispatch) {
396 f = load_file(argv[1]);
397 if (f.body == NULL) {
398 fprintf(stderr,
399 "%s: cannot load patch '%s' - %s\n", Cmd,
400 argv[1], strerror(errno));
401 return 2;
403 if (which == '2')
404 chunks2 = chunks3 =
405 split_patch(f, &flist[2],
406 &flist[1]);
407 else
408 chunks2 = chunks3 =
409 split_patch(f, &flist[1],
410 &flist[2]);
412 } else
413 flist[1] = load_file(argv[1]);
414 if (flist[1].body == NULL) {
415 fprintf(stderr,
416 "%s: cannot load file '%s' - %s\n", Cmd,
417 argv[1], strerror(errno));
418 return 2;
420 break;
421 default:
422 fprintf(stderr,
423 "%s: too many files given for --diff\n", Cmd);
424 return 2;
426 if (reverse) {
427 f = flist[0];
428 flist[0] = flist[1];
429 flist[1] = f;
431 fl[0] = split_stream(flist[0], obj);
432 fl[1] = split_stream(flist[1], obj);
433 if (!(obj & WholeWord) && fl[0].elcnt > 50000 && fl[1].elcnt > 50000) {
434 /* Too big - use fewer words if possible */
435 free(fl[0].list);
436 free(fl[1].list);
437 obj |= WholeWord;
438 fl[0] = split_stream(flist[0], obj);
439 fl[1] = split_stream(flist[1], obj);
441 if (chunks2 && !chunks1)
442 csl = pdiff(fl[0], fl[1], chunks2);
443 else
444 csl = diff_patch(fl[0], fl[1], shortest);
445 if ((obj & ByMask) == ByLine) {
446 if (!chunks1)
447 printf("@@ -1,%d +1,%d @@\n",
448 fl[0].elcnt, fl[1].elcnt);
449 exit_status = do_diff_lines(fl, csl);
450 } else {
451 if (!chunks1) {
452 /* count lines in each file */
453 int l1, l2, i;
454 l1 = l2 = 0;
455 for (i = 0 ; i < fl[0].elcnt ; i++)
456 if (ends_line(fl[0].list[i]))
457 l1++;
458 for (i = 0 ; i < fl[1].elcnt ; i++)
459 if (ends_line(fl[1].list[i]))
460 l2++;
461 printf("@@ -1,%d +1,%d @@\n", l1, l2);
463 exit_status = do_diff_words(fl, csl);
465 return exit_status;
468 static int do_merge(int argc, char *argv[], int obj, int blanks,
469 int reverse, int replace, char *outfilename,
470 int ignore, int show_wiggles,
471 int quiet, int shortest)
473 /* merge three files, A B C, so changed between B and C get made to A
475 struct stream f, flist[3];
476 struct file fl[3];
477 int i;
478 int chunks1 = 0, chunks2 = 0, chunks3 = 0;
479 char *replacename = NULL, *orignew = NULL;
480 struct csl *csl1, *csl2;
481 struct ci ci;
482 FILE *outfile = stdout;
484 switch (argc) {
485 case 0:
486 fprintf(stderr, "%s: no files given for --merge\n", Cmd);
487 return 2;
488 case 3:
489 case 2:
490 case 1:
491 for (i = 0; i < argc; i++) {
492 flist[i] = load_file(argv[i]);
493 if (flist[i].body == NULL) {
494 fprintf(stderr, "%s: cannot load file '%s' - %s\n",
495 Cmd,
496 argv[i], strerror(errno));
497 return 2;
500 break;
501 default:
502 fprintf(stderr, "%s: too many files given for --merge\n",
503 Cmd);
504 return 2;
506 switch (argc) {
507 case 1: /* a merge file */
508 f = flist[0];
509 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
510 fprintf(stderr, "%s: merge file %s looks bad.\n",
511 Cmd,
512 argv[0]);
513 return 2;
515 break;
516 case 2: /* a file and a patch */
517 f = flist[1];
518 chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]);
519 break;
520 case 3: /* three separate files */
521 break;
523 if (reverse) {
524 f = flist[1];
525 flist[1] = flist[2];
526 flist[2] = f;
529 for (i = 0; i < 3; i++) {
530 if (flist[i].body == NULL) {
531 fprintf(stderr, "%s: file %d missing\n", Cmd, i);
532 return 2;
535 if (outfilename) {
536 outfile = fopen(outfilename, "w");
537 if (!outfile) {
538 fprintf(stderr, "%s: could not create %s\n",
539 Cmd, outfilename);
540 return 2;
542 } else if (replace) {
543 int fd;
544 replacename = xmalloc(strlen(argv[0]) + 20);
545 orignew = xmalloc(strlen(argv[0]) + 20);
546 strcpy(replacename, argv[0]);
547 strcpy(orignew, argv[0]);
548 strcat(orignew, ".porig");
549 if (open(orignew, O_RDONLY) >= 0 ||
550 errno != ENOENT) {
551 fprintf(stderr, "%s: %s already exists\n",
552 Cmd,
553 orignew);
554 free(replacename);
555 free(orignew);
556 return 2;
558 strcat(replacename, "XXXXXX");
559 fd = mkstemp(replacename);
560 if (fd == -1) {
561 fprintf(stderr,
562 "%s: could not create temporary file for %s\n",
563 Cmd,
564 replacename);
565 free(replacename);
566 free(orignew);
567 return 2;
569 outfile = fdopen(fd, "w");
572 if (obj == 'l')
573 blanks |= ByLine;
574 else
575 blanks |= ByWord;
576 fl[0] = split_stream(flist[0], blanks);
577 fl[1] = split_stream(flist[1], blanks);
578 fl[2] = split_stream(flist[2], blanks);
579 if (!(blanks & WholeWord) &&
580 fl[1].elcnt > 50000 &&
581 (fl[0].elcnt > 50000 || fl[2].elcnt > 50000)) {
582 /* Too many words */
583 free(fl[0].list);
584 free(fl[1].list);
585 free(fl[2].list);
586 blanks |= WholeWord;
587 fl[0] = split_stream(flist[0], blanks);
588 fl[1] = split_stream(flist[1], blanks);
589 fl[2] = split_stream(flist[2], blanks);
592 if (chunks2 && !chunks1)
593 csl1 = pdiff(fl[0], fl[1], chunks2);
594 else
595 csl1 = diff(fl[0], fl[1], shortest);
596 csl2 = diff_patch(fl[1], fl[2], shortest);
598 ci = make_merger(fl[0], fl[1], fl[2], csl1, csl2,
599 obj == 'w', ignore, show_wiggles > 1);
600 print_merge(outfile, &fl[0], &fl[1], &fl[2],
601 obj == 'w', ci.merger, NULL, 0, 0);
602 if (!quiet && ci.conflicts)
603 fprintf(stderr,
604 "%d unresolved conflict%s found\n",
605 ci.conflicts,
606 ci.conflicts == 1 ? "" : "s");
607 if (!quiet && ci.ignored)
608 fprintf(stderr,
609 "%d already-applied change%s ignored\n",
610 ci.ignored,
611 ci.ignored == 1 ? "" : "s");
613 if (outfilename)
614 fclose(outfile);
615 else if (replace) {
616 struct stat statbuf;
618 if (stat(argv[0], &statbuf) != 0) {
619 fprintf(stderr,
620 "%s: failed to stat original file. - %s\n",
621 Cmd, strerror(errno));
622 free(replacename);
623 free(orignew);
624 return 2;
626 if (fchmod(fileno(outfile), statbuf.st_mode) != 0) {
627 fprintf(stderr,
628 "%s: failed to change permission of new file. - %s\n",
629 Cmd, strerror(errno));
630 free(replacename);
631 free(orignew);
632 return 2;
634 fclose(outfile);
635 if (rename(argv[0], orignew) == 0 &&
636 rename(replacename, argv[0]) == 0)
637 /* all ok */;
638 else {
639 fprintf(stderr,
640 "%s: failed to move new file into place.\n",
641 Cmd);
642 free(replacename);
643 free(orignew);
644 return 2;
647 free(replacename);
648 free(orignew);
649 if (show_wiggles)
650 return ci.conflicts + ci.wiggles > 0;
651 else
652 return ci.conflicts > 0;
655 static int multi_merge(int argc, char *argv[], int obj, int blanks,
656 int reverse, int ignore, int show_wiggles,
657 int replace, int strip,
658 int quiet, int shortest)
660 FILE *f;
661 char *filename;
662 struct plist *pl;
663 int num_patches;
664 int rv = 0;
665 int i;
667 if (!replace) {
668 fprintf(stderr,
669 "%s: -p in merge mode requires -r\n",
670 Cmd);
671 return 2;
673 if (argc != 1) {
674 fprintf(stderr,
675 "%s: -p in merge mode requires exactly one file\n",
676 Cmd);
677 return 2;
679 filename = argv[0];
680 f = fopen(filename, "r");
681 if (!f) {
682 fprintf(stderr, "%s: cannot open %s\n",
683 Cmd, filename);
684 return 2;
686 check_dir(filename, fileno(f));
687 pl = parse_patch(f, NULL, &num_patches);
688 fclose(f);
689 if (set_prefix(pl, num_patches, strip) == 0) {
690 fprintf(stderr, "%s: aborting\n", Cmd);
691 return 2;
693 for (i = 0; i < num_patches; i++) {
694 char *name;
695 char *av[2];
696 asprintf(&name, "_wiggle_:%d:%d:%s",
697 pl[i].start, pl[i].end, filename);
698 av[0] = pl[i].file;
699 av[1] = name;
700 rv |= do_merge(2, av, obj, blanks, reverse, 1, NULL, ignore,
701 show_wiggles, quiet, shortest);
703 return rv;
706 int main(int argc, char *argv[])
708 int opt;
709 int option_index;
710 int mode = 0;
711 int obj = 0;
712 int replace = 0;
713 int backup = 1;
714 int which = 0;
715 int ispatch = 0;
716 int reverse = 0;
717 int verbose = 0, quiet = 0;
718 int strip = -1;
719 int exit_status = 0;
720 int ignore = 1;
721 int shortest = 0;
722 int show_wiggles = 0;
723 char *helpmsg;
724 char *trace;
725 char *outfile = NULL;
726 int selftest = 0;
727 int ignore_blanks = 0;
729 trace = getenv("WIGGLE_TRACE");
730 if (trace && *trace)
731 do_trace = 1;
733 while ((opt = getopt_long(argc, argv,
734 short_options, long_options,
735 &option_index)) != -1)
736 switch (opt) {
737 case 'h':
738 helpmsg = Help;
739 switch (mode) {
740 case 'x':
741 helpmsg = HelpExtract;
742 break;
743 case 'd':
744 helpmsg = HelpDiff;
745 break;
746 case 'm':
747 helpmsg = HelpMerge;
748 break;
749 case 'B':
750 helpmsg = HelpBrowse;
751 break;
753 fputs(helpmsg, stderr);
754 exit(0);
756 case 'V':
757 fputs(Version, stderr);
758 exit(0);
759 case ':':
760 case '?':
761 default:
762 fputs(Usage, stderr);
763 exit(2);
765 case 'B':
766 case 'x':
767 case 'd':
768 case 'm':
769 if (mode == 0) {
770 mode = opt;
771 continue;
773 if (mode == 'B' && opt == 'd') {
774 /* Browse/diff mode */
775 ispatch = 2;
776 continue;
778 fprintf(stderr,
779 "%s: mode is '%c' - cannot set to '%c'\n",
780 Cmd, mode, opt);
781 exit(2);
783 case NON_SPACE:
784 ignore_blanks |= WholeWord;
785 continue;
787 case SHORTEST:
788 shortest = 1;
789 continue;
791 case 'w':
792 case 'l':
793 if (obj == 0 || obj == opt) {
794 obj = opt;
795 continue;
797 fprintf(stderr,
798 "%s: cannot select both words and lines.\n", Cmd);
799 exit(2);
801 case 'r':
802 replace = 1;
803 continue;
804 case NO_BACKUP:
805 backup = 0;
806 continue;
807 case 'o':
808 outfile = optarg;
809 replace = 1;
810 continue;
811 case 'R':
812 reverse = 1;
813 continue;
815 case 'b':
816 ignore_blanks |= IgnoreBlanks;
817 continue;
819 case 'i':
820 ignore = 0;
821 continue;
822 case 'W':
823 show_wiggles = 2;
824 ignore = 0;
825 continue;
826 case REPORT_WIGGLES:
827 show_wiggles = 1;
828 continue;
830 case '1':
831 case '2':
832 case '3':
833 if (which == 0 || which == opt) {
834 which = opt;
835 continue;
837 fprintf(stderr,
838 "%s: can only select one of -1, -2, -3\n", Cmd);
839 exit(2);
841 case 'p': /* 'patch' or 'strip' */
842 if (optarg)
843 strip = atol(optarg);
844 ispatch = 1;
845 continue;
847 case 'v':
848 verbose++;
849 continue;
850 case 'q':
851 quiet = 1;
852 continue;
854 case SELF_TEST:
855 selftest = 1;
856 continue;
858 if (!mode)
859 mode = 'm';
861 if (mode == 'B') {
862 vpatch(argc-optind, argv+optind, ispatch,
863 strip, reverse, replace, outfile, selftest,
864 ignore_blanks, backup);
865 /* should not return */
866 exit(1);
869 if (obj && mode == 'x') {
870 fprintf(stderr,
871 "%s: cannot specify --line or --word with --extract\n",
872 Cmd);
873 exit(2);
875 if (mode != 'm' && !obj)
876 obj = 'w';
877 if (ispatch && outfile) {
878 fprintf(stderr, "%s: --output incompatible with --patch\n",
879 Cmd);
880 exit(2);
882 if (replace && mode != 'm') {
883 fprintf(stderr,
884 "%s: --replace or --output only allowed with --merge\n", Cmd);
885 exit(2);
887 if (mode == 'x' && !which) {
888 fprintf(stderr,
889 "%s: must specify -1, -2 or -3 with --extract\n", Cmd);
890 exit(2);
892 if (mode != 'x' && mode != 'd' && which) {
893 fprintf(stderr,
894 "%s: -1, -2 or -3 only allowed with --extract or --diff\n",
895 Cmd);
896 exit(2);
899 if (ispatch && which == '3') {
900 fprintf(stderr,
901 "%s: cannot extract -3 from a patch.\n", Cmd);
902 exit(2);
905 switch (mode) {
906 case 'x':
907 exit_status = extract(argc-optind, argv+optind, ispatch, which);
908 break;
909 case 'd':
910 exit_status = do_diff(argc-optind, argv+optind,
911 (obj == 'l' ? ByLine : ByWord)
912 | ignore_blanks,
913 ispatch, which, reverse, shortest);
914 break;
915 case 'm':
916 if (ispatch)
917 exit_status = multi_merge(argc-optind,
918 argv+optind, obj,
919 ignore_blanks,
920 reverse, ignore,
921 show_wiggles,
922 replace, strip,
923 quiet, shortest);
924 else
925 exit_status = do_merge(
926 argc-optind, argv+optind,
927 obj, ignore_blanks, reverse, replace,
928 outfile,
929 ignore, show_wiggles, quiet, shortest);
930 break;
932 exit(exit_status);