]> git.neil.brown.name Git - wiggle.git/blob - wiggle.c
Disable *all* backups when --no-backups used
[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-2013 Neil Brown <neilb@suse.de>
6  * Copyright (C) 2014-2020 Neil Brown <neil@brown.name>
7  *
8  *
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.
13  *
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.
18  *
19  *    You should have received a copy of the GNU General Public License
20  *    along with this program.
21  *
22  *    Author: Neil Brown
23  *    Email: <neil@brown.name>
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 static void printsep(struct elmnt e1, struct elmnt e2)
95 {
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);
100 }
101
102 static int extract(int argc, char *argv[], int ispatch, int which)
103 {
104         /* extract a branch of a diff or diff3 or merge output
105          * We need one file
106          */
107         struct stream f, flist[3];
108
109         if (argc == 0) {
110                 fprintf(stderr,
111                         "%s: no file given for --extract\n", wiggle_Cmd);
112                 return 2;
113         }
114         if (argc > 1) {
115                 fprintf(stderr,
116                         "%s: only give one file for --extract\n", wiggle_Cmd);
117                 return 2;
118         }
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;
125         }
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;
132                 }
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;
139                 }
140         }
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;
151         }
152         return 0;
153 }
154
155 static int do_diff_lines(struct file fl[2], struct csl *csl)
156 {
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]);
166                         }
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]);
174                         }
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]);
185                         }
186                         a++;
187                         b++;
188                         if (a >= csl->a+csl->len)
189                                 csl++;
190                 }
191         }
192         return exit_status;
193 }
194
195 static int do_diff_words(struct file fl[2], struct csl *csl)
196 {
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;
215                                         }
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;
223                                                 }
224                                         }
225                                 } else
226                                         printf("|");
227                         }
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;
239                         }
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;
249                                         }
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;
257                                                 }
258                                         }
259                                 } else
260                                         printf("|");
261                         }
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;
273                         }
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;
289                                                         }
290                                                 }
291                                         } else {
292                                                 printsep(fl[0].list[a], fl[1].list[b]);
293                                                 a++; b++;
294                                         }
295                                 } else
296                                         printf("|");
297                         }
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++;
304                         }
305                         if (a >= csl->a+csl->len)
306                                 csl++;
307                 }
308         }
309         return exit_status;
310 }
311
312 static int do_diff(int argc, char *argv[], int obj, int ispatch,
313                    int which, int reverse, int shortest)
314 {
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;
321
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;
333                 }
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;
341                 }
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;
350                 }
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;
358                         }
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]);
367
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;
375                 }
376                 break;
377         default:
378                 fprintf(stderr,
379                         "%s: too many files given for --diff\n", wiggle_Cmd);
380                 return 2;
381         }
382         if (reverse) {
383                 f = flist[0];
384                 flist[0] = flist[1];
385                 flist[1] = f;
386         }
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);
396         }
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);
418                 }
419                 exit_status = do_diff_words(fl, csl);
420         }
421         return exit_status;
422 }
423
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)
428 {
429         /* merge three files, A B C, so changed between B and C get made to A
430          */
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;
439
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;
454                         }
455                 }
456                 break;
457         default:
458                 fprintf(stderr, "%s: too many files given for --merge\n",
459                         wiggle_Cmd);
460                 return 2;
461         }
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;
470                 }
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;
478         }
479         if (reverse) {
480                 f = flist[1];
481                 flist[1] = flist[2];
482                 flist[2] = f;
483         }
484
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;
489                 }
490         }
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;
497                 }
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;
513                 }
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;
524                 }
525                 outfile = fdopen(fd, "w");
526         }
527
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);
546         }
547
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);
553
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");
568
569         if (outfilename)
570                 fclose(outfile);
571         else if (replace) {
572                 struct stat statbuf;
573
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;
581                 }
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;
589                 }
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;
601                 }
602         }
603         free(replacename);
604         free(orignew);
605         if (show_wiggles)
606                 return ci.conflicts + ci.wiggles > 0;
607         else
608                 return ci.conflicts > 0;
609 }
610
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)
615 {
616         FILE *f;
617         char *filename;
618         struct plist *pl;
619         int num_patches;
620         int rv = 0;
621         int i;
622
623         if (!replace) {
624                 fprintf(stderr,
625                         "%s: -p in merge mode requires -r\n",
626                         wiggle_Cmd);
627                 return 2;
628         }
629         if (argc != 1) {
630                 fprintf(stderr,
631                         "%s: -p in merge mode requires exactly one file\n",
632                         wiggle_Cmd);
633                 return 2;
634         }
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;
641         }
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;
648         }
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);
658         }
659         return rv;
660 }
661
662 int main(int argc, char *argv[])
663 {
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;
684
685         trace = getenv("WIGGLE_TRACE");
686         if (trace && *trace)
687                 wiggle_do_trace = 1;
688
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;
708                         }
709                         fputs(helpmsg, stderr);
710                         exit(0);
711
712                 case 'V':
713                         fputs(Version, stderr);
714                         exit(0);
715                 case ':':
716                 case '?':
717                 default:
718                         fputs(Usage, stderr);
719                         exit(2);
720
721                 case 'B':
722                 case 'x':
723                 case 'd':
724                 case 'm':
725                         if (mode == 0) {
726                                 mode = opt;
727                                 continue;
728                         }
729                         if (mode == 'B' && opt == 'd') {
730                                 /* Browse/diff mode */
731                                 ispatch = 2;
732                                 continue;
733                         }
734                         fprintf(stderr,
735                                 "%s: mode is '%c' - cannot set to '%c'\n",
736                                 wiggle_Cmd, mode, opt);
737                         exit(2);
738
739                 case NON_SPACE:
740                         ignore_blanks |= WholeWord;
741                         continue;
742
743                 case SHORTEST:
744                         shortest = 1;
745                         continue;
746
747                 case 'w':
748                 case 'l':
749                         if (obj == 0 || obj == opt) {
750                                 obj = opt;
751                                 continue;
752                         }
753                         fprintf(stderr,
754                                 "%s: cannot select both words and lines.\n", wiggle_Cmd);
755                         exit(2);
756
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;
770
771                 case 'b':
772                         ignore_blanks |= IgnoreBlanks;
773                         continue;
774
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;
785
786                 case '1':
787                 case '2':
788                 case '3':
789                         if (which == 0 || which == opt) {
790                                 which = opt;
791                                 continue;
792                         }
793                         fprintf(stderr,
794                                 "%s: can only select one of -1, -2, -3\n", wiggle_Cmd);
795                         exit(2);
796
797                 case 'p': /* 'patch' or 'strip' */
798                         if (optarg)
799                                 strip = atol(optarg);
800                         ispatch = 1;
801                         continue;
802
803                 case 'v':
804                         verbose++;
805                         continue;
806                 case 'q':
807                         quiet = 1;
808                         continue;
809
810                 case SELF_TEST:
811                         selftest = 1;
812                         continue;
813                 }
814         if (!mode)
815                 mode = 'm';
816
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);
823         }
824
825         if (obj && mode == 'x') {
826                 fprintf(stderr,
827                         "%s: cannot specify --line or --word with --extract\n",
828                         wiggle_Cmd);
829                 exit(2);
830         }
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);
837         }
838         if (replace && mode != 'm') {
839                 fprintf(stderr,
840                         "%s: --replace or --output only allowed with --merge\n", wiggle_Cmd);
841                 exit(2);
842         }
843         if (mode == 'x' && !which) {
844                 fprintf(stderr,
845                         "%s: must specify -1, -2 or -3 with --extract\n", wiggle_Cmd);
846                 exit(2);
847         }
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);
853         }
854
855         if (ispatch && which == '3') {
856                 fprintf(stderr,
857                         "%s: cannot extract -3 from a patch.\n", wiggle_Cmd);
858                 exit(2);
859         }
860
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;
889         }
890         exit(exit_status);
891 }