2 * wiggle - apply rejected patches
4 * Copyright (C) 2005 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 * vpatch - visual front end for wiggle
33 * "files" display, lists all files with statistics
34 * - can hide various lines including subdirectories
35 * and files without wiggles or conflicts
36 * "diff" display shows merged file with different parts
37 * in different colours
38 * - untouched are pale A_DIM
39 * - matched/remaining are regular A_NORMAL
40 * - matched/removed are red/underlined A_UNDERLINE
41 * - unmatched in file are A_STANDOUT
42 * - unmatched in patch are A_STANDOUT|A_UNDERLINE ???
43 * - inserted are inverse/green ?? A_REVERSE
45 * The window can be split horiz or vert and two different
46 * views displayed. They will have different parts missing
48 * So a display of NORMAL, underline, standout|underline reverse
49 * should show a normal patch.
62 #define assert(x) do { if (!(x)) abort(); } while (0)
66 unsigned int start
, end
;
70 int chunks
, wiggles
, conflicts
;
74 struct plist
*patch_add_file(struct plist
*pl
, int *np
, char *file
,
75 unsigned int start
, unsigned int end
)
77 /* size of pl is 0, 16, n^2 */
81 while (*file
== '/') file
++; /* leading '/' are bad... */
83 /* printf("adding %s at %d: %u %u\n", file, n, start, end); */
85 else if (n
<=16) asize
= 16;
86 else if ((n
&(n
-1))==0) asize
= n
;
87 else asize
= n
+1; /* not accurate, but not too large */
89 /* need to extend array */
91 if (asize
< 16) asize
= 16;
93 npl
= realloc(pl
, asize
* sizeof(struct plist
));
95 fprintf(stderr
, "malloc failed - skipping %s\n", file
);
103 pl
[n
].last
= pl
[n
].next
= pl
[n
].prev
= pl
[n
].parent
= -1;
104 pl
[n
].chunks
= pl
[n
].wiggles
= 0; pl
[n
].conflicts
= 100;
113 struct plist
*parse_patch(FILE *f
, FILE *of
, int *np
)
115 /* read a multi-file patch from 'f' and record relevant
116 * details in a plist.
117 * if 'of' >= 0, fd might not be seekable so we write
118 * to 'of' and use lseek on 'of' to determine position
120 struct plist
*plist
= NULL
;
123 /* first, find the start of a patch: "\n+++ "
124 * grab the file name and scan to the end of a line
126 char *target
="\n+++ ";
127 char *target2
="\n--- ";
133 while (*pos
&& (c
=fgetc(f
)) != EOF
) {
134 if (of
) fputc(c
, of
);
142 /* now read a file name */
144 while ((c
=fgetc(f
)) != EOF
&& c
!= '\t' && c
!= '\n' && c
!= ' ' &&
147 if (of
) fputc(c
, of
);
152 if (of
) fputc(c
, of
);
153 while (c
!= '\n' && (c
=fgetc(f
)) != EOF
) {
154 if (of
) fputc(c
, of
);
156 start
= of
? ftell(of
) : ftell(f
);
160 /* now skip to end - "\n--- " */
163 while (*pos
&& (c
=fgetc(f
)) != EOF
) {
164 if (of
) fputc(c
, of
);
169 end
= of
? ftell(of
) : ftell(f
);
171 end
-= (pos
- target2
) - 1;
172 plist
= patch_add_file(plist
, np
,
173 strdup(name
), start
, end
);
180 fprintf(stderr
,"vpatch: fatal error\n");
186 static struct stream
load_segment(FILE *f
,
187 unsigned int start
, unsigned int end
)
191 s
.body
= malloc(s
.len
);
194 if (fread(s
.body
, 1, s
.len
, f
) != s
.len
) {
210 nocbreak();nl();endwin();
211 printf("Died on signal %d\n", sig
);
215 int pl_cmp(const void *av
, const void *bv
)
217 const struct plist
*a
= av
;
218 const struct plist
*b
= bv
;
219 return strcmp(a
->file
, b
->file
);
222 int common_depth(char *a
, char *b
)
224 /* find number of patch segments that these two have
232 if (c
) al
= c
-a
; else al
= strlen(a
);
234 if (c
) bl
= c
-b
; else bl
= strlen(b
);
235 if (al
== 0 || al
!= bl
|| strncmp(a
,b
,al
) != 0)
246 struct plist
*add_dir(struct plist
*pl
, int *np
, char *file
, char *curr
)
248 /* any parent of file that is not a parent of curr
249 * needs to be added to pl
251 int d
= common_depth(file
, curr
);
254 char *c
= strchr(file
, '/');
256 if (c
) l
= c
-file
; else l
= strlen(file
);
259 while (*file
== '/') file
++;
260 while (*curr
== '/') curr
++;
264 if (curr
> buf
&& curr
[-1] != '/')
266 while (*file
&& *file
!= '/')
268 while (*file
== '/') *file
++;
271 pl
= patch_add_file(pl
, np
, strdup(buf
),
277 struct plist
*sort_patches(struct plist
*pl
, int *np
)
279 /* sort the patches, add directory names, and re-sort */
285 qsort(pl
, *np
, sizeof(struct plist
), pl_cmp
);
289 pl
= add_dir(pl
, np
, pl
[i
].file
, curr
);
291 qsort(pl
, *np
, sizeof(struct plist
), pl_cmp
);
293 /* array is now stable, so set up parent pointers */
298 for (i
=0; i
<n
; i
++) {
299 int d
= common_depth(prev
, pl
[i
].file
);
303 pl
[i
].parent
= parents
[d
-1];
304 pl
[pl
[i
].parent
].last
= i
;
306 pl
[i
].prev
= prevnode
[d
];
308 pl
[pl
[i
].prev
].next
= i
;
317 int get_strip(char *file
)
322 while (file
&& *file
) {
323 fd
= open(file
, O_RDONLY
);
329 file
= strchr(file
, '/');
337 int set_prefix(struct plist
*pl
, int n
, int strip
)
340 for(i
=0; i
<4 && i
<n
&& strip
< 0; i
++)
341 strip
= get_strip(pl
[i
].file
);
344 fprintf(stderr
, "%s: Cannot file files to patch: please specify --strip\n",
348 for (i
=0; i
<n
; i
++) {
349 char *p
= pl
[i
].file
;
351 for (j
=0; j
<strip
; j
++) {
352 if (p
) p
= strchr(p
,'/');
353 while (p
&& *p
== '/') p
++;
356 fprintf(stderr
, "%s: cannot strip %d segments from %s\n",
357 Cmd
, strip
, pl
[i
].file
);
366 int get_prev(int pos
, struct plist
*pl
, int n
)
368 if (pos
== -1) return pos
;
369 if (pl
[pos
].prev
== -1)
370 return pl
[pos
].parent
;
372 while (pl
[pos
].open
&&
378 int get_next(int pos
, struct plist
*pl
, int n
)
380 if (pos
== -1) return pos
;
387 while (pos
>= 0 && pl
[pos
].next
== -1)
388 pos
= pl
[pos
].parent
;
394 /* global attributes */
395 int a_delete
, a_added
, a_common
, a_sep
, a_void
, a_unmatched
, a_extra
, a_already
;
397 void draw_one(int row
, struct plist
*pl
, FILE *f
, int reverse
)
407 if (pl
->calced
== 0 && pl
->end
) {
408 /* better load the patch and count the chunks */
409 struct stream s1
, s2
;
410 struct stream s
= load_segment(f
, pl
->start
, pl
->end
);
411 struct stream sf
= load_file(pl
->file
);
413 pl
->chunks
= split_patch(s
, &s2
, &s1
);
415 pl
->chunks
= split_patch(s
, &s1
, &s2
);
417 if (sf
.body
== NULL
) {
418 pl
->wiggles
= pl
->conflicts
= -1;
420 struct file ff
, fp1
, fp2
;
421 struct csl
*csl1
, *csl2
;
423 ff
= split_stream(sf
, ByWord
, 0);
424 fp1
= split_stream(s1
, ByWord
, 0);
425 fp2
= split_stream(s2
, ByWord
, 0);
426 csl1
= pdiff(ff
, fp1
, pl
->chunks
);
427 csl2
= diff(fp1
,fp2
);
428 ci
= make_merger(ff
, fp1
, fp2
, csl1
, csl2
, 0);
429 pl
->wiggles
= ci
.wiggles
;
430 pl
->conflicts
= ci
.conflicts
;
448 else sprintf(hdr
, "%2d", pl
->chunks
);
449 if (pl
->wiggles
> 99)
450 strcpy(hdr
+2, " XX");
451 else sprintf(hdr
+2, " %2d", pl
->wiggles
);
452 if (pl
->conflicts
> 99)
453 strcpy(hdr
+5, " XX ");
454 else sprintf(hdr
+5, " %2d ", pl
->conflicts
);
460 else strcpy(hdr
+9, "- ");
462 mvaddstr(row
, 0, hdr
);
463 mvaddstr(row
, 11, pl
->file
);
467 void addword(struct elmnt e
)
469 addnstr(e
.start
, e
.len
);
471 void addsep(struct elmnt e1
, struct elmnt e2
)
476 sscanf(e1
.start
+1, "%d %d %d", &a
, &b
, &c
);
477 sscanf(e2
.start
+1, "%d %d %d", &d
, &e
, &f
);
478 sprintf(buf
, "@@ -%d,%d +%d,%d @@\n", b
,c
,e
,f
);
490 struct elmnt
prev_elmnt(struct pos
*pos
, int mode
,
491 struct file f1
, struct file f2
, struct csl
*csl
)
495 if (pos
->a
> csl
[pos
->c
].a
) {
496 assert(pos
->b
> csl
[pos
->c
].b
);
499 return f1
.list
[pos
->a
];
502 if (pos
->c
) b1
= csl
[pos
->c
-1].b
+csl
[pos
->c
-1].len
;
507 return f2
.list
[pos
->b
];
510 if (pos
->c
) a1
= csl
[pos
->c
-1].a
+csl
[pos
->c
-1].len
;
515 return f1
.list
[pos
->a
];
517 /* must be time to go to previous common segment */
521 e
.start
= NULL
; e
.len
= 0;
527 struct elmnt
next_elmnt(struct pos
*pos
, int mode
, int *type
,
528 struct file f1
, struct file f2
, struct csl
*csl
)
532 e
.start
= NULL
; e
.len
= 0;
538 if (pos
->a
< csl
[pos
->c
].a
) {
540 return f1
.list
[pos
->a
++];
545 if (pos
->b
< csl
[pos
->c
].b
) {
547 return f2
.list
[pos
->b
++];
552 a1
= csl
[pos
->c
].a
+ csl
[pos
->c
].len
;
555 return f1
.list
[pos
->a
++];
557 if (csl
[pos
->c
].len
== 0) {
559 e
.start
= NULL
; e
.len
= 0;
567 void pos_sol(struct pos
*pos
, int mode
, struct file f1
, struct file f2
, struct csl
*csl
)
569 /* find the start-of-line before 'pos' considering 'mode' only.
571 if (pos
->c
< 0) return;
573 struct pos tpos
= *pos
;
574 struct elmnt e
= prev_elmnt(&tpos
, mode
, f1
, f2
, csl
);
575 if (e
.start
== NULL
) {
578 if (e
.start
[0] == '\n' || e
.start
[0] == 0)
584 void prev_pos(struct pos
*pos
, int mode
, struct file f1
, struct file f2
, struct csl
*csl
)
586 /* find the start-of-line before 'pos' considering 'mode' only.
589 if (pos
->c
< 0) return;
591 struct pos tpos
= *pos
;
592 struct elmnt e
= prev_elmnt(&tpos
, mode
, f1
, f2
, csl
);
593 if (e
.start
== NULL
) {
597 if (e
.start
[0] == '\n' || e
.start
[0] == 0) {
603 if (e
.start
[0] == 0) return;
608 void next_pos(struct pos
*pos
, int mode
, struct file f1
, struct file f2
, struct csl
*csl
)
610 /* find the start-of-line after 'pos' considering 'mode' only.
613 if (pos
->c
< 0) return;
615 struct pos tpos
= *pos
;
616 struct elmnt e
= next_elmnt(&tpos
, mode
, &type
, f1
, f2
, csl
);
617 if (e
.start
== NULL
) {
622 if (e
.start
[0] == '\n') {
630 void draw_line(int i
, struct pos pos
, int mode
,
631 struct file f1
, struct file f2
, struct csl
*csl
, int start
, int len
)
638 e1
= next_elmnt(&pos
, mode
, &attr
, f1
, f2
, csl
);
639 if (e1
.start
== NULL
) {
643 if (e1
.start
[0] == '\0') {
646 struct elmnt e2
= f2
.list
[pos
.b
-1];
649 sscanf(e1
.start
+1, "%d %d %d", &a
, &b
, &c
);
650 sscanf(e2
.start
+1, "%d %d %d", &d
, &e
, &f
);
651 sprintf(buf
, "@@ -%d,%d +%d,%d @@\n", b
,c
, e
,f
);
658 if (e1
.start
[0] == '\n') {
661 c
= (unsigned char *)e1
.start
;
664 if (*c
>= ' ' && *c
!= 0x7f) {
665 /* normal width char */
666 if (col
>= start
&& col
< len
+start
)
667 mvaddch(i
,col
-start
, *c
);
669 } else if (*c
== '\t') {
671 if (col
>= start
&& col
< len
+start
)
672 mvaddch(i
, col
-start
, ' ');
674 } while ((col
&7)!= 0);
676 if (col
>=start
&& col
< len
+start
)
677 mvaddch(i
, col
-start
, *c
=='\n'?'@':'?');
690 void diff_window(struct plist
*p
, FILE *f
)
692 /* display just the diff, either 'before' or 'after' or all.
694 * In any case highlighting is the same
696 * Current pos is given as a,b,csl where a and b are
697 * before or in the common segment, and one is immediately
699 * The current row is given by 'row'.
700 * Lines do not wrap, be horizontal scrolling is supported.
702 * We don't insist that the top line lies at or above the top
703 * of the screen, as that allows alignment of different views
707 struct stream s1
, s2
;
717 int mode
= BEFORE
|AFTER
;
719 struct pos pos
, tpos
;
721 s
= load_segment(f
, p
->start
, p
->end
);
722 ch
= split_patch(s
, &s1
, &s2
);
725 f1
= split_stream(s1
, ByWord
, 0);
726 f2
= split_stream(s2
, ByWord
, 0);
730 pos
.a
=0; pos
.b
=0; pos
.c
=0;
736 sprintf(buf
, "File: %s\n", p
->file
);
737 attrset(A_BOLD
); mvaddstr(0,0,buf
); clrtoeol(); attrset(A_NORMAL
);
740 if (row
< 1 || row
>= rows
)
744 getmaxyx(stdscr
,rows
,cols
);
754 pos_sol(&tpos
, mode
, f1
, f2
, csl
);
755 draw_line(row
, tpos
, mode
, f1
, f2
, csl
, start
, cols
);
756 for (i
=row
-1; i
>=1; i
--) {
757 prev_pos(&tpos
, mode
, f1
,f2
,csl
);
758 draw_line(i
, tpos
, mode
, f1
, f2
,csl
, start
,cols
);
761 for (i
=row
+1; i
<rows
; i
++) {
762 next_pos(&tpos
, mode
, f1
, f2
, csl
);
763 draw_line(i
, tpos
, mode
, f1
, f2
, csl
, start
,cols
);
782 next_pos(&tpos
, mode
, f1
, f2
, csl
);
794 prev_pos(&tpos
, mode
, f1
, f2
, csl
);
816 if (start
> 0) start
--;
821 if (start
< cols
) start
++;
829 int m
; /* merger index */
830 int s
; /* stream 0,1,2 for a,b,c */
831 int o
; /* offset in that stream */
834 int invalid(int s
, enum mergetype type
)
838 case Unmatched
: return s
>0;
839 case Unchanged
: return s
>0;
840 case Extraneous
: return s
<2;
841 case Changed
: return s
==1;
842 case Conflict
: return 0;
843 case AlreadyApplied
: return 0;
848 struct elmnt
next_melmnt(struct mpos
*pos
,
849 struct file fm
, struct file fb
, struct file fa
,
856 case 0: l
= m
[pos
->m
].al
; break;
857 case 1: l
= m
[pos
->m
].bl
; break;
858 case 2: l
= m
[pos
->m
].cl
; break;
868 } while (invalid(pos
->s
, m
[pos
->m
].type
));
872 if (pos
->m
== -1 || m
[pos
->m
].type
== End
) {
874 e
.start
= NULL
; e
.len
= 0;
878 default: /* keep compiler happy */
879 case 0: return fm
.list
[m
[pos
->m
].a
+ pos
->o
];
880 case 1: return fb
.list
[m
[pos
->m
].b
+ pos
->o
];
881 case 2: return fa
.list
[m
[pos
->m
].c
+ pos
->o
];
885 struct elmnt
prev_melmnt(struct mpos
*pos
,
886 struct file fm
, struct file fb
, struct file fa
,
890 while (pos
->m
>=0 && pos
->o
< 0) {
897 } while (pos
->m
>= 0 && invalid(pos
->s
, m
[pos
->m
].type
));
900 case 0: pos
->o
= m
[pos
->m
].al
-1; break;
901 case 1: pos
->o
= m
[pos
->m
].bl
-1; break;
902 case 2: pos
->o
= m
[pos
->m
].cl
-1; break;
908 e
.start
= NULL
; e
.len
= 0;
912 default: /* keep compiler happy */
913 case 0: return fm
.list
[m
[pos
->m
].a
+ pos
->o
];
914 case 1: return fb
.list
[m
[pos
->m
].b
+ pos
->o
];
915 case 2: return fa
.list
[m
[pos
->m
].c
+ pos
->o
];
919 int visible(int mode
, enum mergetype type
, int stream
)
923 case 0: /* left - orig plus merge */
925 case End
: return A_NORMAL
;
926 case Unmatched
: return stream
== 0 ? a_unmatched
: -1;
927 case Unchanged
: return stream
== 0 ? a_common
: -1;
928 case Extraneous
: return -1;
929 case Changed
: return stream
== 0? a_delete
:stream
==1?-1:a_added
;
930 case Conflict
: return stream
== 0 ? a_unmatched
: -1;
931 case AlreadyApplied
: return stream
== 0 ? a_unmatched
: -1;
934 case 1: /* right - old plus new */
936 case End
: return A_NORMAL
;
937 case Unmatched
: return -1;
938 case Unchanged
: return stream
== 0 ? a_common
: -1;
939 case Extraneous
: return stream
==2 ? a_extra
: -1;
940 case Changed
: return stream
== 0? a_delete
:stream
==1?-1:a_added
;
941 case Conflict
: return stream
==0?-1:stream
==1?(a_delete
|A_UNDERLINE
):a_added
;
942 case AlreadyApplied
: return stream
==0?-1:stream
==1?a_delete
:a_added
;
947 /* mode can be any combination of ORIG RESULT BEFORE AFTER */
949 case End
: /* The END is always visible */
951 case Unmatched
: /* Visible in ORIG and RESULT */
952 if (mode
& (ORIG
|RESULT
))
955 case Unchanged
: /* visible everywhere, but only show stream 0 */
956 if (stream
== 0) return a_common
;
958 case Extraneous
: /* stream 2 is visible in BEFORE and AFTER */
959 if ((mode
& (BEFORE
|AFTER
))
963 case Changed
: /* stream zero visible ORIG and BEFORE, stream 2 elsewhere */
965 (mode
& (ORIG
|BEFORE
)))
968 (mode
& (RESULT
|AFTER
)))
974 if (mode
& (ORIG
|RESULT
))
975 return a_unmatched
| A_REVERSE
;
979 return a_extra
| A_UNDERLINE
;
982 if (mode
& (AFTER
|RESULT
))
983 return a_added
| A_UNDERLINE
;
990 if (mode
& (ORIG
|RESULT
))
995 return a_delete
| A_UNDERLINE
;
999 return a_added
| A_UNDERLINE
;
1009 void next_mline(struct mpos
*pos
, struct file fm
, struct file fb
, struct file fa
,
1010 struct merge
*m
, int mode
)
1012 if (pos
->m
< 0 || m
[pos
->m
].type
== End
)
1015 struct elmnt e
= next_melmnt(pos
, fm
,fb
,fa
,m
);
1016 if (e
.start
== NULL
)
1018 if (ends_mline(e
) && visible(mode
, m
[pos
->m
].type
, pos
->s
) >= 0)
1023 void prev_mline(struct mpos
*pos
, struct file fm
, struct file fb
, struct file fa
,
1024 struct merge
*m
, int mode
)
1029 struct elmnt e
= prev_melmnt(pos
, fm
,fb
,fa
,m
);
1030 if (e
.start
== NULL
||
1031 (ends_mline(e
) && visible(mode
, m
[pos
->m
].type
, pos
->s
) >= 0))
1037 void blank(int row
, int start
, int cols
, int attr
)
1045 void draw_mside(int mode
, int row
, int offset
, int start
, int cols
,
1046 struct file fm
, struct file fb
, struct file fa
, struct merge
*m
,
1049 /* find previous visible newline, or start of file */
1053 e
= prev_melmnt(&pos
, fm
,fb
,fa
,m
);
1054 } while (e
.start
!= NULL
&&
1055 (!ends_mline(e
) || visible(mode
, m
[pos
.m
].type
, pos
.s
)==-1));
1060 e
= next_melmnt(&pos
, fm
,fb
,fa
,m
);
1061 if (e
.start
== NULL
||
1062 (ends_mline(e
) && visible(mode
, m
[pos
.m
].type
, pos
.s
) != -1)) {
1063 if (col
< start
) col
= start
;
1064 if (e
.start
&& e
.start
[0] == 0) {
1065 attrset(visible(mode
, m
[pos
.m
].type
, pos
.s
));
1066 mvaddstr(row
, col
-start
+offset
, "SEP");
1069 blank(row
, col
-start
+offset
, start
+cols
-col
, e
.start
?visible(mode
, m
[pos
.m
].type
, pos
.s
):A_NORMAL
);
1072 if (visible(mode
, m
[pos
.m
].type
, pos
.s
) == -1) {
1075 if (e
.start
[0] == 0)
1077 attrset(visible(mode
, m
[pos
.m
].type
, pos
.s
));
1078 c
= (unsigned char *)e
.start
;
1081 if (*c
>= ' ' && *c
!= 0x7f) {
1082 if (col
>= start
&& col
< start
+cols
)
1083 mvaddch(row
, col
-start
+offset
, *c
);
1085 } else if (*c
== '\t') {
1087 if (col
>= start
&& col
< start
+cols
)
1088 mvaddch(row
, col
-start
+offset
, ' ');
1090 } while ((col
&7)!= 0);
1092 if (col
>= start
&& col
< start
+cols
)
1093 mvaddch(row
, col
-start
+offset
, '?');
1102 void draw_mline(int row
, struct mpos pos
,
1103 struct file fm
, struct file fb
, struct file fa
,
1105 int start
, int cols
, int mode
)
1108 * Draw the left and right images of this line
1109 * One side might be a_blank depending on the
1110 * visibility of this newline
1114 rcols
= cols
- lcols
- 1;
1116 attrset(A_STANDOUT
);
1117 mvaddch(row
, lcols
, '|');
1118 if (visible(mode
&(ORIG
|RESULT
), m
[pos
.m
].type
, pos
.s
) >= 0)
1119 /* visible on left */
1120 draw_mside(mode
&(ORIG
|RESULT
), row
, 0, start
, lcols
,
1123 blank(row
, 0, lcols
, a_void
);
1125 if (visible(mode
&(BEFORE
|AFTER
), m
[pos
.m
].type
, pos
.s
) >= 0)
1126 /* visible on right */
1127 draw_mside(mode
&(BEFORE
|AFTER
), row
, lcols
+1, start
, rcols
,
1130 blank(row
, lcols
+1, rcols
, a_void
);
1133 extern void cleanlist(struct file a
, struct file b
, struct csl
*list
);
1135 void merge_window(struct plist
*p
, FILE *f
, int reverse
)
1137 /* display the merge in two side-by-side
1139 * left side shows diff between original and new
1140 * right side shows the requested patch
1142 * Unmatched: c_unmatched - left only
1143 * Unchanged: c_normal - left and right
1144 * Extraneous: c_extra - right only
1145 * Changed-a: c_changed - left and right
1146 * Changed-c: c_new - left and right
1147 * AlreadyApplied-b: c_old - right only
1148 * AlreadyApplied-c: c_applied - left and right
1149 * Conflict-a: ?? left only
1150 * Conflict-b: ?? left and right
1153 * A Conflict is displayed as the original in the
1154 * left side, and the highlighted diff in the right.
1156 * Newlines are the key to display.
1157 * 'pos' is always a newline (or eof).
1158 * For each side that this newline is visible one, we
1159 * rewind the previous newline visible on this side, and
1160 * the display the stuff inbetween
1162 * A 'position' is an offset in the merger, a stream
1163 * choice (a,b,c - some aren't relevant) and an offset in
1167 struct stream sm
, sb
, sa
, sp
; /* main, before, after, patch */
1168 struct file fm
, fb
, fa
;
1169 struct csl
*csl1
, *csl2
;
1176 int mode
= ORIG
|RESULT
| BEFORE
|AFTER
;
1179 struct mpos pos
, tpos
;
1181 sp
= load_segment(f
, p
->start
, p
->end
);
1183 ch
= split_patch(sp
, &sa
, &sb
);
1185 ch
= split_patch(sp
, &sb
, &sa
);
1187 sm
= load_file(p
->file
);
1188 fm
= split_stream(sm
, ByWord
, 0);
1189 fb
= split_stream(sb
, ByWord
, 0);
1190 fa
= split_stream(sa
, ByWord
, 0);
1192 csl1
= pdiff(fm
, fb
, ch
);
1193 csl2
= diff(fb
, fa
);
1194 cleanlist(fm
, fb
, csl1
);
1195 cleanlist(fb
, fa
, csl2
);
1197 ci
= make_merger(fm
, fb
, fa
, csl1
, csl2
, 0);
1200 pos
.m
= 0; /* merge node */
1201 pos
.s
= 0; /* stream number */
1202 pos
.o
= -1; /* offset */
1203 next_mline(&pos
, fm
,fb
,fa
,ci
.merger
, mode
);
1208 sprintf(buf
, "File: %s%s\n", p
->file
,reverse
?" - reversed":"");
1209 attrset(A_BOLD
); mvaddstr(0,0,buf
);
1214 if (row
< 1 || rows
>= rows
)
1218 getmaxyx(stdscr
, rows
, cols
);
1228 draw_mline(row
,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode
);
1229 for (i
=row
-1; i
>=1; i
--) {
1230 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1231 draw_mline(i
,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode
);
1234 for (i
=row
+1; i
<rows
; i
++) {
1235 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1236 draw_mline(i
,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode
);
1255 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1256 if (ci
.merger
[tpos
.m
].type
!= End
) {
1263 while (pos
.m
>= 0 && ci
.merger
[pos
.m
].type
== Unmatched
)
1264 next_mline(&pos
,fm
,fb
,fa
,ci
.merger
, mode
);
1273 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1280 case 'a': /* 'after' view in patch window */
1287 case 'b': /* 'before' view in patch window */
1294 case 'o': /* 'original' view in the merge window */
1301 case 'r': /* the 'result' view in the merge window */
1311 if (start
> 0) start
--;
1316 if (start
< cols
) start
++;
1323 void main_window(struct plist
*pl
, int n
, FILE *f
, int reverse
)
1325 /* The main window lists all files together with summary information:
1326 * number of chunks, number of wiggles, number of conflicts.
1327 * The list is scrollable
1328 * When a entry is 'selected', we switch to the 'file' window
1329 * The list can be condensed by removing files with no conflict
1330 * or no wiggles, or removing subdirectories
1332 * We record which file in the list is 'current', and which
1333 * screen line it is on. We try to keep things stable while
1336 * Counts are printed before the name using at most 2 digits.
1337 * Numbers greater than 99 are XX
1339 * 27 5 1 drivers/md/md.c
1341 * A directory show the sum in all children.
1344 * select: enter, space, mouseclick
1345 * on file, go to file window
1346 * on directory, toggle open
1347 * up: k, p, control-p uparrow
1348 * Move to previous open object
1349 * down: j, n, control-n, downarrow
1350 * Move to next open object
1353 int pos
=0; /* position in file */
1354 int row
=1; /* position on screen */
1355 int rows
; /* size of screen in rows */
1363 clear(); attrset(0);
1365 mvaddstr(0,0,"Ch Wi Co Patched Files");
1370 if (row
<1 || row
>= rows
)
1374 getmaxyx(stdscr
, rows
, cols
);
1380 for (i
=row
; i
>1; i
--) {
1381 tpos
= get_prev(tpos
, pl
, n
);
1387 /* Ok, row and pos could be trustworthy now */
1389 for (i
=row
; i
>=1; i
--) {
1390 draw_one(i
, &pl
[tpos
], f
, reverse
);
1391 tpos
= get_prev(tpos
, pl
, n
);
1394 for (i
=row
+1; i
<rows
; i
++) {
1395 tpos
= get_next(tpos
, pl
, n
);
1397 draw_one(i
, &pl
[tpos
], f
, reverse
);
1399 draw_one(i
, NULL
, f
, reverse
);
1402 {char bb
[20]; sprintf(bb
,"%d", c
); mvaddstr(0, 70, bb
); clrtoeol();}
1411 tpos
= get_next(pos
, pl
, n
);
1422 tpos
= get_prev(pos
, pl
, n
);
1431 if (pl
[pos
].end
== 0) {
1432 pl
[pos
].open
= ! pl
[pos
].open
;
1435 /* diff_window(&pl[pos], f); */
1436 merge_window(&pl
[pos
],f
,reverse
);
1440 case 27: /* escape */
1452 int vpatch(int argc
, char *argv
[], int strip
, int reverse
, int replace
)
1454 /* NOT argv[0] is first arg... */
1461 in
= fopen(argv
[0], "r");
1463 printf("No %s\n", argv
[0]);
1469 if (lseek(fileno(in
),0L, 1) == -1) {
1472 fprintf(stderr
, "Cannot create a temp file\n");
1477 pl
= parse_patch(in
, f
, &n
);
1479 if (set_prefix(pl
, n
, strip
) == 0) {
1480 fprintf(stderr
, "%s: aborting\n", Cmd
);
1483 pl
= sort_patches(pl
, &n
);
1486 if (fileno(in
) == 0) {
1495 for (i
=0; i
<n
; i
++) {
1496 printf("%3d: %3d %2d/%2d %s\n", i
, pl
[i
].parent
, pl
[i
].prev
, pl
[i
].next
, pl
[i
].file
);
1500 signal(SIGINT
, catch);
1501 signal(SIGQUIT
, catch);
1502 signal(SIGTERM
, catch);
1503 signal(SIGBUS
, catch);
1504 signal(SIGSEGV
, catch);
1506 initscr(); cbreak(); noecho();
1508 use_default_colors();
1509 if (!has_colors()) {
1510 a_delete
= A_UNDERLINE
;
1512 a_common
= A_NORMAL
;
1514 a_already
= A_STANDOUT
;
1516 init_pair(1, COLOR_WHITE
, COLOR_RED
);
1517 a_delete
= COLOR_PAIR(1);
1518 init_pair(2, COLOR_WHITE
, COLOR_BLUE
);
1519 a_added
= COLOR_PAIR(2);
1520 a_common
= A_NORMAL
;
1521 init_pair(3, COLOR_WHITE
, COLOR_GREEN
);
1522 a_sep
= COLOR_PAIR(3);
1523 init_pair(4, COLOR_WHITE
, COLOR_YELLOW
);
1524 a_void
= COLOR_PAIR(4);
1525 init_pair(5, COLOR_BLUE
, COLOR_WHITE
);
1526 a_unmatched
= COLOR_PAIR(5);
1527 init_pair(6, COLOR_CYAN
, COLOR_WHITE
);
1528 a_extra
= COLOR_PAIR(6);
1530 init_pair(7, COLOR_BLACK
, COLOR_CYAN
);
1531 a_already
= COLOR_PAIR(7);
1533 nonl(); intrflush(stdscr
, FALSE
); keypad(stdscr
, TRUE
);
1534 mousemask(ALL_MOUSE_EVENTS
, NULL
);
1536 main_window(pl
, n
, in
, reverse
);
1538 nocbreak();nl();endwin();
1543 WiggleVerbose
=1 ~/work
/wiggle
/wiggle
-mR fs
/nfsd
/nfs4callback
.c
.patches
/removed
/144NfsdV4
-033 |less
1544 neilb@notabene
:/home
/src
/mm$
1546 ~/work
/wiggle
/wiggle
-BR
.patches
/removed
/144NfsdV4
-033