Disable *all* backups when --no-backups used
[wiggle/upstream.git] / wiggle.c
blob3e889097c9ea465e9b7b7d36c44678ebe116487d
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>
6 * Copyright (C) 2014-2020 Neil Brown <neil@brown.name>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program.
22 * Author: Neil Brown
23 * Email: <neil@brown.name>
27 * Wiggle is a tool for working with patches that don't quite apply properly.
28 * It provides functionality similar to 'diff' and 'merge' but can
29 * work at the level of individual words thus allowing the merging of
30 * two changes that affect the same line, but not the same parts of that line.
32 * Wiggle can also read patch and merge files. Unlike 'merge' it does not
33 * need to be given three separate files, but can be given a file and a patch
34 * and it will extract the pieces of the two other files that it needs from
35 * the patch.
37 * Wiggle performs one of three core function:
38 * --extract -x extract part of a patch or merge file
39 * --diff -d report differences between two files
40 * --merge -m merge the changes between two files into a third file
42 * This is also a --browse (-B) mode which provides interactive access
43 * to the merger.
45 * To perform these, wiggle requires 1, 2, or 3 input streams respectively.
46 * I can get these from individual files, from a diff (unified or context) or
47 * from a merge file.
49 * For merge:
50 * If one file is given, it is a merge file (output of 'merge').
51 * If two files are given, the second is assumed to be a patch,
52 * 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,
61 * otherwise it is a merge.
62 * One of the flags -1 -2 or -3 must also be given and they indicate which
63 * part of the patch or merge to extract.
65 * Difference calculation and merging is performed on lines (-l) or words (-w).
66 * Each 'word' is either 1/all alphnumeric (or '_'), 2/ all space or tab,
67 * or 3/ any other single character.
69 * In the case of -w, an initial diff is computed based on non-trivial words
70 * which includes alhpanumeric words and newlines.
72 * This diff is computed from the ends of the file and is used to find
73 * a suitable starting point and range. Then a more precise diff is
74 * computed over that restricted range
76 * Other options available are:
77 * --replace -r replace first file with result of merge.
78 * --help -h provide help
79 * --version -v version
81 * Defaults are --merge --words
84 #define _GNU_SOURCE
85 #include "wiggle.h"
86 #include <errno.h>
87 #include <fcntl.h>
88 #include <unistd.h>
89 #include <stdlib.h>
90 #include <stdio.h>
91 #include <ctype.h>
92 #include <sys/stat.h>
94 static void printsep(struct elmnt e1, struct elmnt e2)
96 int a, b, c, d, e, f;
97 sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
98 sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
99 printf("@@ -%d,%d +%d,%d @@%s", b, c, e, f, e1.start+18);
102 static int extract(int argc, char *argv[], int ispatch, int which)
104 /* extract a branch of a diff or diff3 or merge output
105 * We need one file
107 struct stream f, flist[3];
109 if (argc == 0) {
110 fprintf(stderr,
111 "%s: no file given for --extract\n", wiggle_Cmd);
112 return 2;
114 if (argc > 1) {
115 fprintf(stderr,
116 "%s: only give one file for --extract\n", wiggle_Cmd);
117 return 2;
119 f = wiggle_load_file(argv[0]);
120 if (f.body == NULL) {
121 fprintf(stderr,
122 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
123 argv[0], strerror(errno));
124 return 2;
126 if (ispatch) {
127 if (wiggle_split_patch(f, &flist[0], &flist[1]) == 0) {
128 fprintf(stderr,
129 "%s: No chunk found in patch: %s\n", wiggle_Cmd,
130 argv[0]);
131 return 0;
133 } else {
134 if (!wiggle_split_merge(f, &flist[0], &flist[1], &flist[2])) {
135 fprintf(stderr,
136 "%s: merge file %s looks bad.\n", wiggle_Cmd,
137 argv[0]);
138 return 2;
141 if (flist[which-'1'].body == NULL) {
142 fprintf(stderr,
143 "%s: %s has no -%c component.\n", wiggle_Cmd,
144 argv[0], which);
145 return 2;
146 } else {
147 if (write(1, flist[which-'1'].body,
148 flist[which-'1'].len)
149 != flist[which-'1'].len)
150 return 2;
152 return 0;
155 static int do_diff_lines(struct file fl[2], struct csl *csl)
157 int a, b;
158 int exit_status = 0;
159 a = b = 0;
160 while (a < fl[0].elcnt || b < fl[1].elcnt) {
161 if (a < csl->a) {
162 if (fl[0].list[a].start[0]) {
163 printf("-");
164 wiggle_printword(stdout,
165 fl[0].list[a]);
167 a++;
168 exit_status = 1;
169 } else if (b < csl->b) {
170 if (fl[1].list[b].start[0]) {
171 printf("+");
172 wiggle_printword(stdout,
173 fl[1].list[b]);
175 b++;
176 exit_status = 1;
177 } else {
178 if (fl[0].list[a].start[0] == '\0')
179 printsep(fl[0].list[a],
180 fl[1].list[b]);
181 else {
182 printf(" ");
183 wiggle_printword(stdout,
184 fl[0].list[a]);
186 a++;
187 b++;
188 if (a >= csl->a+csl->len)
189 csl++;
192 return exit_status;
195 static int do_diff_words(struct file fl[2], struct csl *csl)
197 int a, b;
198 int exit_status = 0;
199 int sol = 1; /* start of line */
200 a = b = 0;
201 while (a < fl[0].elcnt || b < fl[1].elcnt) {
202 if (a < csl->a) {
203 exit_status = 1;
204 if (sol) {
205 int a1;
206 /* If we remove a
207 * whole line, output
208 * +line else clear
209 * sol and retry */
210 sol = 0;
211 for (a1 = a; a1 < csl->a ; a1++)
212 if (ends_line(fl[0].list[a1])) {
213 sol = 1;
214 break;
216 if (sol) {
217 printf("-");
218 for (; a < csl->a ; a++) {
219 wiggle_printword(stdout, fl[0].list[a]);
220 if (ends_line(fl[0].list[a])) {
221 a++;
222 break;
225 } else
226 printf("|");
228 if (!sol) {
229 printf("<<<--");
230 do {
231 if (sol)
232 printf("|");
233 wiggle_printword(stdout, fl[0].list[a]);
234 sol = ends_line(fl[0].list[a]);
235 a++;
236 } while (a < csl->a);
237 printf("%s-->>>", sol ? "|" : "");
238 sol = 0;
240 } else if (b < csl->b) {
241 exit_status = 1;
242 if (sol) {
243 int b1;
244 sol = 0;
245 for (b1 = b; b1 < csl->b; b1++)
246 if (ends_line(fl[1].list[b1])) {
247 sol = 1;
248 break;
250 if (sol) {
251 printf("+");
252 for (; b < csl->b ; b++) {
253 wiggle_printword(stdout, fl[1].list[b]);
254 if (ends_line(fl[1].list[b])) {
255 b++;
256 break;
259 } else
260 printf("|");
262 if (!sol) {
263 printf("<<<++");
264 do {
265 if (sol)
266 printf("|");
267 wiggle_printword(stdout, fl[1].list[b]);
268 sol = ends_line(fl[1].list[b]);
269 b++;
270 } while (b < csl->b);
271 printf("%s++>>>", sol ? "|" : "");
272 sol = 0;
274 } else {
275 if (sol) {
276 int a1;
277 sol = 0;
278 for (a1 = a; a1 < csl->a+csl->len; a1++)
279 if (ends_line(fl[0].list[a1]))
280 sol = 1;
281 if (sol) {
282 if (fl[0].list[a].start[0]) {
283 printf(" ");
284 for (; a < csl->a+csl->len; a++, b++) {
285 wiggle_printword(stdout, fl[0].list[a]);
286 if (ends_line(fl[0].list[a])) {
287 a++, b++;
288 break;
291 } else {
292 printsep(fl[0].list[a], fl[1].list[b]);
293 a++; b++;
295 } else
296 printf("|");
298 if (!sol) {
299 wiggle_printword(stdout, fl[0].list[a]);
300 if (ends_line(fl[0].list[a]))
301 sol = 1;
302 a++;
303 b++;
305 if (a >= csl->a+csl->len)
306 csl++;
309 return exit_status;
312 static int do_diff(int argc, char *argv[], int obj, int ispatch,
313 int which, int reverse, int shortest)
315 /* create a diff (line or char) of two streams */
316 struct stream f, flist[3];
317 int chunks1 = 0, chunks2 = 0, chunks3 = 0;
318 int exit_status = 0;
319 struct file fl[2];
320 struct csl *csl;
322 switch (argc) {
323 case 0:
324 fprintf(stderr, "%s: no file given for --diff\n", wiggle_Cmd);
325 return 2;
326 case 1:
327 f = wiggle_load_file(argv[0]);
328 if (f.body == NULL) {
329 fprintf(stderr,
330 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
331 argv[0], strerror(errno));
332 return 2;
334 chunks1 = chunks2 =
335 wiggle_split_patch(f, &flist[0], &flist[1]);
336 if (!flist[0].body || !flist[1].body) {
337 fprintf(stderr,
338 "%s: couldn't parse patch %s\n", wiggle_Cmd,
339 argv[0]);
340 return 2;
342 break;
343 case 2:
344 flist[0] = wiggle_load_file(argv[0]);
345 if (flist[0].body == NULL) {
346 fprintf(stderr,
347 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
348 argv[0], strerror(errno));
349 return 2;
351 if (ispatch) {
352 f = wiggle_load_file(argv[1]);
353 if (f.body == NULL) {
354 fprintf(stderr,
355 "%s: cannot load patch '%s' - %s\n", wiggle_Cmd,
356 argv[1], strerror(errno));
357 return 2;
359 if (which == '2')
360 chunks2 = chunks3 =
361 wiggle_split_patch(f, &flist[2],
362 &flist[1]);
363 else
364 chunks2 = chunks3 =
365 wiggle_split_patch(f, &flist[1],
366 &flist[2]);
368 } else
369 flist[1] = wiggle_load_file(argv[1]);
370 if (flist[1].body == NULL) {
371 fprintf(stderr,
372 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
373 argv[1], strerror(errno));
374 return 2;
376 break;
377 default:
378 fprintf(stderr,
379 "%s: too many files given for --diff\n", wiggle_Cmd);
380 return 2;
382 if (reverse) {
383 f = flist[0];
384 flist[0] = flist[1];
385 flist[1] = f;
387 fl[0] = wiggle_split_stream(flist[0], obj);
388 fl[1] = wiggle_split_stream(flist[1], obj);
389 if (!(obj & WholeWord) && fl[0].elcnt > 50000 && fl[1].elcnt > 50000) {
390 /* Too big - use fewer words if possible */
391 free(fl[0].list);
392 free(fl[1].list);
393 obj |= WholeWord;
394 fl[0] = wiggle_split_stream(flist[0], obj);
395 fl[1] = wiggle_split_stream(flist[1], obj);
397 if (chunks2 && !chunks1)
398 csl = wiggle_pdiff(fl[0], fl[1], chunks2);
399 else
400 csl = wiggle_diff_patch(fl[0], fl[1], shortest);
401 if ((obj & ByMask) == ByLine) {
402 if (!chunks1)
403 printf("@@ -1,%d +1,%d @@\n",
404 fl[0].elcnt, fl[1].elcnt);
405 exit_status = do_diff_lines(fl, csl);
406 } else {
407 if (!chunks1) {
408 /* count lines in each file */
409 int l1, l2, i;
410 l1 = l2 = 0;
411 for (i = 0 ; i < fl[0].elcnt ; i++)
412 if (ends_line(fl[0].list[i]))
413 l1++;
414 for (i = 0 ; i < fl[1].elcnt ; i++)
415 if (ends_line(fl[1].list[i]))
416 l2++;
417 printf("@@ -1,%d +1,%d @@\n", l1, l2);
419 exit_status = do_diff_words(fl, csl);
421 return exit_status;
424 static int do_merge(int argc, char *argv[], int obj, int blanks,
425 int reverse, int replace, char *outfilename,
426 int ignore, int show_wiggles,
427 int quiet, int shortest, int backup)
429 /* merge three files, A B C, so changed between B and C get made to A
431 struct stream f, flist[3];
432 struct file fl[3];
433 int i;
434 int chunks1 = 0, chunks2 = 0, chunks3 = 0;
435 char *replacename = NULL, *orignew = NULL;
436 struct csl *csl1, *csl2;
437 struct ci ci;
438 FILE *outfile = stdout;
440 switch (argc) {
441 case 0:
442 fprintf(stderr, "%s: no files given for --merge\n", wiggle_Cmd);
443 return 2;
444 case 3:
445 case 2:
446 case 1:
447 for (i = 0; i < argc; i++) {
448 flist[i] = wiggle_load_file(argv[i]);
449 if (flist[i].body == NULL) {
450 fprintf(stderr, "%s: cannot load file '%s' - %s\n",
451 wiggle_Cmd,
452 argv[i], strerror(errno));
453 return 2;
456 break;
457 default:
458 fprintf(stderr, "%s: too many files given for --merge\n",
459 wiggle_Cmd);
460 return 2;
462 switch (argc) {
463 case 1: /* a merge file */
464 f = flist[0];
465 if (!wiggle_split_merge(f, &flist[0], &flist[1], &flist[2])) {
466 fprintf(stderr, "%s: merge file %s looks bad.\n",
467 wiggle_Cmd,
468 argv[0]);
469 return 2;
471 break;
472 case 2: /* a file and a patch */
473 f = flist[1];
474 chunks2 = chunks3 = wiggle_split_patch(f, &flist[1], &flist[2]);
475 break;
476 case 3: /* three separate files */
477 break;
479 if (reverse) {
480 f = flist[1];
481 flist[1] = flist[2];
482 flist[2] = f;
485 for (i = 0; i < 3; i++) {
486 if (flist[i].body == NULL) {
487 fprintf(stderr, "%s: file %d missing\n", wiggle_Cmd, i);
488 return 2;
491 if (outfilename) {
492 outfile = fopen(outfilename, "w");
493 if (!outfile) {
494 fprintf(stderr, "%s: could not create %s\n",
495 wiggle_Cmd, outfilename);
496 return 2;
498 } else if (replace) {
499 int fd;
500 replacename = wiggle_xmalloc(strlen(argv[0]) + 20);
501 orignew = wiggle_xmalloc(strlen(argv[0]) + 20);
502 strcpy(replacename, argv[0]);
503 strcpy(orignew, argv[0]);
504 strcat(orignew, ".porig");
505 if (backup && (open(orignew, O_RDONLY) >= 0 ||
506 errno != ENOENT)) {
507 fprintf(stderr, "%s: %s already exists\n",
508 wiggle_Cmd,
509 orignew);
510 free(replacename);
511 free(orignew);
512 return 2;
514 strcat(replacename, "XXXXXX");
515 fd = mkstemp(replacename);
516 if (fd == -1) {
517 fprintf(stderr,
518 "%s: could not create temporary file for %s\n",
519 wiggle_Cmd,
520 replacename);
521 free(replacename);
522 free(orignew);
523 return 2;
525 outfile = fdopen(fd, "w");
528 if (obj == 'l')
529 blanks |= ByLine;
530 else
531 blanks |= ByWord;
532 fl[0] = wiggle_split_stream(flist[0], blanks);
533 fl[1] = wiggle_split_stream(flist[1], blanks);
534 fl[2] = wiggle_split_stream(flist[2], blanks);
535 if (!(blanks & WholeWord) &&
536 fl[1].elcnt > 50000 &&
537 (fl[0].elcnt > 50000 || fl[2].elcnt > 50000)) {
538 /* Too many words */
539 free(fl[0].list);
540 free(fl[1].list);
541 free(fl[2].list);
542 blanks |= WholeWord;
543 fl[0] = wiggle_split_stream(flist[0], blanks);
544 fl[1] = wiggle_split_stream(flist[1], blanks);
545 fl[2] = wiggle_split_stream(flist[2], blanks);
548 if (chunks2 && !chunks1)
549 csl1 = wiggle_pdiff(fl[0], fl[1], chunks2);
550 else
551 csl1 = wiggle_diff(fl[0], fl[1], shortest);
552 csl2 = wiggle_diff_patch(fl[1], fl[2], shortest);
554 ci = wiggle_make_merger(fl[0], fl[1], fl[2], csl1, csl2,
555 obj == 'w', ignore, show_wiggles > 1);
556 wiggle_print_merge(outfile, &fl[0], &fl[1], &fl[2],
557 obj == 'w', ci.merger, NULL, 0, 0);
558 if (!quiet && ci.conflicts)
559 fprintf(stderr,
560 "%d unresolved conflict%s found\n",
561 ci.conflicts,
562 ci.conflicts == 1 ? "" : "s");
563 if (!quiet && ci.ignored)
564 fprintf(stderr,
565 "%d already-applied change%s ignored\n",
566 ci.ignored,
567 ci.ignored == 1 ? "" : "s");
569 if (outfilename)
570 fclose(outfile);
571 else if (replace) {
572 struct stat statbuf;
574 if (stat(argv[0], &statbuf) != 0) {
575 fprintf(stderr,
576 "%s: failed to stat original file. - %s\n",
577 wiggle_Cmd, strerror(errno));
578 free(replacename);
579 free(orignew);
580 return 2;
582 if (fchmod(fileno(outfile), statbuf.st_mode) != 0) {
583 fprintf(stderr,
584 "%s: failed to change permission of new file. - %s\n",
585 wiggle_Cmd, strerror(errno));
586 free(replacename);
587 free(orignew);
588 return 2;
590 fclose(outfile);
591 if ((!backup || rename(argv[0], orignew) == 0) &&
592 rename(replacename, argv[0]) == 0)
593 /* all ok */;
594 else {
595 fprintf(stderr,
596 "%s: failed to move new file into place.\n",
597 wiggle_Cmd);
598 free(replacename);
599 free(orignew);
600 return 2;
603 free(replacename);
604 free(orignew);
605 if (show_wiggles)
606 return ci.conflicts + ci.wiggles > 0;
607 else
608 return ci.conflicts > 0;
611 static int multi_merge(int argc, char *argv[], int obj, int blanks,
612 int reverse, int ignore, int show_wiggles,
613 int replace, int strip,
614 int quiet, int shortest, int backup)
616 FILE *f;
617 char *filename;
618 struct plist *pl;
619 int num_patches;
620 int rv = 0;
621 int i;
623 if (!replace) {
624 fprintf(stderr,
625 "%s: -p in merge mode requires -r\n",
626 wiggle_Cmd);
627 return 2;
629 if (argc != 1) {
630 fprintf(stderr,
631 "%s: -p in merge mode requires exactly one file\n",
632 wiggle_Cmd);
633 return 2;
635 filename = argv[0];
636 f = fopen(filename, "r");
637 if (!f) {
638 fprintf(stderr, "%s: cannot open %s\n",
639 wiggle_Cmd, filename);
640 return 2;
642 wiggle_check_dir(filename, fileno(f));
643 pl = wiggle_parse_patch(f, NULL, &num_patches);
644 fclose(f);
645 if (wiggle_set_prefix(pl, num_patches, strip) == 0) {
646 fprintf(stderr, "%s: aborting\n", wiggle_Cmd);
647 return 2;
649 for (i = 0; i < num_patches; i++) {
650 char *name;
651 char *av[2];
652 asprintf(&name, "_wiggle_:%d:%d:%s",
653 pl[i].start, pl[i].end, filename);
654 av[0] = pl[i].file;
655 av[1] = name;
656 rv |= do_merge(2, av, obj, blanks, reverse, 1, NULL, ignore,
657 show_wiggles, quiet, shortest, backup);
659 return rv;
662 int main(int argc, char *argv[])
664 int opt;
665 int option_index;
666 int mode = 0;
667 int obj = 0;
668 int replace = 0;
669 int backup = 1;
670 int which = 0;
671 int ispatch = 0;
672 int reverse = 0;
673 int verbose = 0, quiet = 0;
674 int strip = -1;
675 int exit_status = 0;
676 int ignore = 1;
677 int shortest = 0;
678 int show_wiggles = 0;
679 char *helpmsg;
680 char *trace;
681 char *outfile = NULL;
682 int selftest = 0;
683 int ignore_blanks = 0;
685 trace = getenv("WIGGLE_TRACE");
686 if (trace && *trace)
687 wiggle_do_trace = 1;
689 while ((opt = getopt_long(argc, argv,
690 short_options, long_options,
691 &option_index)) != -1)
692 switch (opt) {
693 case 'h':
694 helpmsg = Help;
695 switch (mode) {
696 case 'x':
697 helpmsg = HelpExtract;
698 break;
699 case 'd':
700 helpmsg = HelpDiff;
701 break;
702 case 'm':
703 helpmsg = HelpMerge;
704 break;
705 case 'B':
706 helpmsg = HelpBrowse;
707 break;
709 fputs(helpmsg, stderr);
710 exit(0);
712 case 'V':
713 fputs(Version, stderr);
714 exit(0);
715 case ':':
716 case '?':
717 default:
718 fputs(Usage, stderr);
719 exit(2);
721 case 'B':
722 case 'x':
723 case 'd':
724 case 'm':
725 if (mode == 0) {
726 mode = opt;
727 continue;
729 if (mode == 'B' && opt == 'd') {
730 /* Browse/diff mode */
731 ispatch = 2;
732 continue;
734 fprintf(stderr,
735 "%s: mode is '%c' - cannot set to '%c'\n",
736 wiggle_Cmd, mode, opt);
737 exit(2);
739 case NON_SPACE:
740 ignore_blanks |= WholeWord;
741 continue;
743 case SHORTEST:
744 shortest = 1;
745 continue;
747 case 'w':
748 case 'l':
749 if (obj == 0 || obj == opt) {
750 obj = opt;
751 continue;
753 fprintf(stderr,
754 "%s: cannot select both words and lines.\n", wiggle_Cmd);
755 exit(2);
757 case 'r':
758 replace = 1;
759 continue;
760 case NO_BACKUP:
761 backup = 0;
762 continue;
763 case 'o':
764 outfile = optarg;
765 replace = 1;
766 continue;
767 case 'R':
768 reverse = 1;
769 continue;
771 case 'b':
772 ignore_blanks |= IgnoreBlanks;
773 continue;
775 case 'i':
776 ignore = 0;
777 continue;
778 case 'W':
779 show_wiggles = 2;
780 ignore = 0;
781 continue;
782 case REPORT_WIGGLES:
783 show_wiggles = 1;
784 continue;
786 case '1':
787 case '2':
788 case '3':
789 if (which == 0 || which == opt) {
790 which = opt;
791 continue;
793 fprintf(stderr,
794 "%s: can only select one of -1, -2, -3\n", wiggle_Cmd);
795 exit(2);
797 case 'p': /* 'patch' or 'strip' */
798 if (optarg)
799 strip = atol(optarg);
800 ispatch = 1;
801 continue;
803 case 'v':
804 verbose++;
805 continue;
806 case 'q':
807 quiet = 1;
808 continue;
810 case SELF_TEST:
811 selftest = 1;
812 continue;
814 if (!mode)
815 mode = 'm';
817 if (mode == 'B') {
818 vpatch(argc-optind, argv+optind, ispatch,
819 strip, reverse, replace, outfile, selftest,
820 ignore_blanks, backup);
821 /* should not return */
822 exit(1);
825 if (obj && mode == 'x') {
826 fprintf(stderr,
827 "%s: cannot specify --line or --word with --extract\n",
828 wiggle_Cmd);
829 exit(2);
831 if (mode != 'm' && !obj)
832 obj = 'w';
833 if (ispatch && outfile) {
834 fprintf(stderr, "%s: --output incompatible with --patch\n",
835 wiggle_Cmd);
836 exit(2);
838 if (replace && mode != 'm') {
839 fprintf(stderr,
840 "%s: --replace or --output only allowed with --merge\n", wiggle_Cmd);
841 exit(2);
843 if (mode == 'x' && !which) {
844 fprintf(stderr,
845 "%s: must specify -1, -2 or -3 with --extract\n", wiggle_Cmd);
846 exit(2);
848 if (mode != 'x' && mode != 'd' && which) {
849 fprintf(stderr,
850 "%s: -1, -2 or -3 only allowed with --extract or --diff\n",
851 wiggle_Cmd);
852 exit(2);
855 if (ispatch && which == '3') {
856 fprintf(stderr,
857 "%s: cannot extract -3 from a patch.\n", wiggle_Cmd);
858 exit(2);
861 switch (mode) {
862 case 'x':
863 exit_status = extract(argc-optind, argv+optind, ispatch, which);
864 break;
865 case 'd':
866 exit_status = do_diff(argc-optind, argv+optind,
867 (obj == 'l' ? ByLine : ByWord)
868 | ignore_blanks,
869 ispatch, which, reverse, shortest);
870 break;
871 case 'm':
872 if (ispatch)
873 exit_status = multi_merge(argc-optind,
874 argv+optind, obj,
875 ignore_blanks,
876 reverse, ignore,
877 show_wiggles,
878 replace, strip,
879 quiet, shortest,
880 backup);
881 else
882 exit_status = do_merge(
883 argc-optind, argv+optind,
884 obj, ignore_blanks, reverse, replace,
885 outfile,
886 ignore, show_wiggles, quiet, shortest,
887 backup);
888 break;
890 exit(exit_status);