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
);
771 case 27: /* escape */
785 next_pos(&tpos
, mode
, f1
, f2
, csl
);
797 prev_pos(&tpos
, mode
, f1
, f2
, csl
);
819 if (start
> 0) start
--;
824 if (start
< cols
) start
++;
833 int m
; /* merger index */
834 int s
; /* stream 0,1,2 for a,b,c */
835 int o
; /* offset in that stream */
836 } p
, /* the current point */
837 lo
, /* eol for start of the current group */
838 hi
; /* eol for end of the current group */
839 int side
; /* -1 if on the '-' of a diff,
841 * 0 if on an unchanged (lo/hi not meaningful)
844 int same_mpos(struct mpos a
, struct mpos b
)
846 return a
.p
.m
== b
.p
.m
&&
852 int invalid(int s
, enum mergetype type
)
856 case Unmatched
: return s
>0;
857 case Unchanged
: return s
>0;
858 case Extraneous
: return s
<2;
859 case Changed
: return s
==1;
860 case Conflict
: return 0;
861 case AlreadyApplied
: return 0;
866 struct elmnt
next_melmnt(struct mpos
*pos
,
867 struct file fm
, struct file fb
, struct file fa
,
875 else switch(pos
->p
.s
) {
876 case 0: l
= m
[pos
->p
.m
].al
; break;
877 case 1: l
= m
[pos
->p
.m
].bl
; break;
878 case 2: l
= m
[pos
->p
.m
].cl
; break;
888 } while (invalid(pos
->p
.s
, m
[pos
->p
.m
].type
));
892 if (pos
->p
.m
== -1 || m
[pos
->p
.m
].type
== End
) {
894 e
.start
= NULL
; e
.len
= 0;
898 default: /* keep compiler happy */
899 case 0: return fm
.list
[m
[pos
->p
.m
].a
+ pos
->p
.o
];
900 case 1: return fb
.list
[m
[pos
->p
.m
].b
+ pos
->p
.o
];
901 case 2: return fa
.list
[m
[pos
->p
.m
].c
+ pos
->p
.o
];
905 struct elmnt
prev_melmnt(struct mpos
*pos
,
906 struct file fm
, struct file fb
, struct file fa
,
910 while (pos
->p
.m
>=0 && pos
->p
.o
< 0) {
917 } while (pos
->p
.m
>= 0 && invalid(pos
->p
.s
, m
[pos
->p
.m
].type
));
920 case 0: pos
->p
.o
= m
[pos
->p
.m
].al
-1; break;
921 case 1: pos
->p
.o
= m
[pos
->p
.m
].bl
-1; break;
922 case 2: pos
->p
.o
= m
[pos
->p
.m
].cl
-1; break;
928 e
.start
= NULL
; e
.len
= 0;
932 default: /* keep compiler happy */
933 case 0: return fm
.list
[m
[pos
->p
.m
].a
+ pos
->p
.o
];
934 case 1: return fb
.list
[m
[pos
->p
.m
].b
+ pos
->p
.o
];
935 case 2: return fa
.list
[m
[pos
->p
.m
].c
+ pos
->p
.o
];
939 int visible(int mode
, enum mergetype type
, int stream
)
943 case 0: /* left - orig plus merge */
945 case End
: return A_NORMAL
;
946 case Unmatched
: return stream
== 0 ? a_unmatched
: -1;
947 case Unchanged
: return stream
== 0 ? a_common
: -1;
948 case Extraneous
: return -1;
949 case Changed
: return stream
== 0? a_delete
:stream
==1?-1:a_added
;
950 case Conflict
: return stream
== 0 ? a_unmatched
: -1;
951 case AlreadyApplied
: return stream
== 0 ? a_unmatched
: -1;
954 case 1: /* right - old plus new */
956 case End
: return A_NORMAL
;
957 case Unmatched
: return -1;
958 case Unchanged
: return stream
== 0 ? a_common
: -1;
959 case Extraneous
: return stream
==2 ? a_extra
: -1;
960 case Changed
: return stream
== 0? a_delete
:stream
==1?-1:a_added
;
961 case Conflict
: return stream
==0?-1:stream
==1?(a_delete
|A_UNDERLINE
):a_added
;
962 case AlreadyApplied
: return stream
==0?-1:stream
==1?a_delete
:a_added
;
967 if (mode
== 0) return -1;
968 /* mode can be any combination of ORIG RESULT BEFORE AFTER */
970 case End
: /* The END is always visible */
972 case Unmatched
: /* Visible in ORIG and RESULT */
973 if (mode
& (ORIG
|RESULT
))
976 case Unchanged
: /* visible everywhere, but only show stream 0 */
977 if (stream
== 0) return a_common
;
979 case Extraneous
: /* stream 2 is visible in BEFORE and AFTER */
980 if ((mode
& (BEFORE
|AFTER
))
984 case Changed
: /* stream zero visible ORIG and BEFORE, stream 2 elsewhere */
986 (mode
& (ORIG
|BEFORE
)))
989 (mode
& (RESULT
|AFTER
)))
995 if (mode
& (ORIG
|RESULT
))
996 return a_unmatched
| A_REVERSE
;
1000 return a_extra
| A_UNDERLINE
;
1003 if (mode
& (AFTER
|RESULT
))
1004 return a_added
| A_UNDERLINE
;
1008 case AlreadyApplied
:
1011 if (mode
& (ORIG
|RESULT
))
1016 return a_delete
| A_UNDERLINE
;
1020 return a_added
| A_UNDERLINE
;
1029 int check_line(struct mpos pos
, struct file fm
, struct file fb
, struct file fa
,
1030 struct merge
*m
, int mode
);
1032 void next_mline(struct mpos
*pos
, struct file fm
, struct file fb
, struct file fa
,
1033 struct merge
*m
, int mode
)
1042 if (pos
->p
.m
< 0 || m
[pos
->p
.m
].type
== End
) {
1043 if (pos
->side
== -1) {
1053 struct elmnt e
= next_melmnt(pos
, fm
,fb
,fa
,m
);
1054 if (e
.start
== NULL
)
1056 if (ends_mline(e
) &&
1057 visible(mode
, m
[pos
->p
.m
].type
, pos
->p
.s
) >= 0)
1060 mode2
= check_line(*pos
, fm
,fb
,fa
,m
,mode
);
1062 if (pos
->side
== 1 && !(mode2
& CHANGES
))
1063 /* left a diff-set */
1065 else if (pos
->side
== -1 && !(mode2
& CHANGES
)) {
1066 /*flip from '-' to '+' - need to ensure still visible */
1070 } else if (pos
->side
== 0 && (mode2
& CHANGES
)) {
1071 /* entered a diff-set */
1075 mask
= ORIG
|RESULT
|BEFORE
|AFTER
|CHANGES
|CHANGED
;
1077 mask
&= ~(ORIG
|BEFORE
);
1078 if (pos
->side
== -1)
1079 mask
&= ~(RESULT
|AFTER
);
1080 } while (visible(mode
&mask
, m
[pos
->p
.m
].type
, pos
->p
.s
) < 0);
1084 void prev_mline(struct mpos
*pos
, struct file fm
, struct file fb
, struct file fa
,
1085 struct merge
*m
, int mode
)
1096 struct elmnt e
= prev_melmnt(pos
, fm
,fb
,fa
,m
);
1097 if (e
.start
== NULL
)
1099 if (ends_mline(e
) &&
1100 visible(mode
, m
[pos
->p
.m
].type
, pos
->p
.s
) >= 0)
1103 mode2
= check_line(*pos
, fm
,fb
,fa
,m
,mode
);
1105 if (pos
->side
== -1 && !(mode2
& CHANGES
))
1106 /* we have stepped out of a diff-set */
1108 else if (pos
->side
== 1 && !(mode2
& CHANGES
)) {
1109 /* flipped from '+' to '-' */
1113 } else if (pos
->side
== 0 && (mode2
& CHANGES
)) {
1114 /* entered a diffset */
1118 mask
= ORIG
|RESULT
|BEFORE
|AFTER
|CHANGES
|CHANGED
;
1120 mask
&= ~(ORIG
|BEFORE
);
1121 if (pos
->side
== -1)
1122 mask
&= ~(RESULT
|AFTER
);
1123 } while (visible(mode
&mask
, m
[pos
->p
.m
].type
, pos
->p
.s
) < 0);
1127 void blank(int row
, int start
, int cols
, int attr
)
1135 /* Checkline determines how many screen lines are needed to display
1138 * - one line that is before/original
1139 * - one line that is after/result
1140 * - one line that is unchanged/unmatched/extraneous
1141 * - two lines, but only one on the left.
1142 * - two lines, one before and one after.
1143 * CLARIFY/CORRECT this.
1145 int check_line(struct mpos pos
, struct file fm
, struct file fb
, struct file fa
,
1146 struct merge
*m
, int mode
)
1151 if (visible(ORIG
, m
[pos
.p
.m
].type
, pos
.p
.s
) >= 0)
1153 if (visible(RESULT
, m
[pos
.p
.m
].type
, pos
.p
.s
) >= 0)
1155 if (visible(BEFORE
, m
[pos
.p
.m
].type
, pos
.p
.s
) >= 0)
1157 if (visible(AFTER
, m
[pos
.p
.m
].type
, pos
.p
.s
) >= 0)
1161 if (m
[pos
.p
.m
].type
== Changed
)
1162 rv
|= CHANGED
| CHANGES
;
1163 else if ((m
[pos
.p
.m
].type
== AlreadyApplied
||
1164 m
[pos
.p
.m
].type
== Conflict
))
1166 e
= prev_melmnt(&pos
, fm
,fb
,fa
,m
);
1167 } while (e
.start
!= NULL
&&
1168 (!ends_mline(e
) || visible(mode
, m
[pos
.p
.m
].type
, pos
.p
.s
)==-1));
1170 return rv
& (mode
| CHANGED
| CHANGES
);
1173 int mcontains(struct mpos pos
,
1174 struct file fm
, struct file fb
, struct file fa
, struct merge
*m
,
1175 int mode
, char *search
)
1177 /* See if and of the files, between start of this line and here,
1178 * contain the search string
1181 int len
= strlen(search
);
1183 e
= prev_melmnt(&pos
, fm
,fb
,fa
,m
);
1186 for (i
=0; i
<e
.len
; i
++)
1187 if (strncmp(e
.start
+i
, search
, len
)==0)
1190 } while (e
.start
!= NULL
&&
1191 (!ends_mline(e
) || visible(mode
, m
[pos
.p
.m
].type
, pos
.p
.s
)==-1));
1195 void draw_mside(int mode
, int row
, int offset
, int start
, int cols
,
1196 struct file fm
, struct file fb
, struct file fa
, struct merge
*m
,
1198 int target
, int *colp
)
1200 /* find previous visible newline, or start of file */
1204 e
= prev_melmnt(&pos
, fm
,fb
,fa
,m
);
1205 } while (e
.start
!= NULL
&&
1206 (!ends_mline(e
) || visible(mode
, m
[pos
.p
.m
].type
, pos
.p
.s
)==-1));
1211 e
= next_melmnt(&pos
, fm
,fb
,fa
,m
);
1212 if (e
.start
== NULL
||
1213 (ends_mline(e
) && visible(mode
, m
[pos
.p
.m
].type
, pos
.p
.s
) != -1)) {
1214 if (colp
) *colp
= col
;
1215 if (col
< start
) col
= start
;
1216 if (e
.start
&& e
.start
[0] == 0) {
1217 attrset(visible(mode
, m
[pos
.p
.m
].type
, pos
.p
.s
));
1218 mvaddstr(row
, col
-start
+offset
, "SEP");
1221 blank(row
, col
-start
+offset
, start
+cols
-col
, e
.start
?visible(mode
, m
[pos
.p
.m
].type
, pos
.p
.s
):A_NORMAL
);
1224 if (visible(mode
, m
[pos
.p
.m
].type
, pos
.p
.s
) == -1) {
1227 if (e
.start
[0] == 0)
1229 attrset(visible(mode
, m
[pos
.p
.m
].type
, pos
.p
.s
));
1230 c
= (unsigned char *)e
.start
;
1233 if (*c
>= ' ' && *c
!= 0x7f) {
1234 if (col
>= start
&& col
< start
+cols
)
1235 mvaddch(row
, col
-start
+offset
, *c
);
1237 } else if (*c
== '\t') {
1239 if (col
>= start
&& col
< start
+cols
)
1240 mvaddch(row
, col
-start
+offset
, ' ');
1242 } while ((col
&7)!= 0);
1244 if (col
>= start
&& col
< start
+cols
)
1245 mvaddch(row
, col
-start
+offset
, '?');
1249 if (colp
&& target
<= col
) {
1258 void draw_mline(int row
, struct mpos pos
,
1259 struct file fm
, struct file fb
, struct file fa
,
1261 int start
, int cols
, int mode
,
1262 int target
, int *colp
)
1265 * Draw the left and right images of this line
1266 * One side might be a_blank depending on the
1267 * visibility of this newline
1268 * If Changed is found, return ORIG|RESULT | BEFORE|AFTER,
1269 * If AlreadyApplied or Conflict, return BEFORE|AFTER
1272 lcols
= (cols
-1)/2-1;
1273 rcols
= cols
- lcols
- 3;
1275 attrset(A_STANDOUT
);
1276 mvaddch(row
, lcols
+1, '|');
1279 if (!(mode
& CHANGES
)) {
1280 mvaddch(row
, 0, ' ');
1281 mvaddch(row
, lcols
+2, ' ');
1282 } else if (mode
& (ORIG
|BEFORE
)) {
1283 mvaddch(row
, 0, '-');
1284 mvaddch(row
, lcols
+2, '-');
1286 mvaddch(row
, 0, '+');
1287 mvaddch(row
, lcols
+2, '+');
1290 if (visible(mode
&(ORIG
|RESULT
), m
[pos
.p
.m
].type
, pos
.p
.s
) >= 0)
1291 /* visible on left */
1292 draw_mside(mode
&(ORIG
|RESULT
), row
, 1, start
, lcols
,
1293 fm
,fb
,fa
,m
, pos
, target
, colp
);
1295 blank(row
, 0, lcols
+1, a_void
);
1296 if (colp
) *colp
= 0;
1299 if (visible(mode
&(BEFORE
|AFTER
), m
[pos
.p
.m
].type
, pos
.p
.s
) >= 0)
1300 /* visible on right */
1301 draw_mside(mode
&(BEFORE
|AFTER
), row
, lcols
+3, start
, rcols
,
1302 fm
,fb
,fa
,m
, pos
, 0, NULL
);
1304 blank(row
, lcols
+2, rcols
+1, a_void
);
1307 extern void cleanlist(struct file a
, struct file b
, struct csl
*list
);
1309 void merge_window(struct plist
*p
, FILE *f
, int reverse
)
1311 /* display the merge in two side-by-side
1313 * left side shows diff between original and new
1314 * right side shows the requested patch
1316 * Unmatched: c_unmatched - left only
1317 * Unchanged: c_normal - left and right
1318 * Extraneous: c_extra - right only
1319 * Changed-a: c_changed - left and right
1320 * Changed-c: c_new - left and right
1321 * AlreadyApplied-b: c_old - right only
1322 * AlreadyApplied-c: c_applied - left and right
1323 * Conflict-a: ?? left only
1324 * Conflict-b: ?? left and right
1327 * A Conflict is displayed as the original in the
1328 * left side, and the highlighted diff in the right.
1330 * Newlines are the key to display.
1331 * 'pos' is always a newline (or eof).
1332 * For each side that this newline is visible on, we
1333 * rewind the previous newline visible on this side, and
1334 * the display the stuff in between
1336 * A 'position' is an offset in the merger, a stream
1337 * choice (a,b,c - some aren't relevant) and an offset in
1341 struct stream sm
, sb
, sa
, sp
; /* main, before, after, patch */
1342 struct file fm
, fb
, fa
;
1343 struct csl
*csl1
, *csl2
;
1350 int mode
= ORIG
|RESULT
| BEFORE
|AFTER
;
1354 int col
=0, target
=0;
1356 struct mpos tpos
, toppos
, botpos
;
1359 int meta
= 0, tmeta
;
1363 int search_notfound
= 0;
1365 struct search_anchor
{
1366 struct search_anchor
*next
;
1369 int row
, col
, searchlen
;
1372 sp
= load_segment(f
, p
->start
, p
->end
);
1374 ch
= split_patch(sp
, &sa
, &sb
);
1376 ch
= split_patch(sp
, &sb
, &sa
);
1378 sm
= load_file(p
->file
);
1379 fm
= split_stream(sm
, ByWord
, 0);
1380 fb
= split_stream(sb
, ByWord
, 0);
1381 fa
= split_stream(sa
, ByWord
, 0);
1383 csl1
= pdiff(fm
, fb
, ch
);
1384 csl2
= diff(fb
, fa
);
1386 cleanlist(fm
, fb
, csl1
);
1387 cleanlist(fb
, fa
, csl2
);
1390 ci
= make_merger(fm
, fb
, fa
, csl1
, csl2
, 0);
1393 pos
.p
.m
= 0; /* merge node */
1394 pos
.p
.s
= 0; /* stream number */
1395 pos
.p
.o
= -1; /* offset */
1396 next_mline(&pos
, fm
,fb
,fa
,ci
.merger
, mode
);
1397 mode2
= check_line(pos
, fm
,fb
,fa
, ci
.merger
, mode
);
1398 if (!(mode2
& CHANGES
))
1401 if (mode2
& (ORIG
|BEFORE
))
1409 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1410 mode2
= check_line(tpos
, fm
,fb
,fa
, ci
.merger
, mode
);
1411 } while ((mode2
& CHANGES
) && ci
.merger
[tpos
.p
.m
].type
!= End
);
1417 sprintf(buf
, "File: %s%s\n", p
->file
,reverse
?" - reversed":"");
1418 attrset(A_BOLD
); mvaddstr(0,0,buf
);
1423 if (row
< 1 || row
>= rows
)
1428 getmaxyx(stdscr
, rows
, cols
);
1429 rows
--; /* keep last row clear */
1440 /* Always refresh the line */
1441 while (start
> target
) { start
-= 8; refresh
= 1;}
1442 if (start
< 0) start
= 0;
1444 mode2
= check_line(pos
, fm
,fb
,fa
,ci
.merger
,mode
);
1445 if ((pos
.side
<= 0) && (mode2
& (ORIG
|BEFORE
)))
1446 draw_mline(row
,pos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(ORIG
|BEFORE
|CHANGED
|CHANGES
), target
, &col
);
1447 else if (pos
.side
> 0 && (mode2
& (RESULT
|AFTER
))) {
1448 if (mode2
& CHANGED
)
1449 draw_mline(row
,pos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(RESULT
|AFTER
|CHANGED
|CHANGES
), target
, &col
);
1450 else if (mode2
& CHANGES
)
1451 draw_mline(row
,pos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(AFTER
|CHANGED
|CHANGES
), target
, &col
);
1453 if (col
> (cols
-3)/2+start
) {
1461 if (start
< 0) start
= 0;
1468 for (i
=row
-1; i
>=1 && tpos
.p
.m
>= 0; ) {
1469 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1470 mode2
= check_line(tpos
, fm
,fb
,fa
, ci
.merger
, mode
);
1472 if (tpos
.side
== 1 &&
1473 (mode2
& (RESULT
|AFTER
)) &&
1474 (mode2
& (CHANGED
)))
1475 draw_mline(i
--,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(RESULT
|AFTER
|CHANGED
|CHANGES
), 0, NULL
);
1476 else if (tpos
.side
== 1 &&
1477 (mode2
& (RESULT
|AFTER
)) &&
1478 (mode2
& (CHANGES
)))
1479 draw_mline(i
--,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(AFTER
|CHANGED
|CHANGES
), 0, NULL
);
1480 else if ((tpos
.side
== 0 || tpos
.side
== -1) && (mode2
& (ORIG
|BEFORE
)))
1481 draw_mline(i
--,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(ORIG
|BEFORE
|CHANGED
|CHANGES
), 0, NULL
);
1483 toppos
= tpos
; toprow
= i
;
1485 blank(i
--, 0, cols
, a_void
);
1487 for (i
=row
; i
<rows
&& ci
.merger
[tpos
.p
.m
].type
!= End
; ) {
1488 mode2
= check_line(tpos
, fm
,fb
,fa
,ci
.merger
,mode
);
1489 if ((tpos
.side
<= 0) && (mode2
& (ORIG
|BEFORE
)))
1490 draw_mline(i
++,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(ORIG
|BEFORE
|CHANGED
|CHANGES
), 0, NULL
);
1491 else if (tpos
.side
> 0 && (mode2
& (RESULT
|AFTER
))) {
1492 if (mode2
& CHANGED
)
1493 draw_mline(i
++,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(RESULT
|AFTER
|CHANGED
|CHANGES
), 0, NULL
);
1494 else if (mode2
& CHANGES
)
1495 draw_mline(i
++,tpos
,fm
,fb
,fa
,ci
.merger
,start
,cols
, mode2
&(AFTER
|CHANGED
|CHANGES
), 0, NULL
);
1497 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1499 botpos
= tpos
; botrow
= i
;
1501 blank(i
++, 0, cols
, a_void
);
1504 #define META(c) ((c)|0x1000)
1505 #define SEARCH(c) ((c)|0x2000)
1508 if (num
>=0) { char buf
[10]; sprintf(buf
, "%d ", num
); addstr(buf
);}
1509 if (meta
& META(0)) addstr("ESC...");
1510 if (meta
& SEARCH(0)) {
1511 if (searchdir
) addstr("Backwards ");
1514 if (search_notfound
)
1515 addstr(" - Not Found.");
1516 search_notfound
= 0;
1519 move(row
,col
-start
);
1521 tmeta
= meta
; meta
= 0;
1522 tnum
= num
; num
= -1;
1524 case 27: /* escape */
1528 case META('<'): /* start of file */
1533 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
,mode
);
1534 } while (tpos
.p
.m
>= 0);
1538 case META('>'): /* end of file */
1540 if (tnum
>=0) goto start
;
1544 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
,mode
);
1545 } while (ci
.merger
[tpos
.p
.m
].type
!= End
);
1550 if (tnum
< 0) tnum
= 0;
1551 num
= tnum
*10 + (c
-'0');
1558 /* incr search forward */
1561 search
[searchlen
] = 0;
1566 /* incr search backwards */
1569 search
[searchlen
] = 0;
1572 case SEARCH('G'-64):
1573 case SEARCH('S'-64):
1576 tpos
= pos
; trow
= row
;
1579 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
,mode
);
1582 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
,mode
);
1586 case SEARCH('H'-64):
1589 struct search_anchor
*a
;
1595 struct search_anchor
*a
;
1601 search_notfound
= a
->notfound
;
1602 searchlen
= a
->searchlen
;
1603 search
[searchlen
] = 0;
1608 case SEARCH(' ') ... SEARCH('~'):
1611 if (searchlen
< sizeof(search
)-1)
1612 search
[searchlen
++] = c
& (0x7f);
1613 search
[searchlen
] = 0;
1614 tpos
= pos
; trow
= row
;
1616 search_notfound
= 1;
1618 if (mcontains(tpos
, fm
,fb
,fa
,ci
.merger
,mode
,search
)) {
1621 search_notfound
= 0;
1626 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
,mode
);
1629 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
,mode
);
1631 } while (tpos
.p
.m
>= 0 && ci
.merger
[tpos
.p
.m
].type
!= End
);
1636 if (toprow
>= 1) row
-= (toprow
+1);
1639 case 'V'-64: /* page down */
1647 case META('v'): /* page up */
1660 if (tnum
< 0) tnum
= 1;
1661 for (; tnum
> 0 ; tnum
--) {
1663 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1664 if (ci
.merger
[tpos
.p
.m
].type
!= End
) {
1675 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1676 } while (pos
.side
!= 0 && ci
.merger
[tpos
.p
.m
].type
!= End
);
1680 next_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1681 } while (pos
.side
== 0 && ci
.merger
[tpos
.p
.m
].type
!= End
);
1689 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1690 } while (tpos
.side
== 0 && tpos
.p
.m
>= 0);
1694 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1695 } while (tpos
.side
!= 0 && tpos
.p
.m
>= 0);
1703 if (tnum
< 0) tnum
= 1;
1704 for (; tnum
> 0 ; tnum
--) {
1706 prev_mline(&tpos
, fm
,fb
,fa
,ci
.merger
, mode
);
1707 if (tpos
.p
.m
>= 0) {
1718 if (target
< 0) target
= 0;
1737 case 'a': /* 'after' view in patch window */
1744 case 'b': /* 'before' view in patch window */
1751 case 'o': /* 'original' view in the merge window */
1758 case 'r': /* the 'result' view in the merge window */
1768 if (start
> 0) start
--;
1773 if (start
< cols
) start
++;
1779 if (meta
== SEARCH(0)) {
1780 if (anchor
== NULL
||
1781 !same_mpos(anchor
->pos
, pos
) ||
1782 anchor
->searchlen
!= searchlen
||
1783 anchor
->col
!= col
) {
1784 struct search_anchor
*a
= malloc(sizeof(*a
));
1788 a
->searchlen
= searchlen
;
1789 a
->notfound
= search_notfound
;
1795 struct search_anchor
*a
= anchor
;
1803 void main_window(struct plist
*pl
, int n
, FILE *f
, int reverse
)
1805 /* The main window lists all files together with summary information:
1806 * number of chunks, number of wiggles, number of conflicts.
1807 * The list is scrollable
1808 * When a entry is 'selected', we switch to the 'file' window
1809 * The list can be condensed by removing files with no conflict
1810 * or no wiggles, or removing subdirectories
1812 * We record which file in the list is 'current', and which
1813 * screen line it is on. We try to keep things stable while
1816 * Counts are printed before the name using at most 2 digits.
1817 * Numbers greater than 99 are XX
1819 * 27 5 1 drivers/md/md.c
1821 * A directory show the sum in all children.
1824 * select: enter, space, mouseclick
1825 * on file, go to file window
1826 * on directory, toggle open
1827 * up: k, p, control-p uparrow
1828 * Move to previous open object
1829 * down: j, n, control-n, downarrow
1830 * Move to next open object
1833 int pos
=0; /* position in file */
1834 int row
=1; /* position on screen */
1835 int rows
; /* size of screen in rows */
1843 clear(); attrset(0);
1845 mvaddstr(0,0,"Ch Wi Co Patched Files");
1850 if (row
<1 || row
>= rows
)
1854 getmaxyx(stdscr
, rows
, cols
);
1860 for (i
=row
; i
>1; i
--) {
1861 tpos
= get_prev(tpos
, pl
, n
);
1867 /* Ok, row and pos could be trustworthy now */
1869 for (i
=row
; i
>=1; i
--) {
1870 draw_one(i
, &pl
[tpos
], f
, reverse
);
1871 tpos
= get_prev(tpos
, pl
, n
);
1874 for (i
=row
+1; i
<rows
; i
++) {
1875 tpos
= get_next(tpos
, pl
, n
);
1877 draw_one(i
, &pl
[tpos
], f
, reverse
);
1879 draw_one(i
, NULL
, f
, reverse
);
1882 {char bb
[20]; sprintf(bb
,"%d", c
); mvaddstr(0, 70, bb
); clrtoeol();}
1891 tpos
= get_next(pos
, pl
, n
);
1902 tpos
= get_prev(pos
, pl
, n
);
1911 if (pl
[pos
].end
== 0) {
1912 pl
[pos
].open
= ! pl
[pos
].open
;
1915 /* diff_window(&pl[pos], f); */
1916 merge_window(&pl
[pos
],f
,reverse
);
1920 case 27: /* escape */
1921 mvaddstr(0,70,"ESC..."); clrtoeol();
1937 int vpatch(int argc
, char *argv
[], int strip
, int reverse
, int replace
)
1939 /* NOT argv[0] is first arg... */
1946 in
= fopen(argv
[0], "r");
1948 printf("No %s\n", argv
[0]);
1954 if (lseek(fileno(in
),0L, 1) == -1) {
1957 fprintf(stderr
, "Cannot create a temp file\n");
1962 pl
= parse_patch(in
, f
, &n
);
1964 if (set_prefix(pl
, n
, strip
) == 0) {
1965 fprintf(stderr
, "%s: aborting\n", Cmd
);
1968 pl
= sort_patches(pl
, &n
);
1971 if (fileno(in
) == 0) {
1980 for (i
=0; i
<n
; i
++) {
1981 printf("%3d: %3d %2d/%2d %s\n", i
, pl
[i
].parent
, pl
[i
].prev
, pl
[i
].next
, pl
[i
].file
);
1985 signal(SIGINT
, catch);
1986 signal(SIGQUIT
, catch);
1987 signal(SIGTERM
, catch);
1988 signal(SIGBUS
, catch);
1989 signal(SIGSEGV
, catch);
1991 initscr(); cbreak(); noecho();
1993 use_default_colors();
1994 if (!has_colors()) {
1995 a_delete
= A_UNDERLINE
;
1997 a_common
= A_NORMAL
;
1999 a_already
= A_STANDOUT
;
2001 init_pair(1, COLOR_WHITE
, COLOR_RED
);
2002 a_delete
= COLOR_PAIR(1);
2003 init_pair(2, COLOR_WHITE
, COLOR_BLUE
);
2004 a_added
= COLOR_PAIR(2);
2005 a_common
= A_NORMAL
;
2006 init_pair(3, COLOR_WHITE
, COLOR_GREEN
);
2007 a_sep
= COLOR_PAIR(3);
2008 init_pair(4, COLOR_WHITE
, COLOR_YELLOW
);
2009 a_void
= COLOR_PAIR(4);
2010 init_pair(5, COLOR_BLUE
, -1);
2011 a_unmatched
= COLOR_PAIR(5);
2012 init_pair(6, COLOR_CYAN
, -1);
2013 a_extra
= COLOR_PAIR(6);
2015 init_pair(7, COLOR_BLACK
, COLOR_CYAN
);
2016 a_already
= COLOR_PAIR(7);
2018 nonl(); intrflush(stdscr
, FALSE
); keypad(stdscr
, TRUE
);
2019 mousemask(ALL_MOUSE_EVENTS
, NULL
);
2021 main_window(pl
, n
, in
, reverse
);
2023 nocbreak();nl();endwin();
2028 WiggleVerbose
=1 ~/work
/wiggle
/wiggle
-mR fs
/nfsd
/nfs4callback
.c
.patches
/removed
/144NfsdV4
-033 |less
2029 neilb@notabene
:/home
/src
/mm$
2031 ~/work
/wiggle
/wiggle
-BR
.patches
/removed
/144NfsdV4
-033