2 * wiggle - apply rejected patches
4 * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
5 * Copyright (C) 2010-2013 Neil Brown <neilb@suse.de>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program.
22 * Email: <neilb@suse.de>
26 * Wiggle is a tool for working with patches that don't quite apply properly.
27 * It provides functionality similar to 'diff' and 'merge' but can
28 * work at the level of individual words thus allowing the merging of
29 * two changes that affect the same line, but not the same parts of that line.
31 * Wiggle can also read patch and merge files. Unlike 'merge' it does not
32 * need to be given three separate files, but can be given a file and a patch
33 * and it will extract the pieces of the two other files that it needs from
36 * Wiggle performs one of three core function:
37 * --extract -x extract part of a patch or merge file
38 * --diff -d report differences between two files
39 * --merge -m merge the changes between two files into a third file
41 * This is also a --browse (-B) mode which provides interactive access
44 * To perform these, wiggle requires 1, 2, or 3 input streams respectively.
45 * I can get these from individual files, from a diff (unified or context) or
49 * If one file is given, it is a merge file (output of 'merge').
50 * If two files are given, the second is assumed to be a patch,
51 * the first is a normal file.
52 * If three files are given, they are taken to be normal files.
55 * If one file is given, it is a patch
56 * If two files are given, they are normal files.
59 * Only one file can be given. -p indicates it is a patch,
60 * otherwise it is a merge.
61 * One of the flags -1 -2 or -3 must also be given and they indicate which
62 * part of the patch or merge to extract.
64 * Difference calculation and merging is performed on lines (-l) or words (-w).
65 * Each 'word' is either 1/all alphnumeric (or '_'), 2/ all space or tab,
66 * or 3/ any other single character.
68 * In the case of -w, an initial diff is computed based on non-trivial words
69 * which includes alhpanumeric words and newlines.
71 * This diff is computed from the ends of the file and is used to find
72 * a suitable starting point and range. Then a more precise diff is
73 * computed over that restricted range
75 * Other options available are:
76 * --replace -r replace first file with result of merge.
77 * --help -h provide help
78 * --version -v version
80 * Defaults are --merge --words
96 void die(char *reason
)
98 fprintf(stderr
, "%s: fatal error: %s failure\n", Cmd
, reason
);
102 void check_dir(char *name
, int fd
)
105 if (fstat(fd
, &st
) != 0) {
106 fprintf(stderr
, "%s: fatal: %s is strange\n", Cmd
, name
);
109 if (S_ISDIR(st
.st_mode
)) {
110 fprintf(stderr
, "%s: %s is a directory\n", Cmd
, name
);
115 void *xmalloc(int size
)
117 void *rv
= malloc(size
);
119 char *msg
= "Failed to allocate memory - aborting\n";
120 write(2, msg
, strlen(msg
));
126 void printword(FILE *f
, struct elmnt e
)
129 fprintf(f
, "%.*s", e
.plen
+ e
.prefix
,
133 sscanf(e
.start
+1, "%d %d %d", &a
, &b
, &c
);
134 fprintf(f
, "*** %d,%d **** %d%s", b
, c
, a
, e
.start
+18);
138 static void printsep(struct elmnt e1
, struct elmnt e2
)
140 int a
, b
, c
, d
, e
, f
;
141 sscanf(e1
.start
+1, "%d %d %d", &a
, &b
, &c
);
142 sscanf(e2
.start
+1, "%d %d %d", &d
, &e
, &f
);
143 printf("@@ -%d,%d +%d,%d @@%s", b
, c
, e
, f
, e1
.start
+18);
146 static int extract(int argc
, char *argv
[], int ispatch
, int which
)
148 /* extract a branch of a diff or diff3 or merge output
151 struct stream f
, flist
[3];
155 "%s: no file given for --extract\n", Cmd
);
160 "%s: only give one file for --extract\n", Cmd
);
163 f
= load_file(argv
[0]);
164 if (f
.body
== NULL
) {
166 "%s: cannot load file '%s' - %s\n", Cmd
,
167 argv
[0], strerror(errno
));
171 if (split_patch(f
, &flist
[0], &flist
[1]) == 0) {
173 "%s: No chunk found in patch: %s\n", Cmd
,
178 if (!split_merge(f
, &flist
[0], &flist
[1], &flist
[2])) {
180 "%s: merge file %s looks bad.\n", Cmd
,
185 if (flist
[which
-'1'].body
== NULL
) {
187 "%s: %s has no -%c component.\n", Cmd
,
191 if (write(1, flist
[which
-'1'].body
,
192 flist
[which
-'1'].len
)
193 != flist
[which
-'1'].len
)
199 static int do_diff_lines(struct file fl
[2], struct csl
*csl
)
204 while (a
< fl
[0].elcnt
|| b
< fl
[1].elcnt
) {
206 if (fl
[0].list
[a
].start
[0]) {
213 } else if (b
< csl
->b
) {
214 if (fl
[1].list
[b
].start
[0]) {
222 if (fl
[0].list
[a
].start
[0] == '\0')
223 printsep(fl
[0].list
[a
],
232 if (a
>= csl
->a
+csl
->len
)
239 static int do_diff_words(struct file fl
[2], struct csl
*csl
)
243 int sol
= 1; /* start of line */
245 while (a
< fl
[0].elcnt
|| b
< fl
[1].elcnt
) {
255 for (a1
= a
; a1
< csl
->a
; a1
++)
256 if (ends_line(fl
[0].list
[a1
])) {
262 for (; a
< csl
->a
; a
++) {
263 printword(stdout
, fl
[0].list
[a
]);
264 if (ends_line(fl
[0].list
[a
])) {
277 printword(stdout
, fl
[0].list
[a
]);
278 sol
= ends_line(fl
[0].list
[a
]);
280 } while (a
< csl
->a
);
281 printf("%s-->>>", sol
? "|" : "");
284 } else if (b
< csl
->b
) {
289 for (b1
= b
; b1
< csl
->b
; b1
++)
290 if (ends_line(fl
[1].list
[b1
])) {
296 for (; b
< csl
->b
; b
++) {
297 printword(stdout
, fl
[1].list
[b
]);
298 if (ends_line(fl
[1].list
[b
])) {
311 printword(stdout
, fl
[1].list
[b
]);
312 sol
= ends_line(fl
[1].list
[b
]);
314 } while (b
< csl
->b
);
315 printf("%s++>>>", sol
? "|" : "");
322 for (a1
= a
; a1
< csl
->a
+csl
->len
; a1
++)
323 if (ends_line(fl
[0].list
[a1
]))
326 if (fl
[0].list
[a
].start
[0]) {
328 for (; a
< csl
->a
+csl
->len
; a
++, b
++) {
329 printword(stdout
, fl
[0].list
[a
]);
330 if (ends_line(fl
[0].list
[a
])) {
336 printsep(fl
[0].list
[a
], fl
[1].list
[b
]);
343 printword(stdout
, fl
[0].list
[a
]);
344 if (ends_line(fl
[0].list
[a
]))
349 if (a
>= csl
->a
+csl
->len
)
356 static int do_diff(int argc
, char *argv
[], int obj
, int ispatch
,
357 int which
, int reverse
, int shortest
)
359 /* create a diff (line or char) of two streams */
360 struct stream f
, flist
[3];
361 int chunks1
= 0, chunks2
= 0, chunks3
= 0;
368 fprintf(stderr
, "%s: no file given for --diff\n", Cmd
);
371 f
= load_file(argv
[0]);
372 if (f
.body
== NULL
) {
374 "%s: cannot load file '%s' - %s\n", Cmd
,
375 argv
[0], strerror(errno
));
379 split_patch(f
, &flist
[0], &flist
[1]);
380 if (!flist
[0].body
|| !flist
[1].body
) {
382 "%s: couldn't parse patch %s\n", Cmd
,
388 flist
[0] = load_file(argv
[0]);
389 if (flist
[0].body
== NULL
) {
391 "%s: cannot load file '%s' - %s\n", Cmd
,
392 argv
[0], strerror(errno
));
396 f
= load_file(argv
[1]);
397 if (f
.body
== NULL
) {
399 "%s: cannot load patch '%s' - %s\n", Cmd
,
400 argv
[1], strerror(errno
));
405 split_patch(f
, &flist
[2],
409 split_patch(f
, &flist
[1],
413 flist
[1] = load_file(argv
[1]);
414 if (flist
[1].body
== NULL
) {
416 "%s: cannot load file '%s' - %s\n", Cmd
,
417 argv
[1], strerror(errno
));
423 "%s: too many files given for --diff\n", Cmd
);
431 fl
[0] = split_stream(flist
[0], obj
);
432 fl
[1] = split_stream(flist
[1], obj
);
433 if (!(obj
& WholeWord
) && fl
[0].elcnt
> 50000 && fl
[1].elcnt
> 50000) {
434 /* Too big - use fewer words if possible */
438 fl
[0] = split_stream(flist
[0], obj
);
439 fl
[1] = split_stream(flist
[1], obj
);
441 if (chunks2
&& !chunks1
)
442 csl
= pdiff(fl
[0], fl
[1], chunks2
);
444 csl
= diff_patch(fl
[0], fl
[1], shortest
);
445 if ((obj
& ByMask
) == ByLine
) {
447 printf("@@ -1,%d +1,%d @@\n",
448 fl
[0].elcnt
, fl
[1].elcnt
);
449 exit_status
= do_diff_lines(fl
, csl
);
452 /* count lines in each file */
455 for (i
= 0 ; i
< fl
[0].elcnt
; i
++)
456 if (ends_line(fl
[0].list
[i
]))
458 for (i
= 0 ; i
< fl
[1].elcnt
; i
++)
459 if (ends_line(fl
[1].list
[i
]))
461 printf("@@ -1,%d +1,%d @@\n", l1
, l2
);
463 exit_status
= do_diff_words(fl
, csl
);
468 static int do_merge(int argc
, char *argv
[], int obj
, int blanks
,
469 int reverse
, int replace
, char *outfilename
,
470 int ignore
, int show_wiggles
,
471 int quiet
, int shortest
)
473 /* merge three files, A B C, so changed between B and C get made to A
475 struct stream f
, flist
[3];
478 int chunks1
= 0, chunks2
= 0, chunks3
= 0;
479 char *replacename
= NULL
, *orignew
= NULL
;
480 struct csl
*csl1
, *csl2
;
482 FILE *outfile
= stdout
;
486 fprintf(stderr
, "%s: no files given for --merge\n", Cmd
);
491 for (i
= 0; i
< argc
; i
++) {
492 flist
[i
] = load_file(argv
[i
]);
493 if (flist
[i
].body
== NULL
) {
494 fprintf(stderr
, "%s: cannot load file '%s' - %s\n",
496 argv
[i
], strerror(errno
));
502 fprintf(stderr
, "%s: too many files given for --merge\n",
507 case 1: /* a merge file */
509 if (!split_merge(f
, &flist
[0], &flist
[1], &flist
[2])) {
510 fprintf(stderr
, "%s: merge file %s looks bad.\n",
516 case 2: /* a file and a patch */
518 chunks2
= chunks3
= split_patch(f
, &flist
[1], &flist
[2]);
520 case 3: /* three separate files */
529 for (i
= 0; i
< 3; i
++) {
530 if (flist
[i
].body
== NULL
) {
531 fprintf(stderr
, "%s: file %d missing\n", Cmd
, i
);
536 outfile
= fopen(outfilename
, "w");
538 fprintf(stderr
, "%s: could not create %s\n",
542 } else if (replace
) {
544 replacename
= xmalloc(strlen(argv
[0]) + 20);
545 orignew
= xmalloc(strlen(argv
[0]) + 20);
546 strcpy(replacename
, argv
[0]);
547 strcpy(orignew
, argv
[0]);
548 strcat(orignew
, ".porig");
549 if (open(orignew
, O_RDONLY
) >= 0 ||
551 fprintf(stderr
, "%s: %s already exists\n",
558 strcat(replacename
, "XXXXXX");
559 fd
= mkstemp(replacename
);
562 "%s: could not create temporary file for %s\n",
569 outfile
= fdopen(fd
, "w");
576 fl
[0] = split_stream(flist
[0], blanks
);
577 fl
[1] = split_stream(flist
[1], blanks
);
578 fl
[2] = split_stream(flist
[2], blanks
);
579 if (!(blanks
& WholeWord
) &&
580 fl
[1].elcnt
> 50000 &&
581 (fl
[0].elcnt
> 50000 || fl
[2].elcnt
> 50000)) {
587 fl
[0] = split_stream(flist
[0], blanks
);
588 fl
[1] = split_stream(flist
[1], blanks
);
589 fl
[2] = split_stream(flist
[2], blanks
);
592 if (chunks2
&& !chunks1
)
593 csl1
= pdiff(fl
[0], fl
[1], chunks2
);
595 csl1
= diff(fl
[0], fl
[1], shortest
);
596 csl2
= diff_patch(fl
[1], fl
[2], shortest
);
598 ci
= make_merger(fl
[0], fl
[1], fl
[2], csl1
, csl2
,
599 obj
== 'w', ignore
, show_wiggles
> 1);
600 print_merge(outfile
, &fl
[0], &fl
[1], &fl
[2],
601 obj
== 'w', ci
.merger
, NULL
, 0, 0);
602 if (!quiet
&& ci
.conflicts
)
604 "%d unresolved conflict%s found\n",
606 ci
.conflicts
== 1 ? "" : "s");
607 if (!quiet
&& ci
.ignored
)
609 "%d already-applied change%s ignored\n",
611 ci
.ignored
== 1 ? "" : "s");
618 if (stat(argv
[0], &statbuf
) != 0) {
620 "%s: failed to stat original file. - %s\n",
621 Cmd
, strerror(errno
));
626 if (fchmod(fileno(outfile
), statbuf
.st_mode
) != 0) {
628 "%s: failed to change permission of new file. - %s\n",
629 Cmd
, strerror(errno
));
635 if (rename(argv
[0], orignew
) == 0 &&
636 rename(replacename
, argv
[0]) == 0)
640 "%s: failed to move new file into place.\n",
650 return ci
.conflicts
+ ci
.wiggles
> 0;
652 return ci
.conflicts
> 0;
655 static int multi_merge(int argc
, char *argv
[], int obj
, int blanks
,
656 int reverse
, int ignore
, int show_wiggles
,
657 int replace
, int strip
,
658 int quiet
, int shortest
)
669 "%s: -p in merge mode requires -r\n",
675 "%s: -p in merge mode requires exactly one file\n",
680 f
= fopen(filename
, "r");
682 fprintf(stderr
, "%s: cannot open %s\n",
686 check_dir(filename
, fileno(f
));
687 pl
= parse_patch(f
, NULL
, &num_patches
);
689 if (set_prefix(pl
, num_patches
, strip
) == 0) {
690 fprintf(stderr
, "%s: aborting\n", Cmd
);
693 for (i
= 0; i
< num_patches
; i
++) {
696 asprintf(&name
, "_wiggle_:%d:%d:%s",
697 pl
[i
].start
, pl
[i
].end
, filename
);
700 rv
|= do_merge(2, av
, obj
, blanks
, reverse
, 1, NULL
, ignore
,
701 show_wiggles
, quiet
, shortest
);
706 int main(int argc
, char *argv
[])
717 int verbose
= 0, quiet
= 0;
722 int show_wiggles
= 0;
725 char *outfile
= NULL
;
727 int ignore_blanks
= 0;
729 trace
= getenv("WIGGLE_TRACE");
733 while ((opt
= getopt_long(argc
, argv
,
734 short_options
, long_options
,
735 &option_index
)) != -1)
741 helpmsg
= HelpExtract
;
750 helpmsg
= HelpBrowse
;
753 fputs(helpmsg
, stderr
);
757 fputs(Version
, stderr
);
762 fputs(Usage
, stderr
);
773 if (mode
== 'B' && opt
== 'd') {
774 /* Browse/diff mode */
779 "%s: mode is '%c' - cannot set to '%c'\n",
784 ignore_blanks
|= WholeWord
;
793 if (obj
== 0 || obj
== opt
) {
798 "%s: cannot select both words and lines.\n", Cmd
);
816 ignore_blanks
|= IgnoreBlanks
;
833 if (which
== 0 || which
== opt
) {
838 "%s: can only select one of -1, -2, -3\n", Cmd
);
841 case 'p': /* 'patch' or 'strip' */
843 strip
= atol(optarg
);
862 vpatch(argc
-optind
, argv
+optind
, ispatch
,
863 strip
, reverse
, replace
, outfile
, selftest
,
864 ignore_blanks
, backup
);
865 /* should not return */
869 if (obj
&& mode
== 'x') {
871 "%s: cannot specify --line or --word with --extract\n",
875 if (mode
!= 'm' && !obj
)
877 if (ispatch
&& outfile
) {
878 fprintf(stderr
, "%s: --output incompatible with --patch\n",
882 if (replace
&& mode
!= 'm') {
884 "%s: --replace or --output only allowed with --merge\n", Cmd
);
887 if (mode
== 'x' && !which
) {
889 "%s: must specify -1, -2 or -3 with --extract\n", Cmd
);
892 if (mode
!= 'x' && mode
!= 'd' && which
) {
894 "%s: -1, -2 or -3 only allowed with --extract or --diff\n",
899 if (ispatch
&& which
== '3') {
901 "%s: cannot extract -3 from a patch.\n", Cmd
);
907 exit_status
= extract(argc
-optind
, argv
+optind
, ispatch
, which
);
910 exit_status
= do_diff(argc
-optind
, argv
+optind
,
911 (obj
== 'l' ? ByLine
: ByWord
)
913 ispatch
, which
, reverse
, shortest
);
917 exit_status
= multi_merge(argc
-optind
,
925 exit_status
= do_merge(
926 argc
-optind
, argv
+optind
,
927 obj
, ignore_blanks
, reverse
, replace
,
929 ignore
, show_wiggles
, quiet
, shortest
);