Release 0.8
[wiggle/upstream.git] / demo.orig / vpatch.c
blobd3bd584d2324105b14d5736f0b452b078603ef20
2 /*
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
9 * in different colours
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.
25 #include "wiggle.h"
26 #include <malloc.h>
27 #include <string.h>
28 #include <curses.h>
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <signal.h>
33 #define assert(x) do { if (!(x)) abort(); } while (0)
35 struct plist {
36 char *file;
37 unsigned int start, end;
38 int parent;
39 int next, prev, last;
40 int open;
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 */
48 int n = *np;
49 int asize;
51 /* printf("adding %s at %d: %u %u\n", file, n, start, end); */
52 if (n==0) asize = 0;
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 */
56 if (asize <= n) {
57 /* need to extend array */
58 struct plist *npl;
59 if (asize < 16) asize = 16;
60 else asize += asize;
61 npl = realloc(pl, asize * sizeof(struct plist));
62 if (!npl) {
63 fprintf(stderr, "malloc failed - skipping %s\n", file);
64 return pl;
66 pl = npl;
68 pl[n].file = file;
69 pl[n].start = start;
70 pl[n].end = end;
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;
73 pl[n].open = 1;
74 *np = n+1;
75 return pl;
80 struct plist *parse_patch(FILE *f, FILE *of, int *np)
82 /* read a multi-file patch from 'f' and record relevant
83 * details in a plist.
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;
89 while (!feof(f)) {
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--- ";
95 char *pos = target;
96 int c;
97 char name[1024];
98 unsigned start, end;
100 while (*pos && (c=fgetc(f)) != EOF ) {
101 if (of) fputc(c, of);
102 if (c == *pos)
103 pos++;
104 else pos = target;
106 if (c == EOF)
107 break;
108 assert(c == ' ');
109 /* now read a file name */
110 pos = name;
111 while ((c=fgetc(f)) != EOF && c != '\t' && c != '\n' && c != ' ' &&
112 pos - name < 1023) {
113 *pos++ = c;
114 if (of) fputc(c, of);
116 *pos = 0;
117 if (c == EOF)
118 break;
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);
125 if (c == EOF) break;
127 /* now skip to end - "\n--- " */
128 pos = target2+1;
130 while (*pos && (c=fgetc(f)) != EOF) {
131 if (of) fputc(c, of);
132 if (c == *pos)
133 pos++;
134 else pos = target2;
136 if (pos > target2) {
137 end = of ? ftell(of) : ftell(f);
138 end -= (pos - target2) - 1;
139 plist = patch_add_file(plist, np,
140 strdup(name), start, end);
143 return plist;
145 void die()
147 fprintf(stderr,"vpatch: fatal error\n");
148 abort();
149 exit(3);
153 static struct stream load_segment(FILE *f,
154 unsigned int start, unsigned int end)
156 struct stream s;
157 s.len = end - start;
158 s.body = malloc(s.len);
159 if (s.body) {
160 fseek(f, start, 0);
161 if (fread(s.body, 1, s.len, f) != s.len) {
162 free(s.body);
163 s.body = NULL;
165 } else
166 die();
167 return s;
171 void catch(int sig)
173 if (sig == SIGINT) {
174 signal(sig, catch);
175 return;
177 nocbreak();nl();endwin();
178 printf("Died on signal %d\n", sig);
179 exit(2);
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
192 * in common
194 int depth = 0;
195 while(1) {
196 char *c;
197 int al, bl;
198 c = strchr(a, '/');
199 if (c) al = c-a; else al = strlen(a);
200 c = strchr(b, '/');
201 if (c) bl = c-b; else bl = strlen(b);
202 if (al == 0 || al != bl || strncmp(a,b,al) != 0)
203 return depth;
204 a+= al;
205 while (*a=='/') a++;
206 b+= bl;
207 while(*b=='/') b++;
209 depth++;
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);
219 char *buf = curr;
220 while (d) {
221 char *c = strchr(file, '/');
222 int l;
223 if (c) l = c-file; else l = strlen(file);
224 file += l;
225 curr += l;
226 while (*file == '/') file++;
227 while (*curr == '/') curr++;
228 d--;
230 while (*file) {
231 if (curr > buf && curr[-1] != '/')
232 *curr++ = '/';
233 while (*file && *file != '/')
234 *curr++ = *file++;
235 while (*file == '/') *file++;
236 *curr = '\0';
237 if (*file)
238 pl = patch_add_file(pl, np, strdup(buf),
239 0, 0);
241 return pl;
244 struct plist *sort_patches(struct plist *pl, int *np)
246 /* sort the patches, add directory names, and re-sort */
247 char curr[1024];
248 char *prev;
249 int parents[100];
250 int prevnode[100];
251 int i, n;
252 qsort(pl, *np, sizeof(struct plist), pl_cmp);
253 curr[0] = 0;
254 n = *np;
255 for (i=0; i<n; i++)
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 */
261 n = *np;
262 curr[0] = 0;
263 prevnode[0] = -1;
264 prev = "";
265 for (i=0; i<n; i++) {
266 int d = common_depth(prev, pl[i].file);
267 if (d == 0)
268 pl[i].parent = -1;
269 else {
270 pl[i].parent = parents[d-1];
271 pl[pl[i].parent].last = i;
273 pl[i].prev = prevnode[d];
274 if (pl[i].prev > -1)
275 pl[pl[i].prev].next = i;
276 prev = pl[i].file;
277 parents[d] = i;
278 prevnode[d] = i;
279 prevnode[d+1] = -1;
281 return pl;
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;
289 pos = pl[pos].prev;
290 while (pl[pos].open &&
291 pl[pos].last >= 0)
292 pos = pl[pos].last;
293 return pos;
296 int get_next(int pos, struct plist *pl, int n)
298 if (pos == -1) return pos;
299 if (pl[pos].open) {
300 if (pos +1 < n)
301 return pos+1;
302 else
303 return -1;
305 while (pos >= 0 && pl[pos].next == -1)
306 pos = pl[pos].parent;
307 if (pos >= 0)
308 pos = pl[pos].next;
309 return pos;
312 void draw_one(int row, struct plist *pl)
314 char hdr[10];
315 hdr[0] = 0;
317 if (pl == NULL) {
318 move(row,0);
319 clrtoeol();
320 return;
322 if (pl->chunks > 99)
323 strcpy(hdr, "XX");
324 else sprintf(hdr, "%02d", pl->chunks);
325 if (pl->wiggles > 99)
326 strcpy(hdr, " XX");
327 else sprintf(hdr+2, " %02d", pl->wiggles);
328 if (pl->conflicts > 99)
329 strcpy(hdr, " XX");
330 else sprintf(hdr+5, " %02d ", pl->conflicts);
331 if (pl->end)
332 strcpy(hdr+9, "= ");
333 else if (pl->open)
334 strcpy(hdr+9, "+ ");
335 else strcpy(hdr+9, "- ");
337 mvaddstr(row, 0, hdr);
338 mvaddstr(row, 11, pl->file);
339 clrtoeol();
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 ....
352 struct stream s;
353 struct stream s1, s2;
354 struct file f1, f2;
355 struct csl *csl;
356 char buf[100];
357 int ch;
358 s = load_segment(f, p->start, p->end);
359 ch = split_patch(s, &s1, &s2);
361 clear();
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);
369 csl = diff(f1, f2);
371 /* now try to display the diff highlighted */
372 int sol = 1;
373 int a=0, b=0;
375 while(a<f1.elcnt || b < f2.elcnt) {
376 if (a < csl->a) {
377 if (sol) {
378 int a1;
379 /* if we remove a whole line, output +line,
380 * else clear sol and retry
382 sol = 0;
383 for (a1=a; a1<csl->a; a1++)
384 if (f1.list[a1].start[0] == '\n') {
385 sol = 1;
386 break;
388 if (sol) {
389 addch('-');
390 attron(A_UNDERLINE);
391 for (; a<csl->a; a++) {
392 addword(f1.list[a]);
393 if (f1.list[a].start[0] == '\n') {
394 a++;
395 break;
398 attroff(A_UNDERLINE);
399 } else addch('|');
401 if (!sol) {
402 attron(A_UNDERLINE);
403 do {
404 if (sol) {
405 attroff(A_UNDERLINE);
406 addch('|');
407 attron(A_UNDERLINE);
409 addword(f1.list[a]);
410 sol = (f1.list[a].start[0] == '\n');
411 a++;
412 } while (a < csl->a);
413 attroff(A_UNDERLINE);
414 if (sol) addch('|');
415 sol = 0;
417 } else if (b < csl->b) {
418 if (sol) {
419 int b1;
420 sol = 0;
421 for (b1=b; b1<csl->b; b1++)
422 if (f2.list[b1].start[0] == '\n') {
423 sol = 1;
424 break;
426 if (sol) {
427 addch('+');
428 attron(A_BOLD);
429 for (; b<csl->b; b++) {
430 addword(f2.list[b]);
431 if (f2.list[b].start[0] == '\n') {
432 b++;
433 break;
436 attroff(A_BOLD);
437 } else addch('|');
439 if (!sol) {
440 attron(A_BOLD);
441 do {
442 if (sol) {
443 attroff(A_BOLD);
444 addch('|');
445 attron(A_BOLD);
447 addword(f2.list[b]);
448 sol = (f2.list[b].start[0] == '\n');
449 b++;
450 } while (b < csl->b);
451 attroff(A_BOLD);
452 if (sol) addch('|');
453 sol = 0;
455 } else {
456 if (sol) {
457 int a1;
458 sol = 0;
459 for (a1=a; a1<csl->a+csl->len; a1++)
460 if (f1.list[a1].start[0] == '\n')
461 sol = 1;
462 if (sol) {
463 if (f1.list[a].start[0]) {
464 addch(' ');
465 for (; a< csl->a+csl->len; a++,b++) {
466 addword(f1.list[a]);
467 if (f1.list[a].start[0]=='\n') {
468 a++,b++;
469 break;
472 } else {
473 addstr("SEP\n");
474 a++; b++;
476 } else addch('|');
478 if (!sol) {
479 addword(f1.list[a]);
480 if (f1.list[a].start[0] == '\n')
481 sol = 1;
482 a++;
483 b++;
485 if (a >= csl->a+csl->len)
486 csl++;
491 getch();
493 free(s1.body);
494 free(s2.body);
495 free(f1.list);
496 free(f2.list);
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
510 * moving.
512 * Counts are printed before the name using at most 2 digits.
513 * Numbers greater than 99 are XX
514 * Ch Wi Co File
515 * 27 5 1 drivers/md/md.c
517 * A directory show the sum in all children.
519 * Commands:
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 */
532 int cols;
533 int tpos, i;
534 int refresh = 2;
535 int c;
537 while(1) {
538 if (refresh == 2) {
539 clear();
540 attron(A_BOLD);
541 mvaddstr(0,0,"Ch Wi Co Patched Files");
542 move(2,0);
543 attroff(A_BOLD);
544 refresh = 1;
546 if (row <1 || row >= rows)
547 refresh = 1;
548 if (refresh) {
549 refresh = 0;
550 getmaxyx(stdscr, rows, cols);
551 if (row >= rows +3)
552 row = (rows+1)/2;
553 if (row >= rows)
554 row = rows-1;
555 tpos = pos;
556 for (i=row; i>1; i--) {
557 tpos = get_prev(tpos, pl, n);
558 if (tpos == -1) {
559 row = row - i + 1;
560 break;
563 /* Ok, row and pos could be trustworthy now */
564 tpos = pos;
565 for (i=row; i>=1; i--) {
566 draw_one(i, &pl[tpos]);
567 tpos = get_prev(tpos, pl, n);
569 tpos = pos;
570 for (i=row+1; i<rows; i++) {
571 tpos = get_next(tpos, pl, n);
572 if (tpos >= 0)
573 draw_one(i, &pl[tpos]);
574 else
575 draw_one(i, NULL);
578 move(row, 9);
579 c = getch();
580 switch(c) {
581 case 'j':
582 case 'n':
583 case 'N':
584 case 'N'-64:
585 case KEY_DOWN:
586 tpos = get_next(pos, pl, n);
587 if (tpos >= 0) {
588 pos = tpos;
589 row++;
591 break;
592 case 'k':
593 case 'p':
594 case 'P':
595 case 'P'-64:
596 case KEY_UP:
597 tpos = get_prev(pos, pl, n);
598 if (tpos >= 0) {
599 pos = tpos;
600 row--;
602 break;
604 case ' ':
605 case 13:
606 if (pl[pos].end == 0) {
607 pl[pos].open = ! pl[pos].open;
608 refresh = 1;
609 } else {
610 diff_window(&pl[pos], f);
611 refresh = 2;
613 break;
614 case 27: /* escape */
615 case 'q':
616 return;
622 int main(int argc, char *argv[])
624 int n = 0;
625 FILE *f = NULL;
626 FILE *in = stdin;
627 struct plist *pl;
629 if (argc == 3)
630 f = fopen(argv[argc-1], "w+");
631 if (argc >=2)
632 in = fopen(argv[1], "r");
633 else {
634 printf("no arg...\n");
635 exit(2);
638 pl = parse_patch(in, f, &n);
639 pl = sort_patches(pl, &n);
641 if (f) {
642 fclose(in);
643 in = f;
645 #if 0
646 int i;
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);
650 exit(0);
651 #endif
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();
665 return 0;