3 * vpatch - visual front end for wiggle
5 * "files" display, lists all files with statistics
6 * - can hide various lines including subdirectories
7 * and files without wiggles or conflicts
8 * "diff" display shows merged file with different parts
10 * - untouched are pale A_DIM
11 * - matched/remaining are regular A_NORMAL
12 * - matched/removed are red/underlined A_UNDERLINE
13 * - unmatched in file are A_STANDOUT
14 * - unmatched in patch are A_STANDOUT|A_UNDERLINE ???
15 * - inserted are inverse/green ?? A_REVERSE
17 * The window can be split horiz or vert and two different
18 * views displayed. They will have different parts missing
20 * So a display of NORMAL, underline, standout|underline reverse
21 * should show a normal patch.
33 #define assert(x) do { if (!(x)) abort(); } while (0)
37 unsigned int start
, end
;
41 int chunks
, wiggles
, conflicts
;
44 struct plist
*patch_add_file(struct plist
*pl
, int *np
, char *file
,
45 unsigned int start
, unsigned int end
)
47 /* size of pl is 0, 16, n^2 */
51 /* printf("adding %s at %d: %u %u\n", file, n, start, end); */
53 else if (n
<=16) asize
= 16;
54 else if ((n
&(n
-1))==0) asize
= n
;
55 else asize
= n
+1; /* not accurate, but not too large */
57 /* need to extend array */
59 if (asize
< 16) asize
= 16;
61 npl
= realloc(pl
, asize
* sizeof(struct plist
));
63 fprintf(stderr
, "malloc failed - skipping %s\n", file
);
71 pl
[n
].last
= pl
[n
].next
= pl
[n
].prev
= pl
[n
].parent
= -1;
72 pl
[n
].chunks
= pl
[n
].wiggles
= pl
[n
].conflicts
= 0;
80 struct plist
*parse_patch(FILE *f
, FILE *of
, int *np
)
82 /* read a multi-file patch from 'f' and record relevant
84 * if 'of' >= 0, fd might not be seekable so we write
85 * to 'of' and use lseek on 'of' to determine position
87 struct plist
*plist
= NULL
;
90 /* first, find the start of a patch: "\n+++ "
91 * grab the file name and scan to the end of a line
93 char *target
="\n+++ ";
94 char *target2
="\n--- ";
100 while (*pos
&& (c
=fgetc(f
)) != EOF
) {
101 if (of
) fputc(c
, of
);
109 /* now read a file name */
111 while ((c
=fgetc(f
)) != EOF
&& c
!= '\t' && c
!= '\n' && c
!= ' ' &&
114 if (of
) fputc(c
, of
);
119 if (of
) fputc(c
, of
);
120 while (c
!= '\n' && (c
=fgetc(f
)) != EOF
) {
121 if (of
) fputc(c
, of
);
123 start
= of
? ftell(of
) : ftell(f
);
127 /* now skip to end - "\n--- " */
130 while (*pos
&& (c
=fgetc(f
)) != EOF
) {
131 if (of
) fputc(c
, of
);
137 end
= of
? ftell(of
) : ftell(f
);
138 end
-= (pos
- target2
) - 1;
139 plist
= patch_add_file(plist
, np
,
140 strdup(name
), start
, end
);
147 fprintf(stderr
,"vpatch: fatal error\n");
153 static struct stream
load_segment(FILE *f
,
154 unsigned int start
, unsigned int end
)
158 s
.body
= malloc(s
.len
);
161 if (fread(s
.body
, 1, s
.len
, f
) != s
.len
) {
177 nocbreak();nl();endwin();
178 printf("Died on signal %d\n", sig
);
182 int pl_cmp(const void *av
, const void *bv
)
184 const struct plist
*a
= av
;
185 const struct plist
*b
= bv
;
186 return strcmp(a
->file
, b
->file
);
189 int common_depth(char *a
, char *b
)
191 /* find number of patch segments that these two have
199 if (c
) al
= c
-a
; else al
= strlen(a
);
201 if (c
) bl
= c
-b
; else bl
= strlen(b
);
202 if (al
== 0 || al
!= bl
|| strncmp(a
,b
,al
) != 0)
213 struct plist
*add_dir(struct plist
*pl
, int *np
, char *file
, char *curr
)
215 /* any parent of file that is not a parent of curr
216 * needs to be added to pl
218 int d
= common_depth(file
, curr
);
221 char *c
= strchr(file
, '/');
223 if (c
) l
= c
-file
; else l
= strlen(file
);
226 while (*file
== '/') file
++;
227 while (*curr
== '/') curr
++;
231 if (curr
> buf
&& curr
[-1] != '/')
233 while (*file
&& *file
!= '/')
235 while (*file
== '/') *file
++;
238 pl
= patch_add_file(pl
, np
, strdup(buf
),
244 struct plist
*sort_patches(struct plist
*pl
, int *np
)
246 /* sort the patches, add directory names, and re-sort */
252 qsort(pl
, *np
, sizeof(struct plist
), pl_cmp
);
256 pl
= add_dir(pl
, np
, pl
[i
].file
, curr
);
258 qsort(pl
, *np
, sizeof(struct plist
), pl_cmp
);
260 /* array is now stable, so set up parent pointers */
265 for (i
=0; i
<n
; i
++) {
266 int d
= common_depth(prev
, pl
[i
].file
);
270 pl
[i
].parent
= parents
[d
-1];
271 pl
[pl
[i
].parent
].last
= i
;
273 pl
[i
].prev
= prevnode
[d
];
275 pl
[pl
[i
].prev
].next
= i
;
284 int get_prev(int pos
, struct plist
*pl
, int n
)
286 if (pos
== -1) return pos
;
287 if (pl
[pos
].prev
== -1)
288 return pl
[pos
].parent
;
290 while (pl
[pos
].open
&&
296 int get_next(int pos
, struct plist
*pl
, int n
)
298 if (pos
== -1) return pos
;
305 while (pos
>= 0 && pl
[pos
].next
== -1)
306 pos
= pl
[pos
].parent
;
312 void draw_one(int row
, struct plist
*pl
)
324 else sprintf(hdr
, "%02d", pl
->chunks
);
325 if (pl
->wiggles
> 99)
327 else sprintf(hdr
+2, " %02d", pl
->wiggles
);
328 if (pl
->conflicts
> 99)
330 else sprintf(hdr
+5, " %02d ", pl
->conflicts
);
335 else strcpy(hdr
+9, "- ");
337 mvaddstr(row
, 0, hdr
);
338 mvaddstr(row
, 11, pl
->file
);
342 void addword(struct elmnt e
)
344 addnstr(e
.start
, e
.len
);
347 void diff_window(struct plist
*p
, FILE *f
)
350 * I wonder what to display here ....
353 struct stream s1
, s2
;
358 s
= load_segment(f
, p
->start
, p
->end
);
359 ch
= split_patch(s
, &s1
, &s2
);
362 sprintf(buf
, "Chunk count: %d\n", ch
);
363 mvaddstr(1,1,buf
); clrtoeol();
366 f1
= split_stream(s1
, ByWord
, 0);
367 f2
= split_stream(s2
, ByWord
, 0);
371 /* now try to display the diff highlighted */
375 while(a
<f1
.elcnt
|| b
< f2
.elcnt
) {
379 /* if we remove a whole line, output +line,
380 * else clear sol and retry
383 for (a1
=a
; a1
<csl
->a
; a1
++)
384 if (f1
.list
[a1
].start
[0] == '\n') {
391 for (; a
<csl
->a
; a
++) {
393 if (f1
.list
[a
].start
[0] == '\n') {
398 attroff(A_UNDERLINE
);
405 attroff(A_UNDERLINE
);
410 sol
= (f1
.list
[a
].start
[0] == '\n');
412 } while (a
< csl
->a
);
413 attroff(A_UNDERLINE
);
417 } else if (b
< csl
->b
) {
421 for (b1
=b
; b1
<csl
->b
; b1
++)
422 if (f2
.list
[b1
].start
[0] == '\n') {
429 for (; b
<csl
->b
; b
++) {
431 if (f2
.list
[b
].start
[0] == '\n') {
448 sol
= (f2
.list
[b
].start
[0] == '\n');
450 } while (b
< csl
->b
);
459 for (a1
=a
; a1
<csl
->a
+csl
->len
; a1
++)
460 if (f1
.list
[a1
].start
[0] == '\n')
463 if (f1
.list
[a
].start
[0]) {
465 for (; a
< csl
->a
+csl
->len
; a
++,b
++) {
467 if (f1
.list
[a
].start
[0]=='\n') {
480 if (f1
.list
[a
].start
[0] == '\n')
485 if (a
>= csl
->a
+csl
->len
)
499 void main_window(struct plist
*pl
, int n
, FILE *f
)
501 /* The main window lists all files together with summary information:
502 * number of chunks, number of wiggles, number of conflicts.
503 * The list is scrollable
504 * When a entry is 'selected', we switch to the 'file' window
505 * The list can be condensed by removing files with no conflict
506 * or no wiggles, or removing subdirectories
508 * We record which file in the list is 'current', and which
509 * screen line it is on. We try to keep things stable while
512 * Counts are printed before the name using at most 2 digits.
513 * Numbers greater than 99 are XX
515 * 27 5 1 drivers/md/md.c
517 * A directory show the sum in all children.
520 * select: enter, space, mouseclick
521 * on file, go to file window
522 * on directory, toggle open
523 * up: k, p, control-p uparrow
524 * Move to previous open object
525 * down: j, n, control-n, downarrow
526 * Move to next open object
529 int pos
=0; /* position in file */
530 int row
=1; /* position on screen */
531 int rows
; /* size of screen in rows */
541 mvaddstr(0,0,"Ch Wi Co Patched Files");
546 if (row
<1 || row
>= rows
)
550 getmaxyx(stdscr
, rows
, cols
);
556 for (i
=row
; i
>1; i
--) {
557 tpos
= get_prev(tpos
, pl
, n
);
563 /* Ok, row and pos could be trustworthy now */
565 for (i
=row
; i
>=1; i
--) {
566 draw_one(i
, &pl
[tpos
]);
567 tpos
= get_prev(tpos
, pl
, n
);
570 for (i
=row
+1; i
<rows
; i
++) {
571 tpos
= get_next(tpos
, pl
, n
);
573 draw_one(i
, &pl
[tpos
]);
586 tpos
= get_next(pos
, pl
, n
);
597 tpos
= get_prev(pos
, pl
, n
);
606 if (pl
[pos
].end
== 0) {
607 pl
[pos
].open
= ! pl
[pos
].open
;
610 diff_window(&pl
[pos
], f
);
614 case 27: /* escape */
622 int main(int argc
, char *argv
[])
630 f
= fopen(argv
[argc
-1], "w+");
632 in
= fopen(argv
[1], "r");
634 printf("no arg...\n");
638 pl
= parse_patch(in
, f
, &n
);
639 pl
= sort_patches(pl
, &n
);
647 for (i
=0; i
<n
; i
++) {
648 printf("%3d: %3d %2d/%2d %s\n", i
, pl
[i
].parent
, pl
[i
].prev
, pl
[i
].next
, pl
[i
].file
);
652 signal(SIGINT
, catch);
653 signal(SIGQUIT
, catch);
654 signal(SIGTERM
, catch);
655 signal(SIGBUS
, catch);
656 signal(SIGSEGV
, catch);
658 initscr(); cbreak(); noecho();
659 nonl(); intrflush(stdscr
, FALSE
); keypad(stdscr
, TRUE
);
660 mousemask(ALL_MOUSE_EVENTS
, NULL
);
662 main_window(pl
, n
, in
);
664 nocbreak();nl();endwin();