Disable *all* backups when --no-backups used
[wiggle/upstream.git] / demo.patched / vpatch.c
blob1746ab0cefc2b30e33083478cde5f729ce7a22c4
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 horizontally or vertically and
18 * two different views displayed. They will have different
19 * parts missing
21 * So a display of NORMAL, underline, standout|underline reverse
22 * should show a normal patch.
26 #include "wiggle.h"
27 #include <malloc.h>
28 #include <string.h>
29 #include <curses.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32 #include <signal.h>
34 #define assert(x) do { if (!(x)) abort(); } while (0)
36 struct plist {
37 char *file;
38 unsigned int start, end;
39 int parent;
40 int next, prev, last;
41 int open;
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 */
49 int n = *np;
50 int asize;
52 /* printf("adding %s at %d: %u %u\n", file, n, start, end); */
53 if (n==0) asize = 0;
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 */
57 if (asize <= n) {
58 /* need to extend array */
59 struct plist *npl;
60 if (asize < 16) asize = 16;
61 else asize += asize;
62 npl = realloc(pl, asize * sizeof(struct plist));
63 if (!npl) {
64 fprintf(stderr, "malloc failed - skipping %s\n", file);
65 return pl;
67 pl = npl;
69 pl[n].file = file;
70 pl[n].start = start;
71 pl[n].end = end;
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;
74 pl[n].open = 1;
75 *np = n+1;
76 return pl;
81 struct plist *parse_patch(FILE *f, FILE *of, int *np)
83 /* read a multi-file patch from 'f' and record relevant
84 * details in a plist.
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;
90 while (!feof(f)) {
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--- ";
96 char *pos = target;
97 int c;
98 char name[1024];
99 unsigned start, end;
101 while (*pos && (c=fgetc(f)) != EOF ) {
102 if (of) fputc(c, of);
103 if (c == *pos)
104 pos++;
105 else pos = target;
107 if (c == EOF)
108 break;
109 assert(c == ' ');
110 /* now read a file name */
111 pos = name;
112 while ((c=fgetc(f)) != EOF && c != '\t' && c != '\n' && c != ' ' &&
113 pos - name < 1023) {
114 *pos++ = c;
115 if (of) fputc(c, of);
117 *pos = 0;
118 if (c == EOF)
119 break;
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);
126 if (c == EOF) break;
128 /* now skip to end - "\n--- " */
129 pos = target2+1;
131 while (*pos && (c=fgetc(f)) != EOF) {
132 if (of) fputc(c, of);
133 if (c == *pos)
134 pos++;
135 else pos = target2;
137 if (pos > target2) {
138 end = of ? ftell(of) : ftell(f);
139 end -= (pos - target2) - 1;
140 plist = patch_add_file(plist, np,
141 strdup(name), start, end);
144 return plist;
146 void die()
148 fprintf(stderr,"vpatch: fatal error\n");
149 abort();
150 exit(3);
154 static struct stream load_segment(FILE *f,
155 unsigned int start, unsigned int end)
157 struct stream s;
158 s.len = end - start;
159 s.body = malloc(s.len);
160 if (s.body) {
161 fseek(f, start, 0);
162 if (fread(s.body, 1, s.len, f) != s.len) {
163 free(s.body);
164 s.body = NULL;
166 } else
167 die();
168 return s;
172 void catch(int sig)
174 if (sig == SIGINT) {
175 signal(sig, catch);
176 return;
178 nocbreak();nl();endwin();
179 printf("Died on signal %d\n", sig);
180 exit(2);
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
193 * in common
195 int depth = 0;
196 while(1) {
197 char *c;
198 int al, bl;
199 c = strchr(a, '/');
200 if (c) al = c-a; else al = strlen(a);
201 c = strchr(b, '/');
202 if (c) bl = c-b; else bl = strlen(b);
203 if (al == 0 || al != bl || strncmp(a,b,al) != 0)
204 return depth;
205 a+= al;
206 while (*a=='/') a++;
207 b+= bl;
208 while(*b=='/') b++;
210 depth++;
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);
220 char *buf = curr;
221 while (d) {
222 char *c = strchr(file, '/');
223 int l;
224 if (c) l = c-file; else l = strlen(file);
225 file += l;
226 curr += l;
227 while (*file == '/') file++;
228 while (*curr == '/') curr++;
229 d--;
231 while (*file) {
232 if (curr > buf && curr[-1] != '/')
233 *curr++ = '/';
234 while (*file && *file != '/')
235 *curr++ = *file++;
236 while (*file == '/') *file++;
237 *curr = '\0';
238 if (*file)
239 pl = patch_add_file(pl, np, strdup(buf),
240 0, 0);
242 return pl;
245 struct plist *sort_patches(struct plist *pl, int *np)
247 /* sort the patches, add directory names, and re-sort */
248 char curr[1024];
249 char *prev;
250 int parents[100];
251 int prevnode[100];
252 int i, n;
253 qsort(pl, *np, sizeof(struct plist), pl_cmp);
254 curr[0] = 0;
255 n = *np;
256 for (i=0; i<n; i++)
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 */
262 n = *np;
263 curr[0] = 0;
264 prevnode[0] = -1;
265 prev = "";
266 for (i=0; i<n; i++) {
267 int d = common_depth(prev, pl[i].file);
268 if (d == 0)
269 pl[i].parent = -1;
270 else {
271 pl[i].parent = parents[d-1];
272 pl[pl[i].parent].last = i;
274 pl[i].prev = prevnode[d];
275 if (pl[i].prev > -1)
276 pl[pl[i].prev].next = i;
277 prev = pl[i].file;
278 parents[d] = i;
279 prevnode[d] = i;
280 prevnode[d+1] = -1;
282 return pl;
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;
290 pos = pl[pos].prev;
291 while (pl[pos].open &&
292 pl[pos].last >= 0)
293 pos = pl[pos].last;
294 return pos;
297 int get_next(int pos, struct plist *pl, int n)
299 if (pos == -1) return pos;
300 if (pl[pos].open) {
301 if (pos +1 < n)
302 return pos+1;
303 else
304 return -1;
306 while (pos >= 0 && pl[pos].next == -1)
307 pos = pl[pos].parent;
308 if (pos >= 0)
309 pos = pl[pos].next;
310 return pos;
313 void draw_one(int row, struct plist *pl)
315 char hdr[10];
316 hdr[0] = 0;
318 if (pl == NULL) {
319 move(row,0);
320 clrtoeol();
321 return;
323 if (pl->chunks > 99)
324 strcpy(hdr, "XX");
325 else sprintf(hdr, "%02d", pl->chunks);
326 if (pl->wiggles > 99)
327 strcpy(hdr, " XX");
328 else sprintf(hdr+2, " %02d", pl->wiggles);
329 if (pl->conflicts > 99)
330 strcpy(hdr, " XX");
331 else sprintf(hdr+5, " %02d ", pl->conflicts);
332 if (pl->end)
333 strcpy(hdr+9, "= ");
334 else if (pl->open)
335 strcpy(hdr+9, "+ ");
336 else strcpy(hdr+9, "- ");
338 mvaddstr(row, 0, hdr);
339 mvaddstr(row, 11, pl->file);
340 clrtoeol();
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 ....
353 struct stream s;
354 struct stream s1, s2;
355 struct file f1, f2;
356 struct csl *csl;
357 char buf[100];
358 int ch;
359 s = load_segment(f, p->start, p->end);
360 ch = split_patch(s, &s1, &s2);
362 clear();
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);
370 csl = diff(f1, f2);
372 /* now try to display the diff highlighted */
373 int sol = 1;
374 int a=0, b=0;
376 while(a<f1.elcnt || b < f2.elcnt) {
377 if (a < csl->a) {
378 if (sol) {
379 int a1;
380 /* if we remove a whole line, output +line,
381 * else clear sol and retry
383 sol = 0;
384 for (a1=a; a1<csl->a; a1++)
385 if (f1.list[a1].start[0] == '\n') {
386 sol = 1;
387 break;
389 if (sol) {
390 addch('-');
391 attron(A_UNDERLINE);
392 for (; a<csl->a; a++) {
393 addword(f1.list[a]);
394 if (f1.list[a].start[0] == '\n') {
395 a++;
396 break;
399 attroff(A_UNDERLINE);
400 } else addch('|');
402 if (!sol) {
403 attron(A_UNDERLINE);
404 do {
405 if (sol) {
406 attroff(A_UNDERLINE);
407 addch('|');
408 attron(A_UNDERLINE);
410 addword(f1.list[a]);
411 sol = (f1.list[a].start[0] == '\n');
412 a++;
413 } while (a < csl->a);
414 attroff(A_UNDERLINE);
415 if (sol) addch('|');
416 sol = 0;
418 } else if (b < csl->b) {
419 if (sol) {
420 int b1;
421 sol = 0;
422 for (b1=b; b1<csl->b; b1++)
423 if (f2.list[b1].start[0] == '\n') {
424 sol = 1;
425 break;
427 if (sol) {
428 addch('+');
429 attron(A_BOLD);
430 for (; b<csl->b; b++) {
431 addword(f2.list[b]);
432 if (f2.list[b].start[0] == '\n') {
433 b++;
434 break;
437 attroff(A_BOLD);
438 } else addch('|');
440 if (!sol) {
441 attron(A_BOLD);
442 do {
443 if (sol) {
444 attroff(A_BOLD);
445 addch('|');
446 attron(A_BOLD);
448 addword(f2.list[b]);
449 sol = (f2.list[b].start[0] == '\n');
450 b++;
451 } while (b < csl->b);
452 attroff(A_BOLD);
453 if (sol) addch('|');
454 sol = 0;
456 } else {
457 if (sol) {
458 int a1;
459 sol = 0;
460 for (a1=a; a1<csl->a+csl->len; a1++)
461 if (f1.list[a1].start[0] == '\n')
462 sol = 1;
463 if (sol) {
464 if (f1.list[a].start[0]) {
465 addch(' ');
466 for (; a< csl->a+csl->len; a++,b++) {
467 addword(f1.list[a]);
468 if (f1.list[a].start[0]=='\n') {
469 a++,b++;
470 break;
473 } else {
474 addstr("SEP\n");
475 a++; b++;
477 } else addch('|');
479 if (!sol) {
480 addword(f1.list[a]);
481 if (f1.list[a].start[0] == '\n')
482 sol = 1;
483 a++;
484 b++;
486 if (a >= csl->a+csl->len)
487 csl++;
492 getch();
494 free(s1.body);
495 free(s2.body);
496 free(f1.list);
497 free(f2.list);
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
511 * moving.
513 * Counts are printed before the name using at most 2 digits.
514 * Numbers greater than 99 are XX
515 * Ch Wi Co File
516 * 27 5 1 drivers/md/md.c
518 * A directory show the sum in all children.
520 * Commands:
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 */
533 int cols;
534 int tpos, i;
535 int refresh = 2;
536 int c;
538 while(1) {
539 if (refresh == 2) {
540 clear();
541 attron(A_BOLD);
542 mvaddstr(0,0,"Ch Wi Co Patched Files");
543 move(2,0);
544 attroff(A_BOLD);
545 refresh = 1;
547 if (row <1 || row >= rows)
548 refresh = 1;
549 if (refresh) {
550 refresh = 0;
551 getmaxyx(stdscr, rows, cols);
552 if (row >= rows +3)
553 row = (rows+1)/2;
554 if (row >= rows)
555 row = rows-1;
556 tpos = pos;
557 for (i=row; i>1; i--) {
558 tpos = get_prev(tpos, pl, n);
559 if (tpos == -1) {
560 row = row - i + 1;
561 break;
564 /* Ok, row and pos could be trustworthy now */
565 tpos = pos;
566 for (i=row; i>=1; i--) {
567 draw_one(i, &pl[tpos]);
568 tpos = get_prev(tpos, pl, n);
570 tpos = pos;
571 for (i=row+1; i<rows; i++) {
572 tpos = get_next(tpos, pl, n);
573 if (tpos >= 0)
574 draw_one(i, &pl[tpos]);
575 else
576 draw_one(i, NULL);
579 move(row, 9);
580 c = getch();
581 switch(c) {
582 case 'j':
583 case 'n':
584 case 'N':
585 case 'N'-64:
586 case KEY_DOWN:
587 tpos = get_next(pos, pl, n);
588 if (tpos >= 0) {
589 pos = tpos;
590 row++;
592 break;
593 case 'k':
594 case 'p':
595 case 'P':
596 case 'P'-64:
597 case KEY_UP:
598 tpos = get_prev(pos, pl, n);
599 if (tpos >= 0) {
600 pos = tpos;
601 row--;
603 break;
605 case ' ':
606 case 13:
607 if (pl[pos].end == 0) {
608 pl[pos].open = ! pl[pos].open;
609 refresh = 1;
610 } else {
611 diff_window(&pl[pos], f);
612 refresh = 2;
614 break;
615 case 27: /* escape */
616 case 'q':
617 return;
623 int main(int argc, char *argv[])
625 int n = 0;
626 FILE *f = NULL;
627 FILE *in = stdin;
628 struct plist *pl;
630 if (argc == 3)
631 f = fopen(argv[argc-1], "w+");
632 if (argc >=2)
633 in = fopen(argv[1], "r");
634 else {
635 printf("no arg...\n");
636 exit(2);
639 pl = parse_patch(in, f, &n);
640 pl = sort_patches(pl, &n);
642 if (f) {
643 fclose(in);
644 in = f;
646 #if 0
647 int i;
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);
651 exit(0);
652 #endif
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();
666 return 0;