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 horizontally or vertically and
18 * two different views displayed. They will have different
21 * So a display of NORMAL, underline, standout|underline reverse
22 * should show a normal patch.
34 #define assert(x) do { if (!(x)) abort(); } while (0)
38 unsigned int start
, end
;
42 int chunks
, wiggles
, conflicts
;
45 struct plist
*patch_add_file(struct plist
*pl
, int *np
, char *file
,
46 unsigned int start
, unsigned int end
)
48 /* size of pl is 0, 16, n^2 */
52 /* printf("adding %s at %d: %u %u\n", file, n, start, end); */
54 else if (n
<=16) asize
= 16;
55 else if ((n
&(n
-1))==0) asize
= n
;
56 else asize
= n
+1; /* not accurate, but not too large */
58 /* need to extend array */
60 if (asize
< 16) asize
= 16;
62 npl
= realloc(pl
, asize
* sizeof(struct plist
));
64 fprintf(stderr
, "malloc failed - skipping %s\n", file
);
72 pl
[n
].last
= pl
[n
].next
= pl
[n
].prev
= pl
[n
].parent
= -1;
73 pl
[n
].chunks
= pl
[n
].wiggles
= pl
[n
].conflicts
= 0;
81 struct plist
*parse_patch(FILE *f
, FILE *of
, int *np
)
83 /* read a multi-file patch from 'f' and record relevant
85 * if 'of' >= 0, fd might not be seekable so we write
86 * to 'of' and use lseek on 'of' to determine position
88 struct plist
*plist
= NULL
;
91 /* first, find the start of a patch: "\n+++ "
92 * grab the file name and scan to the end of a line
94 char *target
="\n+++ ";
95 char *target2
="\n--- ";
101 while (*pos
&& (c
=fgetc(f
)) != EOF
) {
102 if (of
) fputc(c
, of
);
110 /* now read a file name */
112 while ((c
=fgetc(f
)) != EOF
&& c
!= '\t' && c
!= '\n' && c
!= ' ' &&
115 if (of
) fputc(c
, of
);
120 if (of
) fputc(c
, of
);
121 while (c
!= '\n' && (c
=fgetc(f
)) != EOF
) {
122 if (of
) fputc(c
, of
);
124 start
= of
? ftell(of
) : ftell(f
);
128 /* now skip to end - "\n--- " */
131 while (*pos
&& (c
=fgetc(f
)) != EOF
) {
132 if (of
) fputc(c
, of
);
138 end
= of
? ftell(of
) : ftell(f
);
139 end
-= (pos
- target2
) - 1;
140 plist
= patch_add_file(plist
, np
,
141 strdup(name
), start
, end
);
148 fprintf(stderr
,"vpatch: fatal error\n");
154 static struct stream
load_segment(FILE *f
,
155 unsigned int start
, unsigned int end
)
159 s
.body
= malloc(s
.len
);
162 if (fread(s
.body
, 1, s
.len
, f
) != s
.len
) {
178 nocbreak();nl();endwin();
179 printf("Died on signal %d\n", sig
);
183 int pl_cmp(const void *av
, const void *bv
)
185 const struct plist
*a
= av
;
186 const struct plist
*b
= bv
;
187 return strcmp(a
->file
, b
->file
);
190 int common_depth(char *a
, char *b
)
192 /* find number of patch segments that these two have
200 if (c
) al
= c
-a
; else al
= strlen(a
);
202 if (c
) bl
= c
-b
; else bl
= strlen(b
);
203 if (al
== 0 || al
!= bl
|| strncmp(a
,b
,al
) != 0)
214 struct plist
*add_dir(struct plist
*pl
, int *np
, char *file
, char *curr
)
216 /* any parent of file that is not a parent of curr
217 * needs to be added to pl
219 int d
= common_depth(file
, curr
);
222 char *c
= strchr(file
, '/');
224 if (c
) l
= c
-file
; else l
= strlen(file
);
227 while (*file
== '/') file
++;
228 while (*curr
== '/') curr
++;
232 if (curr
> buf
&& curr
[-1] != '/')
234 while (*file
&& *file
!= '/')
236 while (*file
== '/') *file
++;
239 pl
= patch_add_file(pl
, np
, strdup(buf
),
245 struct plist
*sort_patches(struct plist
*pl
, int *np
)
247 /* sort the patches, add directory names, and re-sort */
253 qsort(pl
, *np
, sizeof(struct plist
), pl_cmp
);
257 pl
= add_dir(pl
, np
, pl
[i
].file
, curr
);
259 qsort(pl
, *np
, sizeof(struct plist
), pl_cmp
);
261 /* array is now stable, so set up parent pointers */
266 for (i
=0; i
<n
; i
++) {
267 int d
= common_depth(prev
, pl
[i
].file
);
271 pl
[i
].parent
= parents
[d
-1];
272 pl
[pl
[i
].parent
].last
= i
;
274 pl
[i
].prev
= prevnode
[d
];
276 pl
[pl
[i
].prev
].next
= i
;
285 int get_prev(int pos
, struct plist
*pl
, int n
)
287 if (pos
== -1) return pos
;
288 if (pl
[pos
].prev
== -1)
289 return pl
[pos
].parent
;
291 while (pl
[pos
].open
&&
297 int get_next(int pos
, struct plist
*pl
, int n
)
299 if (pos
== -1) return pos
;
306 while (pos
>= 0 && pl
[pos
].next
== -1)
307 pos
= pl
[pos
].parent
;
313 void draw_one(int row
, struct plist
*pl
)
325 else sprintf(hdr
, "%02d", pl
->chunks
);
326 if (pl
->wiggles
> 99)
328 else sprintf(hdr
+2, " %02d", pl
->wiggles
);
329 if (pl
->conflicts
> 99)
331 else sprintf(hdr
+5, " %02d ", pl
->conflicts
);
336 else strcpy(hdr
+9, "- ");
338 mvaddstr(row
, 0, hdr
);
339 mvaddstr(row
, 11, pl
->file
);
343 void addword(struct elmnt e
)
345 addnstr(e
.start
, e
.len
);
348 void diff_window(struct plist
*p
, FILE *f
)
351 * I wonder what to display here ....
354 struct stream s1
, s2
;
359 s
= load_segment(f
, p
->start
, p
->end
);
360 ch
= split_patch(s
, &s1
, &s2
);
363 sprintf(buf
, "Chunk count: %d\n", ch
);
364 mvaddstr(1,1,buf
); clrtoeol();
367 f1
= split_stream(s1
, ByWord
, 0);
368 f2
= split_stream(s2
, ByWord
, 0);
372 /* now try to display the diff highlighted */
376 while(a
<f1
.elcnt
|| b
< f2
.elcnt
) {
380 /* if we remove a whole line, output +line,
381 * else clear sol and retry
384 for (a1
=a
; a1
<csl
->a
; a1
++)
385 if (f1
.list
[a1
].start
[0] == '\n') {
392 for (; a
<csl
->a
; a
++) {
394 if (f1
.list
[a
].start
[0] == '\n') {
399 attroff(A_UNDERLINE
);
406 attroff(A_UNDERLINE
);
411 sol
= (f1
.list
[a
].start
[0] == '\n');
413 } while (a
< csl
->a
);
414 attroff(A_UNDERLINE
);
418 } else if (b
< csl
->b
) {
422 for (b1
=b
; b1
<csl
->b
; b1
++)
423 if (f2
.list
[b1
].start
[0] == '\n') {
430 for (; b
<csl
->b
; b
++) {
432 if (f2
.list
[b
].start
[0] == '\n') {
449 sol
= (f2
.list
[b
].start
[0] == '\n');
451 } while (b
< csl
->b
);
460 for (a1
=a
; a1
<csl
->a
+csl
->len
; a1
++)
461 if (f1
.list
[a1
].start
[0] == '\n')
464 if (f1
.list
[a
].start
[0]) {
466 for (; a
< csl
->a
+csl
->len
; a
++,b
++) {
468 if (f1
.list
[a
].start
[0]=='\n') {
481 if (f1
.list
[a
].start
[0] == '\n')
486 if (a
>= csl
->a
+csl
->len
)
500 void main_window(struct plist
*pl
, int n
, FILE *f
)
502 /* The main window lists all files together with summary information:
503 * number of chunks, number of wiggles, number of conflicts.
504 * The list is scrollable
505 * When a entry is 'selected', we switch to the 'file' window
506 * The list can be condensed by removing files with no conflict
507 * or no wiggles, or removing subdirectories
509 * We record which file in the list is 'current', and which
510 * screen line it is on. We try to keep things stable while
513 * Counts are printed before the name using at most 2 digits.
514 * Numbers greater than 99 are XX
516 * 27 5 1 drivers/md/md.c
518 * A directory show the sum in all children.
521 * select: enter, space, mouseclick
522 * on file, go to file window
523 * on directory, toggle open
524 * up: k, p, control-p uparrow
525 * Move to previous open object
526 * down: j, n, control-n, downarrow
527 * Move to next open object
530 int pos
=0; /* position in file */
531 int row
=1; /* position on screen */
532 int rows
; /* size of screen in rows */
542 mvaddstr(0,0,"Ch Wi Co Patched Files");
547 if (row
<1 || row
>= rows
)
551 getmaxyx(stdscr
, rows
, cols
);
557 for (i
=row
; i
>1; i
--) {
558 tpos
= get_prev(tpos
, pl
, n
);
564 /* Ok, row and pos could be trustworthy now */
566 for (i
=row
; i
>=1; i
--) {
567 draw_one(i
, &pl
[tpos
]);
568 tpos
= get_prev(tpos
, pl
, n
);
571 for (i
=row
+1; i
<rows
; i
++) {
572 tpos
= get_next(tpos
, pl
, n
);
574 draw_one(i
, &pl
[tpos
]);
587 tpos
= get_next(pos
, pl
, n
);
598 tpos
= get_prev(pos
, pl
, n
);
607 if (pl
[pos
].end
== 0) {
608 pl
[pos
].open
= ! pl
[pos
].open
;
611 diff_window(&pl
[pos
], f
);
615 case 27: /* escape */
623 int main(int argc
, char *argv
[])
631 f
= fopen(argv
[argc
-1], "w+");
633 in
= fopen(argv
[1], "r");
635 printf("no arg...\n");
639 pl
= parse_patch(in
, f
, &n
);
640 pl
= sort_patches(pl
, &n
);
648 for (i
=0; i
<n
; i
++) {
649 printf("%3d: %3d %2d/%2d %s\n", i
, pl
[i
].parent
, pl
[i
].prev
, pl
[i
].next
, pl
[i
].file
);
653 signal(SIGINT
, catch);
654 signal(SIGQUIT
, catch);
655 signal(SIGBUS
, catch);
656 signal(SIGTERM
, catch);
657 signal(SIGSEGV
, catch);
659 initscr(); cbreak(); noecho();
660 nonl(); intrflush(stdscr
, FALSE
); keypad(stdscr
, TRUE
);
661 mousemask(ALL_MOUSE_EVENTS
, NULL
);
663 main_window(pl
, n
, in
);
665 nocbreak();nl();endwin();