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
);
486 #define CHANGED 32 /* The RESULT is different to ORIG */
487 #define CHANGES 64 /* AFTER is different to BEFORE */
492 struct elmnt
prev_elmnt(struct pos
*pos
, int mode
,
493 struct file f1
, struct file f2
, struct csl
*csl
)
497 if (pos
->a
> csl
[pos
->c
].a
) {
498 assert(pos
->b
> csl
[pos
->c
].b
);
501 return f1
.list
[pos
->a
];
504 if (pos
->c
) b1
= csl
[pos
->c
-1].b
+csl
[pos
->c
-1].len
;
509 return f2
.list
[pos
->b
];
512 if (pos
->c
) a1
= csl
[pos
->c
-1].a
+csl
[pos
->c
-1].len
;
517 return f1
.list
[pos
->a
];
519 /* must be time to go to previous common segment */
523 e
.start
= NULL
; e
.len
= 0;
529 struct elmnt
next_elmnt(struct pos
*pos
, int mode
, int *type
,
530 struct file f1
, struct file f2
, struct csl
*csl
)
534 e
.start
= NULL
; e
.len
= 0;
540 if (pos
->a
< csl
[pos
->c
].a
) {
542 return f1
.list
[pos
->a
++];
547 if (pos
->b
< csl
[pos
->c
].b
) {
549 return f2
.list
[pos
->b
++];
554 a1
= csl
[pos
->c
].a
+ csl
[pos
->c
].len
;
557 return f1
.list
[pos
->a
++];
559 if (csl
[pos
->c
].len
== 0) {
561 e
.start
= NULL
; e
.len
= 0;
569 void pos_sol(struct pos
*pos
, int mode
, struct file f1
, struct file f2
, struct csl
*csl
)
571 /* find the start-of-line before 'pos' considering 'mode' only.
573 if (pos
->c
< 0) return;
575 struct pos tpos
= *pos
;
576 struct elmnt e
= prev_elmnt(&tpos
, mode
, f1
, f2
, csl
);
577 if (e
.start
== NULL
) {
580 if (e
.start
[0] == '\n' || e
.start
[0] == 0)
586 void prev_pos(struct pos
*pos
, int mode
, struct file f1
, struct file f2
, struct csl
*csl
)
588 /* find the start-of-line before 'pos' considering 'mode' only.
591 if (pos
->c
< 0) return;
593 struct pos tpos
= *pos
;
594 struct elmnt e
= prev_elmnt(&tpos
, mode
, f1
, f2
, csl
);
595 if (e
.start
== NULL
) {
599 if (e
.start
[0] == '\n' || e
.start
[0] == 0) {
605 if (e
.start
[0] == 0) return;
610 void next_pos(struct pos
*pos
, int mode
, struct file f1
, struct file f2
, struct csl
*csl
)
612 /* find the start-of-line after 'pos' considering 'mode' only.
615 if (pos
->c
< 0) return;
617 struct pos tpos
= *pos
;
618 struct elmnt e
= next_elmnt(&tpos
, mode
, &type
, f1
, f2
, csl
);
619 if (e
.start
== NULL
) {
624 if (e
.start
[0] == '\n') {
632 void draw_line(int i
, struct pos pos
, int mode
,
633 struct file f1
, struct file f2
, struct csl
*csl
, int start
, int len
)
640 e1
= next_elmnt(&pos
, mode
, &attr
, f1
, f2
, csl
);
641 if (e1
.start
== NULL
) {
645 if (e1
.start
[0] == '\0') {
648 struct elmnt e2
= f2
.list
[pos
.b
-1];
651 sscanf(e1
.start
+1, "%d %d %d", &a
, &b
, &c
);
652 sscanf(e2
.start
+1, "%d %d %d", &d
, &e
, &f
);
653 sprintf(buf
, "@@ -%d,%d +%d,%d @@\n", b
,c
, e
,f
);
660 if (e1
.start
[0] == '\n') {
663 c
= (unsigned char *)e1
.start
;
666 if (*c
>= ' ' && *c
!= 0x7f) {
667 /* normal width char */
668 if (col
>= start
&& col
< len
+start
)
669 mvaddch(i
,col
-start
, *c
);
671 } else if (*c
== '\t') {
673 if (col
>= start
&& col
< len
+start
)
674 mvaddch(i
, col
-start
, ' ');
676 } while ((col
&7)!= 0);
678 if (col
>=start
&& col
< len
+start
)
679 mvaddch(i
, col
-start
, *c
=='\n'?'@':'?');
692 void diff_window(struct plist
*p
, FILE *f
)
694 /* display just the diff, either 'before' or 'after' or all.
696 * In any case highlighting is the same
698 * Current pos is given as a,b,csl where a and b are
699 * before or in the common segment, and one is immediately
701 * The current row is given by 'row'.
702 * Lines do not wrap, be horizontal scrolling is supported.
704 * We don't insist that the top line lies at or above the top
705 * of the screen, as that allows alignment of different views
709 struct stream s1
, s2
;
719 int mode
= BEFORE
|AFTER
;
721 struct pos pos
, tpos
;
723 s
= load_segment(f
, p
->start
, p
->end
);
724 ch
= split_patch(s
, &s1
, &s2
);
727 f1
= split_stream(s1
, ByWord
, 0);
728 f2
= split_stream(s2
, ByWord
, 0);
732 pos
.a
=0; pos
.b
=0; pos
.c
=0;
738 sprintf(buf
, "File: %s\n", p
->file
);
739 attrset(A_BOLD
); mvaddstr(0,0,buf
); clrtoeol(); attrset(A_NORMAL
);
742 if (row
< 1 || row
>= rows
)
746 getmaxyx(stdscr
,rows
,cols
);
756 pos_sol(&tpos
, mode
, f1
, f2
, csl
);
757 draw_line(row
, tpos
, mode
, f1
, f2
, csl
, start
, cols
);
758 for (i
=row
-1; i
>=1; i
--) {
759 prev_pos(&tpos
, mode
, f1
,f2
,csl
);
760 draw_line(i
, tpos
, mode
, f1
, f2
,csl
, start
,cols
);
763 for (i
=row
+1; i
<rows
; i
++) {
764 next_pos(&tpos
, mode
, f1
, f2
, csl
);
765 draw_line(i
, tpos
, mode
, f1
, f2
, csl
, start
,cols
);
784 next_pos(&tpos
, mode
, f1
, f2
, csl
);
796 prev_pos(&tpos
, mode
, f1
, f2
, csl
);
818 if (start
> 0) start
--;
823 if (start
< cols
) start
++;
831 int m
; /* merger index */
832 int s
; /* stream 0,1,2 for a,b,c */
833 int o
; /* offset in that stream */
836 int invalid(int s
, enum mergetype type
)
840 case Unmatched
: return s
>0;
841 case Unchanged
: return s
>0;
842 case Extraneous
: return s
<2;
843 case Changed
: return s
==1;
844 case Conflict
: return 0;
845 case AlreadyApplied
: return 0;
850 struct elmnt
next_melmnt(struct mpos
*pos
,
851 struct file fm
, struct file fb
, struct file fa
,
858 case 0: l
= m
[pos
->m
].al
; break;
859 case 1: l
= m
[pos
->m
].bl
; break;
860 case 2: l
= m
[pos
->m
].cl
; break;
870 } while (invalid(pos
->s
, m
[pos
->m
].type
));
874 if (pos
->m
== -1 || m
[pos
->m
].type
== End
) {
876 e
.start
= NULL
; e
.len
= 0;
880 default: /* keep compiler happy */
881 case 0: return fm
.list
[m
[pos
->m
].a
+ pos
->o
];
882 case 1: return fb
.list
[m
[pos
->m
].b
+ pos
->o
];
883 case 2: return fa
.list
[m
[pos
->m
].c
+ pos
->o
];
887 struct elmnt
prev_melmnt(struct mpos
*pos
,
888 struct file fm
, struct file fb
, struct file fa
,
892 while (pos
->m
>=0 && pos
->o
< 0) {
899 } while (pos
->m
>= 0 && invalid(pos
->s
, m
[pos
->m
].type
));
902 case 0: pos
->o
= m
[pos
->m
].al
-1; break;
903 case 1: pos
->o
= m
[pos
->m
].bl
-1; break;
904 case 2: pos
->o
= m
[pos
->m
].cl
-1; break;
910 e
.start
= NULL
; e
.len
= 0;
914 default: /* keep compiler happy */
915 case 0: return fm
.list
[m
[pos
->m
].a
+ pos
->o
];
916 case 1: return fb
.list
[m
[pos
->m
].b
+ pos
->o
];
917 case 2: return fa
.list
[m
[pos
->m
].c
+ pos
->o
];
921 int visible(int mode
, enum mergetype type
, int stream
)
925 case 0: /* left - orig plus merge */
927 case End
: return A_NORMAL
;
928 case Unmatched
: return stream
== 0 ? a_unmatched
: -1;
929 case Unchanged
: return stream
== 0 ? a_common
: -1;
930 case Extraneous
: return -1;
931 case Changed
: return stream
== 0? a_delete
:stream
==1?-1:a_added
;
932 case Conflict
: return stream
== 0 ? a_unmatched
: -1;
933 case AlreadyApplied
: return stream
== 0 ? a_unmatched
: -1;
936 case 1: /* right - old plus new */
938 case End
: return A_NORMAL
;
939 case Unmatched
: return -1;
940 case Unchanged
: return stream
== 0 ? a_common
: -1;
941 case Extraneous
: return stream
==2 ? a_extra
: -1;
942 case Changed
: return stream
== 0? a_delete
:stream
==1?-1:a_added
;
943 case Conflict
: return stream
==0?-1:stream
==1?(a_delete
|A_UNDERLINE
):a_added
;
944 case AlreadyApplied
: return stream
==0?-1:stream
==1?a_delete
:a_added
;
949 if (mode
== 0) return -1;
950 /* mode can be any combination of ORIG RESULT BEFORE AFTER */
952 case End
: /* The END is always visible */
954 case Unmatched
: /* Visible in ORIG and RESULT */
955 if (mode
& (ORIG
|RESULT
))
958 case Unchanged
: /* visible everywhere, but only show stream 0 */
959 if (stream
== 0) return a_common
;
961 case Extraneous
: /* stream 2 is visible in BEFORE and AFTER */
962 if ((mode
& (BEFORE
|AFTER
))
966 case Changed
: /* stream zero visible ORIG and BEFORE, stream 2 elsewhere */
968 (mode
& (ORIG
|BEFORE
)))
971 (mode
& (RESULT
|AFTER
)))
977 if (mode
& (ORIG
|RESULT
))
978 return a_unmatched
| A_REVERSE
;
982 return a_extra
| A_UNDERLINE
;
985 if (mode
& (AFTER
|RESULT
))
986 return a_added
| A_UNDERLINE
;
993 if (mode
& (ORIG
|RESULT
))
998 return a_delete
| A_UNDERLINE
;
1002 return a_added
| A_UNDERLINE
;
1012 void next_mline(struct mpos
*pos
, struct file fm
, struct file fb
, struct file fa
,
1013 struct merge
*m
, int mode
)
1015 if (pos
->m
< 0 || m
[pos
->m
].type
== End
)
1018 struct elmnt e
= next_melmnt(pos
, fm
,fb
,fa
,m
);
1019 if (e
.start
== NULL
)
1021 if (ends_mline(e
) && visible(mode
, m
[pos
->m
].type
, pos
->s
) >= 0)
1026 void prev_mline(struct mpos
*pos
, struct file fm
, struct file fb
, struct file fa
,
1027 struct merge
*m
, int mode
)
1032 struct elmnt e
= prev_melmnt(pos
, fm
,fb
,fa
,m
);
1033 if (e
.start
== NULL
||
1034 (ends_mline(e
) && visible(mode
, m
[pos
->m
].type
, pos
->s
) >= 0))
1040 void blank(int row
, int start
, int cols
, int attr
)
1048 /* Checkline determines how many screen lines are needed to display
1051 * - one line that is before/original
1052 * - one line that is after/result
1053 * - one line that is unchanged/unmatched/extraneous
1054 * - two lines, but only one on the left.
1055 * - two lines, one before and one after.
1056 * CLARIFY/CORRECT this.
1058 int check_line(struct mpos pos
, struct file fm
, struct file fb
, struct file fa
,
1059 struct merge
*m
, int mode
)
1064 if (visible(ORIG
, m
[pos
.m
].type
, pos
.s
) >= 0)
1066 if (visible(RESULT
, m
[pos
.m
].type
, pos
.s
) >= 0)
1068 if (visible(BEFORE
, m
[pos
.m
].type
, pos
.s
) >= 0)
1070 if (visible(AFTER
, m
[pos
.m
].type
, pos
.s
) >= 0)
1074 if (m
[pos
.m
].type
== Changed
)
1075 rv
|= CHANGED
| CHANGES
;
1076 else if ((m
[pos
.m
].type
== AlreadyApplied
||
1077 m
[pos
.m
].type
== Conflict
))
1079 e
= prev_melmnt(&pos
, fm
,fb
,fa
,m
);
1080 } while (e
.start
!= NULL
&&
1081 (!ends_mline(e
) || visible(mode
, m
[pos
.m
].type
, pos
.s
)==-1));
1083 return rv
& (mode
| CHANGED
| CHANGES
);
1086 void draw_mside(int mode
, int row
, int offset
, int start
, int cols
,
1087 struct file fm
, struct file fb
, struct file fa
, struct merge
*m
,
1090 /* find previous visible newline, or start of file */
1094 e
= prev_melmnt(&pos
, fm
,fb
,fa
,m
);
1095 } while (e
.start
!= NULL
&&
1096 (!ends_mline(e
) || visible(mode
, m
[pos
.m
].type
, pos
.s
)==-1));
1101 e
= next_melmnt(&pos
, fm
,fb
,fa
,m
);
1102 if (e
.start
== NULL
||
1103 (ends_mline(e
) && visible(mode
, m
[pos
.m
].type
, pos
.s
) != -1)) {
1104 if (col
< start
) col
= start
;
1105 if (e
.start
&& e
.start
[0] == 0) {
1106 attrset(visible(mode
, m
[pos
.m
].type
, pos
.s
));
1107 mvaddstr(row
, col
-start
+offset
, "SEP");
1110 blank(row
, col
-start
+offset
, start
+cols
-col
, e
.start
?visible(mode
, m
[pos
.m
].type
, pos
.s
):A_NORMAL
);
1113 if (visible(mode
, m
[pos
.m
].type
, pos
.s
) == -1) {
1116 if (e
.start
[0] == 0)
1118 attrset(visible(mode
, m
[pos
.m
].type
, pos
.s
));
1119 c
= (unsigned char *)e
.start
;
1122 if (*c
>= ' ' && *c
!= 0x7f) {
1123 if (col
>= start
&& col
< start
+cols
)
1124 mvaddch(row
, col
-start
+offset
, *c
);
1126 } else if (*c
== '\t') {
1128 if (col
>= start
&& col
< start
+cols
)
1129 mvaddch(row
, col
-start
+offset
, ' ');
1131 } while ((col
&7)!= 0);
1133 if (col
>= start
&& col
< start
+cols
)
1134 mvaddch(row
, col
-start
+offset
, '?');
1143 void draw_mline(int row
, struct mpos pos
,
1144 struct file fm
, struct file fb
, struct file fa
,
1146 int start
, int cols
, int mode
)
1149 * Draw the left and right images of this line
1150 * One side might be a_blank depending on the
1151 * visibility of this newline
1152 * If Changed is found, return ORIG|RESULT | BEFORE|AFTER,
1153 * If AlreadyApplied or Conflict, return BEFORE|AFTER
1156 lcols
= (cols
-1)/2-1;
1157 rcols
= cols
- lcols
- 3;
1159 attrset(A_STANDOUT
);
1160 mvaddch(row
, lcols
+1, '|');
1163 if (!(mode
& CHANGES
)) {
1164 mvaddch(row
, 0, ' ');
1165 mvaddch(row
, lcols
+2, ' ');
1166 } else if (mode
& (ORIG
|BEFORE
)) {
1167 mvaddch(row
, 0, '-');
1168 mvaddch(row
, lcols
+2, '-');
1170 mvaddch(row
, 0, '+');
1171 mvaddch(row
, lcols
+2, '+');
1174 if (visible(mode
&(ORIG
|RESULT
), m
[pos
.m
].type
, pos
.s
) >= 0)
1175 /* visible on left */
1176 draw_mside(mode
&(ORIG
|RESULT
), row
, 1, start
, lcols
,
1179 blank(row
, 0, lcols
+1, a_void
);
1181 if (visible(mode
&(BEFORE
|AFTER
), m
[pos
.m
].type
, pos
.s
) >= 0)
1182 /* visible on right */
1183 draw_mside(mode
&(BEFORE
|AFTER
), row
, lcols
+3, start
, rcols
,
1186 blank(row
, lcols
+2, rcols
+1, a_void
);
1189 extern void cleanlist(struct file a
, struct file b
, struct csl
*list
);
1191 void merge_window(struct plist
*p
, FILE *f
, int reverse
)
1193 /* display the merge in two side-by-side
1195 * left side shows diff between original and new
1196 * right side shows the requested patch
1198 * Unmatched: c_unmatched - left only
1199 * Unchanged: c_normal - left and right
1200 * Extraneous: c_extra - right only
1201 * Changed-a: c_changed - left and right
1202 * Changed-c: c_new - left and right
1203 * AlreadyApplied-b: c_old - right only
1204 * AlreadyApplied-c: c_applied - left and right
1205 * Conflict-a: ?? left only
1206 * Conflict-b: ?? left and right
1209 * A Conflict is displayed as the original in the
1210 * left side, and the highlighted diff in the right.
1212 * Newlines are the key to display.
1213 * 'pos' is always a newline (or eof).
1214 * For each side that this newline is visible on, we
1215 * rewind the previous newline visible on this side, and
1216 * the display the stuff inbetween
1218 * A 'position' is an offset in the merger, a stream
1219 * choice (a,b,c - some aren't relevant) and an offset in
1223 struct stream sm
, sb
, sa
, sp
; /* main, before, after, patch */
1224 struct file fm
, fb
, fa
;
1225 struct csl
*csl1
, *csl2
;
1232 int mode
= ORIG
|RESULT
| BEFORE
|AFTER
;
1235 struct mpos pos
, tpos
;
1237 sp
= load_segment(f
, p
->start
, p
->end
);
1239 ch
= split_patch(sp
, &sa
, &sb
);
1241 ch
= split_patch(sp
, &sb
, &sa
);
1243 sm
= load_file(p
->file
);
1244 fm
= split_stream(sm
, ByWord
, 0);
1245 fb
= split_stream(sb
, ByWord
, 0);
1246 fa
= split_stream(sa
, ByWord
, 0);
1248 csl1
= pdiff(fm
, fb
, ch
);
1249 csl2
= diff(fb
, fa
);
1251 cleanlist(fm
, fb
, csl1
);
1252 cleanlist(fb
, fa
, csl2
);
1255 ci
= make_merger(fm
, fb
, fa
, csl1
, csl2
, 0);
1258 pos
.m
= 0; /* merge node */
1259 pos
.s
= 0; /* stream number */
1260 pos
.o
= -1; /* offset */
1261 next_mline(&pos
, fm
,fb
,fa
,ci
.merger
, mode
);
1266 sprintf(buf
, "File: %s%s\n", p
->file
,reverse
?" - reversed":"");
1267 attrset(A_BOLD
); mvaddstr(0,0,buf
);
1272 if (row
< 1 || rows
>= rows
)
1278 getmaxyx(stdscr
, rows
, cols
);
1288 for (i
=row
-1; i
>=1 && tpos
.m
>= 0; ) {
1289 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1290 mode2
= check_line(tpos
, fm
,fb
,fa
, ci
.merger
, mode
);
1291 if ((mode2
& (RESULT
|AFTER
)) &&
1292 (mode2
& (CHANGED
)))
1293 draw_mline(i
--,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(RESULT
|AFTER
|CHANGED
|CHANGES
));
1294 else if ((mode2
& (RESULT
|AFTER
)) &&
1295 (mode2
& (CHANGES
)))
1296 draw_mline(i
--,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(AFTER
|CHANGED
|CHANGES
));
1297 if (i
>= 1 && (mode2
& (ORIG
|BEFORE
)))
1298 draw_mline(i
--,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(ORIG
|BEFORE
|CHANGED
|CHANGES
));
1301 for (i
=row
; i
<rows
&& ci
.merger
[tpos
.gm
].type
!= End
; ) {
1302 mode2
= check_line(tpos
, fm
,fb
,fa
,ci
.merger
,mode
);
1303 if (mode2
& (ORIG
|BEFORE
))
1304 draw_mline(i
++,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(ORIG
|BEFORE
|CHANGED
|CHANGES
));
1305 if (i
< rows
&& (mode2
& (RESULT
|AFTER
))) {
1306 if (mode2
& CHANGED
)
1307 draw_mline(i
++,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(RESULT
|AFTER
|CHANGED
|CHANGES
));
1308 else if (mode2
& CHANGES
)
1309 draw_mline(i
++,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(AFTER
|CHANGED
|CHANGES
));
1311 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1331 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1332 if (ci
.merger
[tpos
.m
].type
!= End
) {
1334 mode2
=check_line(pos
, fm
,fb
,fa
,ci
.merger
,mode
);
1335 if ((mode2
& CHANGES
) &&
1336 (mode2
& (BEFORE
|ORIG
)) &&
1337 (mode2
& (AFTER
|RESULT
))
1344 while (pos
.m
>= 0 && ci
.merger
[pos
.m
].type
== Unmatched
)
1345 next_mline(&pos
,fm
,fb
,fa
,ci
.merger
, mode
);
1354 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1358 mode2
= check_line(pos
, fm
,fb
,fa
,ci
.merger
,mode
);
1359 if ((mode2
& CHANGES
) &&
1360 (mode2
& (BEFORE
|ORIG
)) &&
1361 (mode2
& (AFTER
|RESULT
))
1366 case 'a': /* 'after' view in patch window */
1373 case 'b': /* 'before' view in patch window */
1380 case 'o': /* 'original' view in the merge window */
1387 case 'r': /* the 'result' view in the merge window */
1397 if (start
> 0) start
--;
1402 if (start
< cols
) start
++;
1409 void main_window(struct plist
*pl
, int n
, FILE *f
, int reverse
)
1411 /* The main window lists all files together with summary information:
1412 * number of chunks, number of wiggles, number of conflicts.
1413 * The list is scrollable
1414 * When a entry is 'selected', we switch to the 'file' window
1415 * The list can be condensed by removing files with no conflict
1416 * or no wiggles, or removing subdirectories
1418 * We record which file in the list is 'current', and which
1419 * screen line it is on. We try to keep things stable while
1422 * Counts are printed before the name using at most 2 digits.
1423 * Numbers greater than 99 are XX
1425 * 27 5 1 drivers/md/md.c
1427 * A directory show the sum in all children.
1430 * select: enter, space, mouseclick
1431 * on file, go to file window
1432 * on directory, toggle open
1433 * up: k, p, control-p uparrow
1434 * Move to previous open object
1435 * down: j, n, control-n, downarrow
1436 * Move to next open object
1439 int pos
=0; /* position in file */
1440 int row
=1; /* position on screen */
1441 int rows
; /* size of screen in rows */
1449 clear(); attrset(0);
1451 mvaddstr(0,0,"Ch Wi Co Patched Files");
1456 if (row
<1 || row
>= rows
)
1460 getmaxyx(stdscr
, rows
, cols
);
1466 for (i
=row
; i
>1; i
--) {
1467 tpos
= get_prev(tpos
, pl
, n
);
1473 /* Ok, row and pos could be trustworthy now */
1475 for (i
=row
; i
>=1; i
--) {
1476 draw_one(i
, &pl
[tpos
], f
, reverse
);
1477 tpos
= get_prev(tpos
, pl
, n
);
1480 for (i
=row
+1; i
<rows
; i
++) {
1481 tpos
= get_next(tpos
, pl
, n
);
1483 draw_one(i
, &pl
[tpos
], f
, reverse
);
1485 draw_one(i
, NULL
, f
, reverse
);
1488 {char bb
[20]; sprintf(bb
,"%d", c
); mvaddstr(0, 70, bb
); clrtoeol();}
1497 tpos
= get_next(pos
, pl
, n
);
1508 tpos
= get_prev(pos
, pl
, n
);
1517 if (pl
[pos
].end
== 0) {
1518 pl
[pos
].open
= ! pl
[pos
].open
;
1521 /* diff_window(&pl[pos], f); */
1522 merge_window(&pl
[pos
],f
,reverse
);
1526 case 27: /* escape */
1538 int vpatch(int argc
, char *argv
[], int strip
, int reverse
, int replace
)
1540 /* NOT argv[0] is first arg... */
1547 in
= fopen(argv
[0], "r");
1549 printf("No %s\n", argv
[0]);
1555 if (lseek(fileno(in
),0L, 1) == -1) {
1558 fprintf(stderr
, "Cannot create a temp file\n");
1563 pl
= parse_patch(in
, f
, &n
);
1565 if (set_prefix(pl
, n
, strip
) == 0) {
1566 fprintf(stderr
, "%s: aborting\n", Cmd
);
1569 pl
= sort_patches(pl
, &n
);
1572 if (fileno(in
) == 0) {
1581 for (i
=0; i
<n
; i
++) {
1582 printf("%3d: %3d %2d/%2d %s\n", i
, pl
[i
].parent
, pl
[i
].prev
, pl
[i
].next
, pl
[i
].file
);
1586 signal(SIGINT
, catch);
1587 signal(SIGQUIT
, catch);
1588 signal(SIGTERM
, catch);
1589 signal(SIGBUS
, catch);
1590 signal(SIGSEGV
, catch);
1592 initscr(); cbreak(); noecho();
1594 use_default_colors();
1595 if (!has_colors()) {
1596 a_delete
= A_UNDERLINE
;
1598 a_common
= A_NORMAL
;
1600 a_already
= A_STANDOUT
;
1602 init_pair(1, COLOR_WHITE
, COLOR_RED
);
1603 a_delete
= COLOR_PAIR(1);
1604 init_pair(2, COLOR_WHITE
, COLOR_BLUE
);
1605 a_added
= COLOR_PAIR(2);
1606 a_common
= A_NORMAL
;
1607 init_pair(3, COLOR_WHITE
, COLOR_GREEN
);
1608 a_sep
= COLOR_PAIR(3);
1609 init_pair(4, COLOR_WHITE
, COLOR_YELLOW
);
1610 a_void
= COLOR_PAIR(4);
1611 init_pair(5, COLOR_BLUE
, COLOR_WHITE
);
1612 a_unmatched
= COLOR_PAIR(5);
1613 init_pair(6, COLOR_CYAN
, COLOR_WHITE
);
1614 a_extra
= COLOR_PAIR(6);
1616 init_pair(7, COLOR_BLACK
, COLOR_CYAN
);
1617 a_already
= COLOR_PAIR(7);
1619 nonl(); intrflush(stdscr
, FALSE
); keypad(stdscr
, TRUE
);
1620 mousemask(ALL_MOUSE_EVENTS
, NULL
);
1622 main_window(pl
, n
, in
, reverse
);
1624 nocbreak();nl();endwin();
1629 WiggleVerbose
=1 ~/work
/wiggle
/wiggle
-mR fs
/nfsd
/nfs4callback
.c
.patches
/removed
/144NfsdV4
-033 |less
1630 neilb@notabene
:/home
/src
/mm$
1632 ~/work
/wiggle
/wiggle
-BR
.patches
/removed
/144NfsdV4
-033