]> git.neil.brown.name Git - wiggle.git/blob - wiggle.c
Add --output option to redirect output.
[wiggle.git] / wiggle.c
1 /*
2  * wiggle - apply rejected patches
3  *
4  * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
5  * Copyright (C) 2010 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; if not, write to the Free Software Foundation, Inc.,
20  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  *    Author: Neil Brown
23  *    Email: <neilb@suse.de>
24  */
25
26 /*
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.
31  *
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.
36  *
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
41  *
42  * This is also a --browse (-B) mode which provides interactive access
43  * to the merger.
44  *
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.
48  *
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.
54  *
55  * For diff:
56  *    If one file is given, it is a patch
57  *    If two files are given, they are normal files.
58  *
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.
64  *
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.
68  *
69  * In the case of -w, an initial diff is computed based on non-trivial words
70  * which includes alhpanumeric words and newlines.
71  *
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
75  *
76  * Other options available are:
77  *   --replace -r   replace first file with  result of merge.
78  *   --help -h      provide help
79  *   --version -v   version
80  *
81  * Defaults are --merge --words
82  *
83  */
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>
93
94 char *Cmd = "wiggle";
95 int do_trace = 0;
96
97 void die(char *reason)
98 {
99         fprintf(stderr, "%s: fatal error: %s failure\n", Cmd, reason);
100         exit(3);
101 }
102
103 void check_dir(char *name, int fd)
104 {
105         struct stat st;
106         if (fstat(fd, &st) != 0) {
107                 fprintf(stderr, "%s: fatal: %s is strange\n", Cmd, name);
108                 exit(3);
109         }
110         if (S_ISDIR(st.st_mode)) {
111                 fprintf(stderr, "%s: %s is a directory\n", Cmd, name);
112                 exit(3);
113         }
114 }
115
116 void *xmalloc(int size)
117 {
118         void *rv = malloc(size);
119         if (size && !rv) {
120                 char *msg = "Failed to allocate memory - aborting\n";
121                 write(2, msg, strlen(msg));
122                 exit(3);
123         }
124         return rv;
125 }
126
127 void printword(FILE *f, struct elmnt e)
128 {
129         if (e.start[0])
130                 fprintf(f, "%.*s", e.plen + e.prefix,
131                         e.start - e.prefix);
132         else {
133                 int a, b, c;
134                 sscanf(e.start+1, "%d %d %d", &a, &b, &c);
135                 fprintf(f, "*** %d,%d **** %d%s", b, c, a, e.start+18);
136         }
137 }
138
139 static void printsep(struct elmnt e1, struct elmnt e2)
140 {
141         int a, b, c, d, e, f;
142         sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
143         sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
144         printf("@@ -%d,%d +%d,%d @@%s", b, c, e, f, e1.start+18);
145 }
146
147 static int extract(int argc, char *argv[], int ispatch, int which)
148 {
149         /* extract a branch of a diff or diff3 or merge output
150          * We need one file
151          */
152         struct stream f, flist[3];
153
154         if (argc == 0) {
155                 fprintf(stderr,
156                         "%s: no file given for --extract\n", Cmd);
157                 return 2;
158         }
159         if (argc > 1) {
160                 fprintf(stderr,
161                         "%s: only give one file for --extract\n", Cmd);
162                 return 2;
163         }
164         f = load_file(argv[0]);
165         if (f.body == NULL) {
166                 fprintf(stderr,
167                         "%s: cannot load file '%s' - %s\n", Cmd,
168                         argv[0], strerror(errno));
169                 return 2;
170         }
171         if (ispatch) {
172                 if (split_patch(f, &flist[0], &flist[1]) == 0) {
173                         fprintf(stderr,
174                                 "%s: No chunk found in patch: %s\n", Cmd,
175                                 argv[0]);
176                         return 0;
177                 }
178         } else {
179                 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
180                         fprintf(stderr,
181                                 "%s: merge file %s looks bad.\n", Cmd,
182                                 argv[0]);
183                         return 2;
184                 }
185         }
186         if (flist[which-'1'].body == NULL) {
187                 fprintf(stderr,
188                         "%s: %s has no -%c component.\n", Cmd,
189                         argv[0], which);
190                 return 2;
191         } else {
192                 if (write(1, flist[which-'1'].body,
193                           flist[which-'1'].len)
194                     != flist[which-'1'].len)
195                         return 2;
196         }
197         return 0;
198 }
199
200 static int do_diff_lines(struct file fl[2], struct csl *csl)
201 {
202         int a, b;
203         int exit_status = 0;
204         a = b = 0;
205         while (a < fl[0].elcnt || b < fl[1].elcnt) {
206                 if (a < csl->a) {
207                         if (fl[0].list[a].start[0]) {
208                                 printf("-");
209                                 printword(stdout,
210                                           fl[0].list[a]);
211                         }
212                         a++;
213                         exit_status++;
214                 } else if (b < csl->b) {
215                         if (fl[1].list[b].start[0]) {
216                                 printf("+");
217                                 printword(stdout,
218                                           fl[1].list[b]);
219                         }
220                         b++;
221                         exit_status++;
222                 } else {
223                         if (fl[0].list[a].start[0] == '\0')
224                                 printsep(fl[0].list[a],
225                                          fl[1].list[b]);
226                         else {
227                                 printf(" ");
228                                 printword(stdout,
229                                           fl[0].list[a]);
230                         }
231                         a++;
232                         b++;
233                         if (a >= csl->a+csl->len)
234                                 csl++;
235                 }
236         }
237         return exit_status;
238 }
239
240 static int do_diff_words(struct file fl[2], struct csl *csl)
241 {
242         int a, b;
243         int exit_status  = 0;
244         int sol = 1; /* start of line */
245         a = b = 0;
246         while (a < fl[0].elcnt || b < fl[1].elcnt) {
247                 if (a < csl->a) {
248                         exit_status++;
249                         if (sol) {
250                                 int a1;
251                                 /* If we remove a
252                                  * whole line, output
253                                  * +line else clear
254                                  * sol and retry */
255                                 sol = 0;
256                                 for (a1 = a; a1 < csl->a ; a1++)
257                                         if (ends_line(fl[0].list[a1])) {
258                                                 sol = 1;
259                                                 break;
260                                         }
261                                 if (sol) {
262                                         printf("-");
263                                         for (; a < csl->a ; a++) {
264                                                 printword(stdout, fl[0].list[a]);
265                                                 if (ends_line(fl[0].list[a])) {
266                                                         a++;
267                                                         break;
268                                                 }
269                                         }
270                                 } else
271                                         printf("|");
272                         }
273                         if (!sol) {
274                                 printf("<<<--");
275                                 do {
276                                         if (sol)
277                                                 printf("|");
278                                         printword(stdout, fl[0].list[a]);
279                                         sol = ends_line(fl[0].list[a]);
280                                         a++;
281                                 } while (a < csl->a);
282                                 printf("%s-->>>", sol ? "|" : "");
283                                 sol = 0;
284                         }
285                 } else if (b < csl->b) {
286                         exit_status++;
287                         if (sol) {
288                                 int b1;
289                                 sol = 0;
290                                 for (b1 = b; b1 < csl->b; b1++)
291                                         if (ends_line(fl[1].list[b1])) {
292                                                 sol = 1;
293                                                 break;
294                                         }
295                                 if (sol) {
296                                         printf("+");
297                                         for (; b < csl->b ; b++) {
298                                                 printword(stdout, fl[1].list[b]);
299                                                 if (ends_line(fl[1].list[b])) {
300                                                         b++;
301                                                         break;
302                                                 }
303                                         }
304                                 } else
305                                         printf("|");
306                         }
307                         if (!sol) {
308                                 printf("<<<++");
309                                 do {
310                                         if (sol)
311                                                 printf("|");
312                                         printword(stdout, fl[1].list[b]);
313                                         sol = ends_line(fl[1].list[b]);
314                                         b++;
315                                 } while (b < csl->b);
316                                 printf("%s++>>>", sol ? "|" : "");
317                                 sol = 0;
318                         }
319                 } else {
320                         if (sol) {
321                                 int a1;
322                                 sol = 0;
323                                 for (a1 = a; a1 < csl->a+csl->len; a1++)
324                                         if (ends_line(fl[0].list[a1]))
325                                                 sol = 1;
326                                 if (sol) {
327                                         if (fl[0].list[a].start[0]) {
328                                                 printf(" ");
329                                                 for (; a < csl->a+csl->len; a++, b++) {
330                                                         printword(stdout, fl[0].list[a]);
331                                                         if (ends_line(fl[0].list[a])) {
332                                                                 a++, b++;
333                                                                 break;
334                                                         }
335                                                 }
336                                         } else {
337                                                 printsep(fl[0].list[a], fl[1].list[b]);
338                                                 a++; b++;
339                                         }
340                                 } else
341                                         printf("|");
342                         }
343                         if (!sol) {
344                                 printword(stdout, fl[0].list[a]);
345                                 if (ends_line(fl[0].list[a]))
346                                         sol = 1;
347                                 a++;
348                                 b++;
349                         }
350                         if (a >= csl->a+csl->len)
351                                 csl++;
352                 }
353         }
354         return exit_status;
355 }
356
357 static int do_diff(int argc, char *argv[], int obj, int ispatch,
358                    int which, int reverse)
359 {
360         /* create a diff (line or char) of two streams */
361         struct stream f, flist[3];
362         int chunks1 = 0, chunks2 = 0, chunks3 = 0;
363         int exit_status = 0;
364         struct file fl[2];
365         struct csl *csl;
366
367         switch (argc) {
368         case 0:
369                 fprintf(stderr, "%s: no file given for --diff\n", Cmd);
370                 return 2;
371         case 1:
372                 f = load_file(argv[0]);
373                 if (f.body == NULL) {
374                         fprintf(stderr,
375                                 "%s: cannot load file '%s' - %s\n", Cmd,
376                                 argv[0], strerror(errno));
377                         return 2;
378                 }
379                 chunks1 = chunks2 =
380                         split_patch(f, &flist[0], &flist[1]);
381                 if (!flist[0].body || !flist[1].body) {
382                         fprintf(stderr,
383                                 "%s: couldn't parse patch %s\n", Cmd,
384                                 argv[0]);
385                         return 2;
386                 }
387                 break;
388         case 2:
389                 flist[0] = load_file(argv[0]);
390                 if (flist[0].body == NULL) {
391                         fprintf(stderr,
392                                 "%s: cannot load file '%s' - %s\n", Cmd,
393                                 argv[0], strerror(errno));
394                         return 2;
395                 }
396                 if (ispatch) {
397                         f = load_file(argv[1]);
398                         if (f.body == NULL) {
399                                 fprintf(stderr,
400                                         "%s: cannot load patch '%s' - %s\n", Cmd,
401                                         argv[1], strerror(errno));
402                                 return 2;
403                         }
404                         if (which == '2')
405                                 chunks2 = chunks3 =
406                                         split_patch(f, &flist[2],
407                                                     &flist[1]);
408                         else
409                                 chunks2 = chunks3 =
410                                         split_patch(f, &flist[1],
411                                                     &flist[2]);
412
413                 } else
414                         flist[1] = load_file(argv[1]);
415                 if (flist[1].body == NULL) {
416                         fprintf(stderr,
417                                 "%s: cannot load file '%s' - %s\n", Cmd,
418                                 argv[1], strerror(errno));
419                         return 2;
420                 }
421                 break;
422         default:
423                 fprintf(stderr,
424                         "%s: too many files given for --diff\n", Cmd);
425                 return 2;
426         }
427         if (reverse) {
428                 f = flist[1];
429                 flist[1] = flist[2];
430                 flist[2] = f;
431         }
432         fl[0] = split_stream(flist[0], obj);
433         fl[1] = split_stream(flist[1], obj);
434         if (chunks2 && !chunks1)
435                 csl = pdiff(fl[0], fl[1], chunks2);
436         else
437                 csl = diff_patch(fl[0], fl[1]);
438         if ((obj & ByMask) == ByLine) {
439                 if (!chunks1)
440                         printf("@@ -1,%d +1,%d @@\n",
441                                fl[0].elcnt, fl[1].elcnt);
442                 exit_status = do_diff_lines(fl, csl);
443         } else {
444                 if (!chunks1) {
445                         /* count lines in each file */
446                         int l1, l2, i;
447                         l1 = l2 = 0;
448                         for (i = 0 ; i < fl[0].elcnt ; i++)
449                                 if (ends_line(fl[0].list[i]))
450                                         l1++;
451                         for (i = 0 ; i < fl[1].elcnt ; i++)
452                                 if (ends_line(fl[1].list[i]))
453                                         l2++;
454                         printf("@@ -1,%d +1,%d @@\n", l1, l2);
455                 }
456                 exit_status = do_diff_words(fl, csl);
457         }
458         return exit_status;
459 }
460
461 static int do_merge(int argc, char *argv[], int obj, int blanks,
462                     int reverse, int replace, char *outfilename,
463                     int ignore, int show_wiggles,
464                     int quiet)
465 {
466         /* merge three files, A B C, so changed between B and C get made to A
467          */
468         struct stream f, flist[3];
469         struct file fl[3];
470         int i;
471         int chunks1 = 0, chunks2 = 0, chunks3 = 0;
472         char *replacename = NULL, *orignew = NULL;
473         struct csl *csl1, *csl2;
474         struct ci ci;
475         FILE *outfile = stdout;
476
477         switch (argc) {
478         case 0:
479                 fprintf(stderr, "%s: no files given for --merge\n", Cmd);
480                 return 2;
481         case 3:
482         case 2:
483         case 1:
484                 for (i = 0; i < argc; i++) {
485                         flist[i] = load_file(argv[i]);
486                         if (flist[i].body == NULL) {
487                                 fprintf(stderr, "%s: cannot load file '%s' - %s\n",
488                                         Cmd,
489                                         argv[i], strerror(errno));
490                                 return 2;
491                         }
492                 }
493                 break;
494         default:
495                 fprintf(stderr, "%s: too many files given for --merge\n",
496                         Cmd);
497                 return 2;
498         }
499         switch (argc) {
500         case 1: /* a merge file */
501                 f = flist[0];
502                 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
503                         fprintf(stderr, "%s: merge file %s looks bad.\n",
504                                 Cmd,
505                                 argv[0]);
506                         return 2;
507                 }
508                 break;
509         case 2: /* a file and a patch */
510                 f = flist[1];
511                 chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]);
512                 break;
513         case 3: /* three separate files */
514                 break;
515         }
516         if (reverse) {
517                 f = flist[1];
518                 flist[1] = flist[2];
519                 flist[2] = f;
520         }
521
522         for (i = 0; i < 3; i++) {
523                 if (flist[i].body == NULL) {
524                         fprintf(stderr, "%s: file %d missing\n", Cmd, i);
525                         return 2;
526                 }
527         }
528         if (outfilename) {
529                 outfile = fopen(outfilename, "w");
530                 if (!outfile) {
531                         fprintf(stderr, "%s: could not create %s\n",
532                                 Cmd, outfilename);
533                         return 2;
534                 }
535         } else if (replace) {
536                 int fd;
537                 replacename = xmalloc(strlen(argv[0]) + 20);
538                 orignew = xmalloc(strlen(argv[0]) + 20);
539                 strcpy(replacename, argv[0]);
540                 strcpy(orignew, argv[0]);
541                 strcat(orignew, ".porig");
542                 if (open(orignew, O_RDONLY) >= 0 ||
543                     errno != ENOENT) {
544                         fprintf(stderr, "%s: %s already exists\n",
545                                 Cmd,
546                                 orignew);
547                         return 2;
548                 }
549                 strcat(replacename, "XXXXXX");
550                 fd = mkstemp(replacename);
551                 if (fd == -1) {
552                         fprintf(stderr,
553                                 "%s: could not create temporary file for %s\n",
554                                 Cmd,
555                                 replacename);
556                         return 2;
557                 }
558                 outfile = fdopen(fd, "w");
559         }
560
561         if (obj == 'l')
562                 blanks |= ByLine;
563         else
564                 blanks |= ByWord;
565         fl[0] = split_stream(flist[0], blanks);
566         fl[1] = split_stream(flist[1], blanks);
567         fl[2] = split_stream(flist[2], blanks);
568
569         if (chunks2 && !chunks1)
570                 csl1 = pdiff(fl[0], fl[1], chunks2);
571         else
572                 csl1 = diff(fl[0], fl[1]);
573         csl2 = diff_patch(fl[1], fl[2]);
574
575         ci = make_merger(fl[0], fl[1], fl[2], csl1, csl2,
576                          obj == 'w', ignore, show_wiggles > 1);
577         print_merge(outfile, &fl[0], &fl[1], &fl[2],
578                     obj == 'w', ci.merger, NULL, 0, 0);
579         if (!quiet && ci.conflicts)
580                 fprintf(stderr,
581                         "%d unresolved conflict%s found\n",
582                         ci.conflicts,
583                         ci.conflicts == 1 ? "" : "s");
584         if (!quiet && ci.ignored)
585                 fprintf(stderr,
586                         "%d already-applied change%s ignored\n",
587                         ci.ignored,
588                         ci.ignored == 1 ? "" : "s");
589
590         if (outfilename)
591                 fclose(outfile);
592         else if (replace) {
593                 fclose(outfile);
594                 if (rename(argv[0], orignew) == 0 &&
595                     rename(replacename, argv[0]) == 0)
596                         /* all ok */;
597                 else {
598                         fprintf(stderr,
599                                 "%s: failed to move new file into place.\n",
600                                 Cmd);
601                         return 2;
602                 }
603         }
604         if (show_wiggles)
605                 return ci.conflicts + ci.wiggles > 0;
606         else
607                 return ci.conflicts > 0;
608 }
609
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)
614 {
615         FILE *f;
616         char *filename;
617         struct plist *pl;
618         int num_patches;
619         int rv = 0;
620         int i;
621
622         if (!replace) {
623                 fprintf(stderr,
624                         "%s: -p in merge mode requires -r\n",
625                         Cmd);
626                 return 2;
627         }
628         if (argc != 1) {
629                 fprintf(stderr,
630                         "%s: -p in merge mode requires exactly one file\n",
631                         Cmd);
632                 return 2;
633         }
634         filename = argv[0];
635         f = fopen(filename, "r");
636         if (!f) {
637                 fprintf(stderr, "%s: cannot open %s\n",
638                         Cmd, filename);
639                 return 2;
640         }
641         check_dir(filename, fileno(f));
642         pl = parse_patch(f, NULL, &num_patches);
643         fclose(f);
644         if (set_prefix(pl, num_patches, strip) == 0) {
645                 fprintf(stderr, "%s: aborting\n", Cmd);
646                 return 2;
647         }
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);
657         }
658         return rv;
659 }
660
661 int main(int argc, char *argv[])
662 {
663         int opt;
664         int option_index;
665         int mode = 0;
666         int obj = 0;
667         int replace = 0;
668         int which = 0;
669         int ispatch = 0;
670         int reverse = 0;
671         int verbose = 0, quiet = 0;
672         int strip = -1;
673         int exit_status = 0;
674         int ignore = 1;
675         int show_wiggles = 0;
676         char *helpmsg;
677         char *trace;
678         char *outfile = NULL;
679         int selftest = 0;
680         int ignore_blanks = 0;
681
682         trace = getenv("WIGGLE_TRACE");
683         if (trace && *trace)
684                 do_trace = 1;
685
686         while ((opt = getopt_long(argc, argv,
687                                   short_options, long_options,
688                                   &option_index)) != -1)
689                 switch (opt) {
690                 case 'h':
691                         helpmsg = Help;
692                         switch (mode) {
693                         case 'x':
694                                 helpmsg = HelpExtract;
695                                 break;
696                         case 'd':
697                                 helpmsg = HelpDiff;
698                                 break;
699                         case 'm':
700                                 helpmsg = HelpMerge;
701                                 break;
702                         case 'B':
703                                 helpmsg = HelpBrowse;
704                                 break;
705                         }
706                         fputs(helpmsg, stderr);
707                         exit(0);
708
709                 case 'V':
710                         fputs(Version, stderr);
711                         exit(0);
712                 case ':':
713                 case '?':
714                 default:
715                         fputs(Usage, stderr);
716                         exit(2);
717
718                 case 'B':
719                 case 'x':
720                 case 'd':
721                 case 'm':
722                         if (mode == 0) {
723                                 mode = opt;
724                                 continue;
725                         }
726                         if (mode == 'B' && opt == 'd') {
727                                 /* Browse/diff mode */
728                                 ispatch = 2;
729                                 continue;
730                         }
731                         fprintf(stderr,
732                                 "%s: mode is '%c' - cannot set to '%c'\n",
733                                 Cmd, mode, opt);
734                         exit(2);
735
736                 case 'w':
737                 case 'l':
738                         if (obj == 0 || obj == opt) {
739                                 obj = opt;
740                                 continue;
741                         }
742                         fprintf(stderr,
743                                 "%s: cannot select both words and lines.\n", Cmd);
744                         exit(2);
745
746                 case 'r':
747                         replace = 1;
748                         continue;
749                 case 'o':
750                         outfile = optarg;
751                         replace = 1;
752                         continue;
753                 case 'R':
754                         reverse = 1;
755                         continue;
756
757                 case 'b':
758                         ignore_blanks = IgnoreBlanks;
759                         continue;
760
761                 case 'i':
762                         ignore = 0;
763                         continue;
764                 case 'W':
765                         show_wiggles = 2;
766                         ignore = 0;
767                         continue;
768                 case REPORT_WIGGLES:
769                         show_wiggles = 1;
770                         continue;
771
772                 case '1':
773                 case '2':
774                 case '3':
775                         if (which == 0 || which == opt) {
776                                 which = opt;
777                                 continue;
778                         }
779                         fprintf(stderr,
780                                 "%s: can only select one of -1, -2, -3\n", Cmd);
781                         exit(2);
782
783                 case 'p': /* 'patch' or 'strip' */
784                         if (optarg)
785                                 strip = atol(optarg);
786                         ispatch = 1;
787                         continue;
788
789                 case 'v':
790                         verbose++;
791                         continue;
792                 case 'q':
793                         quiet = 1;
794                         continue;
795
796                 case SELF_TEST:
797                         selftest = 1;
798                         continue;
799                 }
800         if (!mode)
801                 mode = 'm';
802
803         if (mode == 'B') {
804                 vpatch(argc-optind, argv+optind, ispatch,
805                        strip, reverse, replace, outfile, selftest,
806                        ignore_blanks);
807                 /* should not return */
808                 exit(1);
809         }
810
811         if (obj && mode == 'x') {
812                 fprintf(stderr,
813                         "%s: cannot specify --line or --word with --extract\n",
814                         Cmd);
815                 exit(2);
816         }
817         if (mode != 'm' && !obj)
818                 obj = 'w';
819         if (ispatch && outfile) {
820                 fprintf(stderr, "%s: --output incompatible with --patch\n",
821                         Cmd);
822                 exit(2);
823         }
824         if (replace && mode != 'm') {
825                 fprintf(stderr,
826                         "%s: --replace or --output only allowed with --merge\n", Cmd);
827                 exit(2);
828         }
829         if (mode == 'x' && !which) {
830                 fprintf(stderr,
831                         "%s: must specify -1, -2 or -3 with --extract\n", Cmd);
832                 exit(2);
833         }
834         if (mode != 'x' && mode != 'd' && which) {
835                 fprintf(stderr,
836                         "%s: -1, -2 or -3 only allowed with --extract or --diff\n",
837                         Cmd);
838                 exit(2);
839         }
840
841         if (ispatch && which == '3') {
842                 fprintf(stderr,
843                         "%s: cannot extract -3 from a patch.\n", Cmd);
844                 exit(2);
845         }
846
847         switch (mode) {
848         case 'x':
849                 exit_status = extract(argc-optind, argv+optind, ispatch, which);
850                 break;
851         case 'd':
852                 exit_status = do_diff(argc-optind, argv+optind,
853                                       (obj == 'l' ? ByLine : ByWord)
854                                       | ignore_blanks,
855                                       ispatch, which, reverse);
856                 break;
857         case 'm':
858                 if (ispatch)
859                         exit_status = multi_merge(argc-optind,
860                                                   argv+optind, obj,
861                                                   ignore_blanks,
862                                                   reverse, ignore,
863                                                   show_wiggles,
864                                                   replace, strip,
865                                                   quiet);
866                 else
867                         exit_status = do_merge(
868                                 argc-optind, argv+optind,
869                                 obj, ignore_blanks, reverse, replace,
870                                 outfile,
871                                 ignore, show_wiggles, quiet);
872                 break;
873         }
874         exit(exit_status);
875 }