2 * wiggle - apply rejected patches
4 * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Email: <neilb@cse.unsw.edu.au>
24 * School of Computer Science and Engineering
25 * The University of New South Wales
31 * Wiggle is a tool for working with patches that don't quite apply properly.
32 * It provides functionality similar to 'diff' and 'merge' but can
33 * work at the level of individual words thus allowing the merging of
34 * two changes that affect the same line, but not the same parts of that line.
36 * Wiggle can also read patch and merge files. Unlike 'merge' it does not
37 * need to be given three separate files, but can be given a file and a patch
38 * and it will extract the pieces of the two two other files that it needs from
41 * Wiggle performs one of three core function:
42 * --extract -x extract part of a patch or merge file
43 * --diff -d report differences between two files
44 * --merge -m merge the changes between two files into a third file
46 * To perform these, wiggle requires 1, 2, or 3 input streams respectively.
47 * I can get there from individual files, from a diff (unified or context) or
51 * If one file is given, it is a merge file (output of 'merge').
52 * If two files are given, the second is assumed to be a patch, the first is a normal file.
53 * If three files are given, they are taken to be normal files.
56 * If one file is given, it is a patch
57 * If two files are given, they are normal files.
60 * Only one file can be given. -p indicates it is a patch, 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 calculate and merging is performed on lines (-l) or words (-w).
65 * In the case of -w, an initial diff is computed based on non-trivial words.
66 * i.e. spaces are ignored
67 * This diff is computed from the ends of the file and is used to find a suitable
68 * starting point and range. Then a more precise diff is computed over that
71 * Other options available are:
72 * --replace -r replace first file with result of merge.
73 * --help -h provide help
74 * --version -v version
76 * Defaults are --merge --words
92 fprintf(stderr
,"wiggle: fatal error\n");
97 void printword(FILE *f
, struct elmnt e
)
100 fprintf(f
, "%.*s", e
.len
, e
.start
);
103 sscanf(e
.start
+1, "%d %d %d", &a
, &b
, &c
);
104 fprintf(f
, "*** %d,%d **** %d\n", b
,c
,a
);
108 static void printsep(struct elmnt e1
, struct elmnt e2
)
111 sscanf(e1
.start
+1, "%d %d %d", &a
, &b
, &c
);
112 sscanf(e2
.start
+1, "%d %d %d", &d
, &e
, &f
);
113 printf("@@ -%d,%d +%d,%d @@\n", b
,c
,e
,f
);
117 /* Remove any entries from the common-sublist that are
118 * just spaces, tabs, or newlines
120 void cleanlist(struct file a
, struct file b
, struct csl
*list
)
122 struct csl
*new = list
;
127 for( ap
= list
->a
; ap
< list
->a
+list
->len
; ap
++) {
128 for (i
=0; i
<a
.list
[ap
].len
; i
++) {
129 char c
= a
.list
[ap
].start
[i
];
133 if (i
!= a
.list
[ap
].len
)
136 if (ap
== list
->a
+list
->len
)
144 int main(int argc
, char *argv
[])
151 char *replacename
=NULL
, *orignew
=NULL
;
155 int verbose
=0, quiet
=0;
158 int chunks1
=0, chunks2
=0, chunks3
=0;
160 FILE *outfile
= stdout
;
164 struct stream f
, flist
[3];
166 struct csl
*csl1
, *csl2
;
168 base0
= strrchr(argv
[0], '/');
169 if (base0
) base0
++; else base0
=argv
[0];
171 /* The name 'vpatch' seems to be used elsewhere */
172 if (strcmp(base0
, "vpatch")==0) {
177 while ((opt
= getopt_long(argc
, argv
,
178 short_options(mode
), long_options
,
179 &option_index
)) != -1)
184 case 'x': helpmsg
= HelpExtract
; break;
185 case 'd': helpmsg
= HelpDiff
; break;
186 case 'm': helpmsg
= HelpMerge
; break;
187 case 'B': helpmsg
= HelpBrowse
; break;
189 fputs(helpmsg
, stderr
);
193 fputs(Version
, stderr
);
198 fputs(Usage
, stderr
);
209 fprintf(stderr
, "wiggle: mode is '%c' - cannot set to '%c'\n",
215 if (obj
== 0 || obj
== opt
) {
219 fprintf(stderr
, "wiggle: cannot select both words and lines.\n");
232 if (which
== 0 || which
== opt
) {
236 fprintf(stderr
, "wiggle: can only select one of -1, -2, -3\n");
241 strip
= atol(optarg
?optarg
:"0");
243 fprintf(stderr
, "wiggle: SORRY, PARSE ERROR\n");
249 case 'v': verbose
++; continue;
250 case 'q': quiet
=1 ; continue;
256 vpatch(argc
-optind
, argv
+optind
, strip
, reverse
, replace
);
257 /* should not return */
261 if (obj
&& mode
== 'x') {
262 fprintf(stderr
,"wiggle: cannot specify --line or --word with --extract\n");
265 if (mode
!= 'm' && !obj
) obj
= 'w';
266 if (replace
&& mode
!= 'm') {
267 fprintf(stderr
, "wiggle: --replace only allowed with --merge\n");
270 if (mode
== 'x' && !which
) {
271 fprintf(stderr
, "wiggle: must specify -1, -2 or -3 with --extract\n");
274 if (mode
!= 'x' && mode
!= 'd' && which
) {
275 fprintf(stderr
, "wiggle: -1, -2 or -3 only allowed with --extract or --diff\n");
278 if (ispatch
&& (mode
!= 'x' && mode
!= 'd')) {
279 fprintf(stderr
, "wiggle: --patch only allowed with --extract or --diff\n");
282 if (ispatch
&& which
== '3') {
283 fprintf(stderr
, "wiggle: cannot extract -3 from a patch.\n");
289 /* extract a branch of a diff or diff3 or merge output
292 if (optind
== argc
) {
293 fprintf(stderr
, "wiggle: no file given for --extract\n");
296 if (optind
< argc
-1) {
297 fprintf(stderr
, "wiggle: only give one file for --extract\n");
300 f
= load_file(argv
[optind
]);
302 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
303 argv
[optind
], strerror(errno
));
307 chunks1
= chunks2
= split_patch(f
, &flist
[0], &flist
[1]);
309 if (!split_merge(f
, &flist
[0], &flist
[1], &flist
[2])) {
310 fprintf(stderr
, "wiggle: merge file %s looks bad.\n",
315 if (flist
[which
-'1'].body
== NULL
) {
316 fprintf(stderr
, "wiggle: %s has no -%c component.\n",
317 argv
[optind
], which
);
320 write(1, flist
[which
-'1'].body
, flist
[which
-'1'].len
);
325 /* create a diff (line or char) of two streams */
326 switch (argc
-optind
) {
328 fprintf(stderr
, "wiggle: no file given for --diff\n");
331 f
= load_file(argv
[optind
]);
332 if (f
.body
== NULL
) {
333 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
334 argv
[optind
], strerror(errno
));
337 chunks1
= chunks2
= split_patch(f
, &flist
[0], &flist
[1]);
338 if (!flist
[0].body
|| !flist
[1].body
) {
339 fprintf(stderr
, "wiggle: couldn't parse patch %s\n",
345 flist
[0] = load_file(argv
[optind
]);
346 if (flist
[0].body
== NULL
) {
347 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
348 argv
[optind
], strerror(errno
));
352 f
= load_file(argv
[optind
+1]);
353 if (f
.body
== NULL
) {
354 fprintf(stderr
, "wiggle: cannot load patch '%s' - %s\n",
355 argv
[optind
], strerror(errno
));
359 chunks2
= chunks3
= split_patch(f
, &flist
[2], &flist
[1]);
361 chunks2
= chunks3
= split_patch(f
, &flist
[1], &flist
[2]);
364 flist
[1] = load_file(argv
[optind
+1]);
365 if (flist
[1].body
== NULL
) {
366 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
367 argv
[optind
+1], strerror(errno
));
372 fprintf(stderr
, "wiggle: too many files given for --diff\n");
382 fl
[0] = split_stream(flist
[0], ByLine
, 0);
383 fl
[1] = split_stream(flist
[1], ByLine
, 0);
384 if (chunks2
&& ! chunks1
)
385 csl1
= pdiff(fl
[0], fl
[1], chunks2
);
387 csl1
= diff(fl
[0], fl
[1]);
390 printf("@@ -1,%d +1,%d @@\n", fl
[0].elcnt
, fl
[1].elcnt
);
392 while (a
<fl
[0].elcnt
|| b
< fl
[1].elcnt
) {
394 if (fl
[0].list
[a
].start
[0]) {
396 printword(stdout
, fl
[0].list
[a
]);
400 } else if (b
< csl1
->b
) {
401 if (fl
[1].list
[b
].start
[0]) {
403 printword(stdout
, fl
[1].list
[b
]);
408 if (fl
[0].list
[a
].start
[0] == '\0')
409 printsep(fl
[0].list
[a
], fl
[1].list
[b
]);
412 printword(stdout
, fl
[0].list
[a
]);
416 if (a
>= csl1
->a
+csl1
->len
)
422 int sol
= 1; /* start of line */
423 fl
[0] = split_stream(flist
[0], ByWord
, 0);
424 fl
[1] = split_stream(flist
[1], ByWord
, 0);
425 if (chunks2
&& !chunks1
)
426 csl1
= pdiff(fl
[0], fl
[1], chunks2
);
428 csl1
= diff(fl
[0], fl
[1]);
431 /* count lines in each file */
434 for (i
=0;i
<fl
[0].elcnt
;i
++)
435 if (ends_line(fl
[0].list
[i
]))
437 for (i
=0;i
<fl
[1].elcnt
;i
++)
438 if (ends_line(fl
[1].list
[i
]))
440 printf("@@ -1,%d +1,%d @@\n", l1
,l2
);
443 while (a
< fl
[0].elcnt
|| b
< fl
[1].elcnt
) {
448 /* If we remove a whole line, output +line
449 * else clear sol and retry */
451 for (a1
=a
; a1
<csl1
->a
;a1
++)
452 if (ends_line(fl
[0].list
[a1
])) {
458 for (; a
<csl1
->a
; a
++) {
459 printword(stdout
, fl
[0].list
[a
]);
460 if (ends_line(fl
[0].list
[a
])) {
470 if (sol
) printf("|");
471 printword(stdout
, fl
[0].list
[a
]);
472 sol
= ends_line(fl
[0].list
[a
]);
474 } while (a
< csl1
->a
);
475 printf("%s-->>>", sol
?"|":"");
478 } else if (b
< csl1
->b
) {
483 for (b1
=b
; b1
<csl1
->b
;b1
++)
484 if(ends_line(fl
[1].list
[b1
])) {
490 for(; b
<csl1
->b
; b
++) {
491 printword(stdout
, fl
[1].list
[b
]);
492 if(ends_line(fl
[1].list
[b
])) {
502 if (sol
) printf("|");
503 printword(stdout
, fl
[1].list
[b
]);
504 sol
= ends_line(fl
[1].list
[b
]);
506 } while (b
< csl1
->b
);
507 printf("%s++>>>",sol
?"|":"");
514 for (a1
=a
; a1
<csl1
->a
+csl1
->len
; a1
++)
515 if (ends_line(fl
[0].list
[a1
]))
518 if (fl
[0].list
[a
].start
[0]) {
520 for(; a
<csl1
->a
+csl1
->len
; a
++,b
++) {
521 printword(stdout
, fl
[0].list
[a
]);
522 if (ends_line(fl
[0].list
[a
])) {
528 printsep(fl
[0].list
[a
], fl
[1].list
[b
]);
535 printword(stdout
, fl
[0].list
[a
]);
536 if (ends_line(fl
[0].list
[a
]))
541 if (a
>= csl1
->a
+csl1
->len
)
549 /* merge three files, A B C, so changed between B and C get made to A
551 switch (argc
-optind
) {
553 fprintf(stderr
, "wiggle: no files given for --merge\n");
558 for (i
=0; i
< argc
-optind
; i
++) {
559 flist
[i
] = load_file(argv
[optind
+i
]);
560 if (flist
[i
].body
== NULL
) {
561 fprintf(stderr
, "wiggle: cannot load file '%s' - %s\n",
562 argv
[optind
+i
], strerror(errno
));
568 fprintf(stderr
, "wiggle: too many files given for --merge\n");
571 switch(argc
-optind
) {
572 case 1: /* a merge file */
574 if (!split_merge(f
, &flist
[0], &flist
[1], &flist
[2])) {
575 fprintf(stderr
,"wiggle: merge file %s looks bad.\n",
580 case 2: /* a file and a patch */
582 chunks2
= chunks3
= split_patch(f
, &flist
[1], &flist
[2]);
584 case 3: /* three separate files */
593 for (i
=0; i
<3; i
++) {
594 if (flist
[i
].body
==NULL
) {
595 fprintf(stderr
, "wiggle: file %d missing\n", i
);
601 replacename
= malloc(strlen(argv
[optind
])+ 20);
602 if (!replacename
) die();
603 orignew
= malloc(strlen(argv
[optind
])+20);
605 strcpy(replacename
, argv
[optind
]);
606 strcpy(orignew
, argv
[optind
]);
607 strcat(orignew
, ".porig");
608 if (open(orignew
, O_RDONLY
) >= 0 ||
610 fprintf(stderr
,"wiggle: %s already exists\n",
614 strcat(replacename
,"XXXXXX");
615 fd
= mkstemp(replacename
);
617 fprintf(stderr
,"wiggle: could not create temporary file for %s\n",
621 outfile
= fdopen(fd
, "w");
626 fl
[0] = split_stream(flist
[0], ByLine
, 0);
627 fl
[1] = split_stream(flist
[1], ByLine
, 0);
628 fl
[2] = split_stream(flist
[2], ByLine
, 0);
630 fl
[0] = split_stream(flist
[0], ByWord
, 0);
631 fl
[1] = split_stream(flist
[1], ByWord
, 0);
632 fl
[2] = split_stream(flist
[2], ByWord
, 0);
634 if (chunks2
&& !chunks1
)
635 csl1
= pdiff(fl
[0], fl
[1], chunks2
);
637 csl1
= diff(fl
[0], fl
[1]);
638 csl2
= diff(fl
[1], fl
[2]);
641 cleanlist(fl
[0],fl
[1],csl1
);
642 cleanlist(fl
[1],fl
[2],csl2
);
648 ci
= print_merge2(outfile
, &fl
[0], &fl
[1], &fl
[2],
649 csl1
, csl2
, obj
=='w');
650 if (!quiet
&& ci
.conflicts
)
651 fprintf(stderr
, "%d unresolved conflict%s found\n", ci
.conflicts
, ci
.conflicts
==1?"":"s");
652 if (!quiet
&& ci
.ignored
)
653 fprintf(stderr
, "%d already-applied change%s ignored\n", ci
.ignored
, ci
.ignored
==1?"":"s");
654 exit_status
= (ci
.conflicts
> 0);
658 if (rename(argv
[optind
], orignew
) ==0 &&
659 rename(replacename
, argv
[optind
]) ==0)
662 fprintf(stderr
, "wiggle: failed to move new file into place.\n");