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/
14 * underlined A_UNDERLINE
15 * - unmatched in file are A_STANDOUT
16 * - unmatched in patch are A_STANDOUT|A_UNDERLINE ???
17 * - inserted are inverse/green ?? A_REVERSE
19 * The window can be split horizontally or vertically and two different
20 * views displayed. They will have different parts missing
22 * So a display of NORMAL, underline, standout|underline reverse
23 * should show a normal patch.
35 #define assert(x) do { if (!(x)) abort(); } while (0)
39 unsigned int start
, end
;
43 int chunks
, wiggles
, conflicts
;
46 struct plist
*patch_add_file(struct plist
*pl
, int *np
, char *file
,
47 unsigned int start
, unsigned int end
)
49 /* size of pl is 0, 16, n^2 */
53 /* printf("adding %s at %d: %u %u\n", file, n, start, end); */
55 else if (n
<=16) asize
= 16;
56 else if ((n
&(n
-1))==0) asize
= n
;
57 else asize
= n
+1; /* not accurate, but not too large */
59 /* need to extend array */
61 if (asize
< 16) asize
= 16;
63 npl
= realloc(pl
, asize
* sizeof(struct plist
));
65 fprintf(stderr
, "malloc failed - skipping %s\n", file
);
73 pl
[n
].last
= pl
[n
].next
= pl
[n
].prev
= pl
[n
].parent
= -1;
74 pl
[n
].chunks
= pl
[n
].wiggles
= pl
[n
].conflicts
= 0;
82 struct plist
*parse_patch(FILE *f
, FILE *of
, int *np
)
84 /* read a multi-file patch from 'f' and record relevant
86 * if 'of' >= 0, fd might not be seekable so we write
87 * to 'of' and use lseek on 'of' to determine position
89 struct plist
*plist
= NULL
;
92 /* first, find the start of a patch: "\n+++ "
93 * grab the file name and scan to the end of a line
95 char *target
="\n+++ ";
96 char *target2
="\n--- ";
102 while (*pos
&& (c
=fgetc(f
)) != EOF
) {
103 if (of
) fputc(c
, of
);
111 /* now read a file name */
113 while ((c
=fgetc(f
)) != EOF
&& c
!= '\t' && c
!= '\n' && c
!= ' ' &&
116 if (of
) fputc(c
, of
);
121 if (of
) fputc(c
, of
);
122 while (c
!= '\n' && (c
=fgetc(f
)) != EOF
) {
123 if (of
) fputc(c
, of
);
125 start
= of
? ftell(of
) : ftell(f
);
129 /* now skip to end - "\n--- " */
132 while (*pos
&& (c
=fgetc(f
)) != EOF
) {
133 if (of
) fputc(c
, of
);
139 end
= of
? ftell(of
) : ftell(f
);
140 end
-= (pos
- target2
) - 1;
141 plist
= patch_add_file(plist
, np
,
142 strdup(name
), start
, end
);
149 fprintf(stderr
,"vpatch: fatal error\n");
155 static struct stream
load_segment(FILE *f
,
156 unsigned int start
, unsigned int end
)
160 s
.body
= malloc(s
.len
);
163 if (fread(s
.body
, 1, s
.len
, f
) != s
.len
) {
179 nocbreak();nl();endwin();
180 printf("Died on signal %d\n", sig
);
184 int pl_cmp(const void *av
, const void *bv
)
186 const struct plist
*a
= av
;
187 const struct plist
*b
= bv
;
188 return strcmp(a
->file
, b
->file
);
191 int common_depth(char *a
, char *b
)
193 /* find number of patch segments that these two have
201 if (c
) al
= c
-a
; else al
= strlen(a
);
203 if (c
) bl
= c
-b
; else bl
= strlen(b
);
204 if (al
== 0 || al
!= bl
|| strncmp(a
,b
,al
) != 0)
215 struct plist
*add_dir(struct plist
*pl
, int *np
, char *file
, char *curr
)
217 /* any parent of file that is not a parent of curr
218 * needs to be added to pl
220 int d
= common_depth(file
, curr
);
223 char *c
= strchr(file
, '/');
225 if (c
) l
= c
-file
; else l
= strlen(file
);
228 while (*file
== '/') file
++;
229 while (*curr
== '/') curr
++;
233 if (curr
> buf
&& curr
[-1] != '/')
235 while (*file
&& *file
!= '/')
237 while (*file
== '/') *file
++;
240 pl
= patch_add_file(pl
, np
, strdup(buf
),
246 struct plist
*sort_patches(struct plist
*pl
, int *np
)
248 /* sort the patches, add directory names, and re-sort */
254 qsort(pl
, *np
, sizeof(struct plist
), pl_cmp
);
258 pl
= add_dir(pl
, np
, pl
[i
].file
, curr
);
260 qsort(pl
, *np
, sizeof(struct plist
), pl_cmp
);
262 /* array is now stable, so set up parent pointers */
267 for (i
=0; i
<n
; i
++) {
268 int d
= common_depth(prev
, pl
[i
].file
);
272 pl
[i
].parent
= parents
[d
-1];
273 pl
[pl
[i
].parent
].last
= i
;
275 pl
[i
].prev
= prevnode
[d
];
277 pl
[pl
[i
].prev
].next
= i
;
286 int get_prev(int pos
, struct plist
*pl
, int n
)
288 if (pos
== -1) return pos
;
289 if (pl
[pos
].prev
== -1)
290 return pl
[pos
].parent
;
292 while (pl
[pos
].open
&&
298 int get_next(int pos
, struct plist
*pl
, int n
)
300 if (pos
== -1) return pos
;
307 while (pos
>= 0 && pl
[pos
].next
== -1)
308 pos
= pl
[pos
].parent
;
314 void draw_one(int row
, struct plist
*pl
)
326 else sprintf(hdr
, "%02d", pl
->chunks
);
327 if (pl
->wiggles
> 99)
329 else sprintf(hdr
+2, " %02d", pl
->wiggles
);
330 if (pl
->conflicts
> 99)
332 else sprintf(hdr
+5, " %02d ", pl
->conflicts
);
337 else strcpy(hdr
+9, "- ");
339 mvaddstr(row
, 0, hdr
);
340 mvaddstr(row
, 11, pl
->file
);
344 void addword(struct elmnt e
)
346 addnstr(e
.start
, e
.len
);
349 void diff_window(struct plist
*p
, FILE *f
)
352 * I wonder what to display here ....
355 struct stream s1
, s2
;
360 s
= load_segment(f
, p
->start
, p
->end
);
361 ch
= split_patch(s
, &s1
, &s2
);
364 sprintf(buf
, "Chunk count: %d\n", ch
);
365 mvaddstr(1,1,buf
); clrtoeol();
368 f1
= split_stream(s1
, ByWord
, 0);
369 f2
= split_stream(s2
, ByWord
, 0);
373 /* now try to display the diff highlighted */
377 while(a
<f1
.elcnt
|| b
< f2
.elcnt
) {
381 /* if we remove a whole line, output +line,
382 * else clear sol and retry
385 for (a1
=a
; a1
<csl
->a
; a1
++)
386 if (f1
.list
[a1
].start
[0] == '\n') {
393 for (; a
<csl
->a
; a
++) {
395 if (f1
.list
[a
].start
[0] == '\n') {
400 attroff(A_UNDERLINE
);
407 attroff(A_UNDERLINE
);
412 sol
= (f1
.list
[a
].start
[0] == '\n');
414 } while (a
< csl
->a
);
415 attroff(A_UNDERLINE
);
419 } else if (b
< csl
->b
) {
423 for (b1
=b
; b1
<csl
->b
; b1
++)
424 if (f2
.list
[b1
].start
[0] == '\n') {
431 for (; b
<csl
->b
; b
++) {
433 if (f2
.list
[b
].start
[0] == '\n') {
450 sol
= (f2
.list
[b
].start
[0] == '\n');
452 } while (b
< csl
->b
);
461 for (a1
=a
; a1
<csl
->a
+csl
->len
; a1
++)
462 if (f1
.list
[a1
].start
[0] == '\n')
465 if (f1
.list
[a
].start
[0]) {
467 for (; a
< csl
->a
+csl
->len
; a
++,b
++) {
469 if (f1
.list
[a
].start
[0]=='\n') {
482 if (f1
.list
[a
].start
[0] == '\n')
487 if (a
>= csl
->a
+csl
->len
)
501 void main_window(struct plist
*pl
, int n
, FILE *f
)
503 /* The main window lists all files together with summary information:
504 * number of chunks, number of wiggles, number of conflicts.
505 * The list is scrollable
506 * When a entry is 'selected', we switch to the 'file' window
507 * The list can be condensed by removing files with no conflict
508 * or no wiggles, or removing subdirectories
510 * We record which file in the list is 'current', and which
511 * screen line it is on. We try to keep things stable while
514 * Counts are printed before the name using at most 2 digits.
515 * Numbers greater than 99 are XX
517 * 27 5 1 drivers/md/md.c
519 * A directory show the sum in all children.
522 * select: enter, space, mouseclick
523 * on file, go to file window
524 * on directory, toggle open
525 * up: k, p, control-p uparrow
526 * Move to previous open object
527 * down: j, n, control-n, downarrow
528 * Move to next open object
531 int pos
=0; /* position in file */
532 int row
=1; /* position on screen */
533 int rows
; /* size of screen in rows */
543 mvaddstr(0,0,"Ch Wi Co Patched Files");
548 if (row
<1 || row
>= rows
)
552 getmaxyx(stdscr
, rows
, cols
);
558 for (i
=row
; i
>1; i
--) {
559 tpos
= get_prev(tpos
, pl
, n
);
565 /* Ok, row and pos could be trustworthy now */
567 for (i
=row
; i
>=1; i
--) {
568 draw_one(i
, &pl
[tpos
]);
569 tpos
= get_prev(tpos
, pl
, n
);
572 for (i
=row
+1; i
<rows
; i
++) {
573 tpos
= get_next(tpos
, pl
, n
);
575 draw_one(i
, &pl
[tpos
]);
588 tpos
= get_next(pos
, pl
, n
);
599 tpos
= get_prev(pos
, pl
, n
);
608 if (pl
[pos
].end
== 0) {
609 pl
[pos
].open
= ! pl
[pos
].open
;
612 diff_window(&pl
[pos
], f
);
616 case 27: /* escape */
624 int main(int argc
, char *argv
[])
632 f
= fopen(argv
[argc
-1], "w+");
634 in
= fopen(argv
[1], "r");
636 printf("no arg...\n");
640 pl
= parse_patch(in
, f
, &n
);
641 pl
= sort_patches(pl
, &n
);
649 for (i
=0; i
<n
; i
++) {
650 printf("%3d: %3d %2d/%2d %s\n", i
, pl
[i
].parent
, pl
[i
].prev
, pl
[i
].next
, pl
[i
].file
);
654 signal(SIGINT
, catch);
655 signal(SIGQUIT
, catch);
656 signal(SIGTERM
, catch);
657 signal(SIGBUS
, catch);
658 signal(SIGSEGV
, catch);
660 initscr(); cbreak(); noecho();
661 nonl(); intrflush(stdscr
, FALSE
); keypad(stdscr
, TRUE
);
662 mousemask(ALL_MOUSE_EVENTS
, NULL
);
664 main_window(pl
, n
, in
);
666 nocbreak();nl();endwin();