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
93 static void printsep(struct elmnt e1
, struct elmnt e2
)
96 sscanf(e1
.start
+1, "%d %d %d", &a
, &b
, &c
);
97 sscanf(e2
.start
+1, "%d %d %d", &d
, &e
, &f
);
98 printf("@@ -%d,%d +%d,%d @@%s", b
, c
, e
, f
, e1
.start
+18);
101 static int extract(int argc
, char *argv
[], int ispatch
, int which
)
103 /* extract a branch of a diff or diff3 or merge output
106 struct stream f
, flist
[3];
110 "%s: no file given for --extract\n", wiggle_Cmd
);
115 "%s: only give one file for --extract\n", wiggle_Cmd
);
118 f
= wiggle_load_file(argv
[0]);
119 if (f
.body
== NULL
) {
121 "%s: cannot load file '%s' - %s\n", wiggle_Cmd
,
122 argv
[0], strerror(errno
));
126 if (wiggle_split_patch(f
, &flist
[0], &flist
[1]) == 0) {
128 "%s: No chunk found in patch: %s\n", wiggle_Cmd
,
133 if (!wiggle_split_merge(f
, &flist
[0], &flist
[1], &flist
[2])) {
135 "%s: merge file %s looks bad.\n", wiggle_Cmd
,
140 if (flist
[which
-'1'].body
== NULL
) {
142 "%s: %s has no -%c component.\n", wiggle_Cmd
,
146 if (write(1, flist
[which
-'1'].body
,
147 flist
[which
-'1'].len
)
148 != flist
[which
-'1'].len
)
154 static int do_diff_lines(struct file fl
[2], struct csl
*csl
)
159 while (a
< fl
[0].elcnt
|| b
< fl
[1].elcnt
) {
161 if (fl
[0].list
[a
].start
[0]) {
163 wiggle_printword(stdout
,
168 } else if (b
< csl
->b
) {
169 if (fl
[1].list
[b
].start
[0]) {
171 wiggle_printword(stdout
,
177 if (fl
[0].list
[a
].start
[0] == '\0')
178 printsep(fl
[0].list
[a
],
182 wiggle_printword(stdout
,
187 if (a
>= csl
->a
+csl
->len
)
194 static int do_diff_words(struct file fl
[2], struct csl
*csl
)
198 int sol
= 1; /* start of line */
200 while (a
< fl
[0].elcnt
|| b
< fl
[1].elcnt
) {
210 for (a1
= a
; a1
< csl
->a
; a1
++)
211 if (ends_line(fl
[0].list
[a1
])) {
217 for (; a
< csl
->a
; a
++) {
218 wiggle_printword(stdout
, fl
[0].list
[a
]);
219 if (ends_line(fl
[0].list
[a
])) {
232 wiggle_printword(stdout
, fl
[0].list
[a
]);
233 sol
= ends_line(fl
[0].list
[a
]);
235 } while (a
< csl
->a
);
236 printf("%s-->>>", sol
? "|" : "");
239 } else if (b
< csl
->b
) {
244 for (b1
= b
; b1
< csl
->b
; b1
++)
245 if (ends_line(fl
[1].list
[b1
])) {
251 for (; b
< csl
->b
; b
++) {
252 wiggle_printword(stdout
, fl
[1].list
[b
]);
253 if (ends_line(fl
[1].list
[b
])) {
266 wiggle_printword(stdout
, fl
[1].list
[b
]);
267 sol
= ends_line(fl
[1].list
[b
]);
269 } while (b
< csl
->b
);
270 printf("%s++>>>", sol
? "|" : "");
277 for (a1
= a
; a1
< csl
->a
+csl
->len
; a1
++)
278 if (ends_line(fl
[0].list
[a1
]))
281 if (fl
[0].list
[a
].start
[0]) {
283 for (; a
< csl
->a
+csl
->len
; a
++, b
++) {
284 wiggle_printword(stdout
, fl
[0].list
[a
]);
285 if (ends_line(fl
[0].list
[a
])) {
291 printsep(fl
[0].list
[a
], fl
[1].list
[b
]);
298 wiggle_printword(stdout
, fl
[0].list
[a
]);
299 if (ends_line(fl
[0].list
[a
]))
304 if (a
>= csl
->a
+csl
->len
)
311 static int do_diff(int argc
, char *argv
[], int obj
, int ispatch
,
312 int which
, int reverse
, int shortest
)
314 /* create a diff (line or char) of two streams */
315 struct stream f
, flist
[3];
316 int chunks1
= 0, chunks2
= 0, chunks3
= 0;
323 fprintf(stderr
, "%s: no file given for --diff\n", wiggle_Cmd
);
326 f
= wiggle_load_file(argv
[0]);
327 if (f
.body
== NULL
) {
329 "%s: cannot load file '%s' - %s\n", wiggle_Cmd
,
330 argv
[0], strerror(errno
));
334 wiggle_split_patch(f
, &flist
[0], &flist
[1]);
335 if (!flist
[0].body
|| !flist
[1].body
) {
337 "%s: couldn't parse patch %s\n", wiggle_Cmd
,
343 flist
[0] = wiggle_load_file(argv
[0]);
344 if (flist
[0].body
== NULL
) {
346 "%s: cannot load file '%s' - %s\n", wiggle_Cmd
,
347 argv
[0], strerror(errno
));
351 f
= wiggle_load_file(argv
[1]);
352 if (f
.body
== NULL
) {
354 "%s: cannot load patch '%s' - %s\n", wiggle_Cmd
,
355 argv
[1], strerror(errno
));
360 wiggle_split_patch(f
, &flist
[2],
364 wiggle_split_patch(f
, &flist
[1],
368 flist
[1] = wiggle_load_file(argv
[1]);
369 if (flist
[1].body
== NULL
) {
371 "%s: cannot load file '%s' - %s\n", wiggle_Cmd
,
372 argv
[1], strerror(errno
));
378 "%s: too many files given for --diff\n", wiggle_Cmd
);
386 fl
[0] = wiggle_split_stream(flist
[0], obj
);
387 fl
[1] = wiggle_split_stream(flist
[1], obj
);
388 if (!(obj
& WholeWord
) && fl
[0].elcnt
> 50000 && fl
[1].elcnt
> 50000) {
389 /* Too big - use fewer words if possible */
393 fl
[0] = wiggle_split_stream(flist
[0], obj
);
394 fl
[1] = wiggle_split_stream(flist
[1], obj
);
396 if (chunks2
&& !chunks1
)
397 csl
= wiggle_pdiff(fl
[0], fl
[1], chunks2
);
399 csl
= wiggle_diff_patch(fl
[0], fl
[1], shortest
);
400 if ((obj
& ByMask
) == ByLine
) {
402 printf("@@ -1,%d +1,%d @@\n",
403 fl
[0].elcnt
, fl
[1].elcnt
);
404 exit_status
= do_diff_lines(fl
, csl
);
407 /* count lines in each file */
410 for (i
= 0 ; i
< fl
[0].elcnt
; i
++)
411 if (ends_line(fl
[0].list
[i
]))
413 for (i
= 0 ; i
< fl
[1].elcnt
; i
++)
414 if (ends_line(fl
[1].list
[i
]))
416 printf("@@ -1,%d +1,%d @@\n", l1
, l2
);
418 exit_status
= do_diff_words(fl
, csl
);
423 static int do_merge(int argc
, char *argv
[], int obj
, int blanks
,
424 int reverse
, int replace
, char *outfilename
,
425 int ignore
, int show_wiggles
,
426 int quiet
, int shortest
)
428 /* merge three files, A B C, so changed between B and C get made to A
430 struct stream f
, flist
[3];
433 int chunks1
= 0, chunks2
= 0, chunks3
= 0;
434 char *replacename
= NULL
, *orignew
= NULL
;
435 struct csl
*csl1
, *csl2
;
437 FILE *outfile
= stdout
;
441 fprintf(stderr
, "%s: no files given for --merge\n", wiggle_Cmd
);
446 for (i
= 0; i
< argc
; i
++) {
447 flist
[i
] = wiggle_load_file(argv
[i
]);
448 if (flist
[i
].body
== NULL
) {
449 fprintf(stderr
, "%s: cannot load file '%s' - %s\n",
451 argv
[i
], strerror(errno
));
457 fprintf(stderr
, "%s: too many files given for --merge\n",
462 case 1: /* a merge file */
464 if (!wiggle_split_merge(f
, &flist
[0], &flist
[1], &flist
[2])) {
465 fprintf(stderr
, "%s: merge file %s looks bad.\n",
471 case 2: /* a file and a patch */
473 chunks2
= chunks3
= wiggle_split_patch(f
, &flist
[1], &flist
[2]);
475 case 3: /* three separate files */
484 for (i
= 0; i
< 3; i
++) {
485 if (flist
[i
].body
== NULL
) {
486 fprintf(stderr
, "%s: file %d missing\n", wiggle_Cmd
, i
);
491 outfile
= fopen(outfilename
, "w");
493 fprintf(stderr
, "%s: could not create %s\n",
494 wiggle_Cmd
, outfilename
);
497 } else if (replace
) {
499 replacename
= wiggle_xmalloc(strlen(argv
[0]) + 20);
500 orignew
= wiggle_xmalloc(strlen(argv
[0]) + 20);
501 strcpy(replacename
, argv
[0]);
502 strcpy(orignew
, argv
[0]);
503 strcat(orignew
, ".porig");
504 if (open(orignew
, O_RDONLY
) >= 0 ||
506 fprintf(stderr
, "%s: %s already exists\n",
513 strcat(replacename
, "XXXXXX");
514 fd
= mkstemp(replacename
);
517 "%s: could not create temporary file for %s\n",
524 outfile
= fdopen(fd
, "w");
531 fl
[0] = wiggle_split_stream(flist
[0], blanks
);
532 fl
[1] = wiggle_split_stream(flist
[1], blanks
);
533 fl
[2] = wiggle_split_stream(flist
[2], blanks
);
534 if (!(blanks
& WholeWord
) &&
535 fl
[1].elcnt
> 50000 &&
536 (fl
[0].elcnt
> 50000 || fl
[2].elcnt
> 50000)) {
542 fl
[0] = wiggle_split_stream(flist
[0], blanks
);
543 fl
[1] = wiggle_split_stream(flist
[1], blanks
);
544 fl
[2] = wiggle_split_stream(flist
[2], blanks
);
547 if (chunks2
&& !chunks1
)
548 csl1
= wiggle_pdiff(fl
[0], fl
[1], chunks2
);
550 csl1
= wiggle_diff(fl
[0], fl
[1], shortest
);
551 csl2
= wiggle_diff_patch(fl
[1], fl
[2], shortest
);
553 ci
= wiggle_make_merger(fl
[0], fl
[1], fl
[2], csl1
, csl2
,
554 obj
== 'w', ignore
, show_wiggles
> 1);
555 wiggle_print_merge(outfile
, &fl
[0], &fl
[1], &fl
[2],
556 obj
== 'w', ci
.merger
, NULL
, 0, 0);
557 if (!quiet
&& ci
.conflicts
)
559 "%d unresolved conflict%s found\n",
561 ci
.conflicts
== 1 ? "" : "s");
562 if (!quiet
&& ci
.ignored
)
564 "%d already-applied change%s ignored\n",
566 ci
.ignored
== 1 ? "" : "s");
573 if (stat(argv
[0], &statbuf
) != 0) {
575 "%s: failed to stat original file. - %s\n",
576 wiggle_Cmd
, strerror(errno
));
581 if (fchmod(fileno(outfile
), statbuf
.st_mode
) != 0) {
583 "%s: failed to change permission of new file. - %s\n",
584 wiggle_Cmd
, strerror(errno
));
590 if (rename(argv
[0], orignew
) == 0 &&
591 rename(replacename
, argv
[0]) == 0)
595 "%s: failed to move new file into place.\n",
605 return ci
.conflicts
+ ci
.wiggles
> 0;
607 return ci
.conflicts
> 0;
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
, int shortest
)
624 "%s: -p in merge mode requires -r\n",
630 "%s: -p in merge mode requires exactly one file\n",
635 f
= fopen(filename
, "r");
637 fprintf(stderr
, "%s: cannot open %s\n",
638 wiggle_Cmd
, filename
);
641 wiggle_check_dir(filename
, fileno(f
));
642 pl
= wiggle_parse_patch(f
, NULL
, &num_patches
);
644 if (wiggle_set_prefix(pl
, num_patches
, strip
) == 0) {
645 fprintf(stderr
, "%s: aborting\n", wiggle_Cmd
);
648 for (i
= 0; i
< num_patches
; i
++) {
651 asprintf(&name
, "_wiggle_:%d:%d:%s",
652 pl
[i
].start
, pl
[i
].end
, filename
);
655 rv
|= do_merge(2, av
, obj
, blanks
, reverse
, 1, NULL
, ignore
,
656 show_wiggles
, quiet
, shortest
);
661 int main(int argc
, char *argv
[])
672 int verbose
= 0, quiet
= 0;
677 int show_wiggles
= 0;
680 char *outfile
= NULL
;
682 int ignore_blanks
= 0;
684 trace
= getenv("WIGGLE_TRACE");
688 while ((opt
= getopt_long(argc
, argv
,
689 short_options
, long_options
,
690 &option_index
)) != -1)
696 helpmsg
= HelpExtract
;
705 helpmsg
= HelpBrowse
;
708 fputs(helpmsg
, stderr
);
712 fputs(Version
, stderr
);
717 fputs(Usage
, stderr
);
728 if (mode
== 'B' && opt
== 'd') {
729 /* Browse/diff mode */
734 "%s: mode is '%c' - cannot set to '%c'\n",
735 wiggle_Cmd
, mode
, opt
);
739 ignore_blanks
|= WholeWord
;
748 if (obj
== 0 || obj
== opt
) {
753 "%s: cannot select both words and lines.\n", wiggle_Cmd
);
771 ignore_blanks
|= IgnoreBlanks
;
788 if (which
== 0 || which
== opt
) {
793 "%s: can only select one of -1, -2, -3\n", wiggle_Cmd
);
796 case 'p': /* 'patch' or 'strip' */
798 strip
= atol(optarg
);
817 vpatch(argc
-optind
, argv
+optind
, ispatch
,
818 strip
, reverse
, replace
, outfile
, selftest
,
819 ignore_blanks
, backup
);
820 /* should not return */
824 if (obj
&& mode
== 'x') {
826 "%s: cannot specify --line or --word with --extract\n",
830 if (mode
!= 'm' && !obj
)
832 if (ispatch
&& outfile
) {
833 fprintf(stderr
, "%s: --output incompatible with --patch\n",
837 if (replace
&& mode
!= 'm') {
839 "%s: --replace or --output only allowed with --merge\n", wiggle_Cmd
);
842 if (mode
== 'x' && !which
) {
844 "%s: must specify -1, -2 or -3 with --extract\n", wiggle_Cmd
);
847 if (mode
!= 'x' && mode
!= 'd' && which
) {
849 "%s: -1, -2 or -3 only allowed with --extract or --diff\n",
854 if (ispatch
&& which
== '3') {
856 "%s: cannot extract -3 from a patch.\n", wiggle_Cmd
);
862 exit_status
= extract(argc
-optind
, argv
+optind
, ispatch
, which
);
865 exit_status
= do_diff(argc
-optind
, argv
+optind
,
866 (obj
== 'l' ? ByLine
: ByWord
)
868 ispatch
, which
, reverse
, shortest
);
872 exit_status
= multi_merge(argc
-optind
,
880 exit_status
= do_merge(
881 argc
-optind
, argv
+optind
,
882 obj
, ignore_blanks
, reverse
, replace
,
884 ignore
, show_wiggles
, quiet
, shortest
);