Cleanup line matching
[tig] / tig.c
1 /**
2  * TIG(1)
3  * ======
4  *
5  * NAME
6  * ----
7  * tig - text-mode interface for git
8  *
9  * SYNOPSIS
10  * --------
11  * [verse]
12  * tig
13  * tig log  [git log options]
14  * tig diff [git diff options]
15  * tig < [git log or git diff output]
16  *
17  * DESCRIPTION
18  * -----------
19  * Browse changes in a git repository.
20  *
21  * OPTIONS
22  * -------
23  *
24  * None.
25  *
26  **/
27
28 #define DEBUG
29
30 #ifndef DEBUG
31 #define NDEBUG
32 #endif
33
34 #include <assert.h>
35 #include <ctype.h>
36 #include <signal.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include <curses.h>
43 #include <form.h>
44
45 static void die(const char *err, ...);
46 static void report(const char *msg, ...);
47
48 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))
49
50 #define KEY_TAB         9
51 #define KEY_ESC         27
52 #define KEY_DEL         127
53
54 #define REQ_OFFSET      (MAX_COMMAND + 1)
55
56 /* Requests for switching between the different views. */
57 #define REQ_DIFF        (REQ_OFFSET + 0)
58 #define REQ_LOG         (REQ_OFFSET + 1)
59 #define REQ_MAIN        (REQ_OFFSET + 2)
60 #define REQ_VIEWS       (REQ_OFFSET + 3)
61
62 #define REQ_QUIT        (REQ_OFFSET + 11)
63 #define REQ_VERSION     (REQ_OFFSET + 12)
64 #define REQ_STOP        (REQ_OFFSET + 13)
65 #define REQ_UPDATE      (REQ_OFFSET + 14)
66 #define REQ_REDRAW      (REQ_OFFSET + 15)
67 #define REQ_FIRST_LINE  (REQ_OFFSET + 16)
68 #define REQ_LAST_LINE   (REQ_OFFSET + 17)
69
70 #define COLOR_TRANSP    (-1)
71
72
73 enum line_type {
74         LINE_AUTHOR,
75         LINE_COMMIT,
76         LINE_DATE,
77         LINE_DIFF,
78         LINE_DIFF_ADD,
79         LINE_DIFF_DEL,
80         LINE_DIFF_CHUNK,
81         LINE_DIFF_TREE,
82         LINE_INDEX,
83         LINE_PARENT,
84         LINE_TREE,
85
86         LINE_UNKNOWN,
87         LINE_CURSOR,
88         LINE_STATUS,
89         LINE_TITLE,
90 };
91
92 struct line_info {
93         enum line_type type;
94         char *line;
95         int linelen;
96
97         int fg;
98         int bg;
99         int attr;
100 };
101
102 #define LINE(type, line, fg, bg, attr) \
103         { LINE_##type, (line), sizeof(line) - 1, (fg), (bg), (attr) }
104
105 static struct line_info line_info[] = {
106         LINE(AUTHOR,     "Author: ",    COLOR_CYAN,     COLOR_TRANSP,   0),
107         //LINE(AUTHOR,   "author ",     COLOR_CYAN,     COLOR_TRANSP,   0),
108         LINE(COMMIT,     "commit ",     COLOR_GREEN,    COLOR_TRANSP,   0),
109         LINE(DATE,       "Date:   ",    COLOR_YELLOW,   COLOR_TRANSP,   0),
110         LINE(DIFF_ADD,   "+",           COLOR_GREEN,    COLOR_TRANSP,   0),
111         LINE(DIFF_CHUNK, "@",           COLOR_MAGENTA,  COLOR_TRANSP,   0),
112         LINE(DIFF_DEL,   "-",           COLOR_RED,      COLOR_TRANSP,   0),
113         LINE(DIFF,       "diff --git ", COLOR_YELLOW,   COLOR_TRANSP,   0),
114         LINE(DIFF_TREE,  "diff-tree ",  COLOR_BLUE,     COLOR_TRANSP,   0),
115         LINE(INDEX,      "index ",      COLOR_BLUE,     COLOR_TRANSP,   0),
116         LINE(PARENT,     "parent ",     COLOR_GREEN,    COLOR_TRANSP,   0),
117         LINE(TREE,       "tree ",       COLOR_GREEN,    COLOR_TRANSP,   0),
118
119         LINE(UNKNOWN,    "",            COLOR_TRANSP,   COLOR_TRANSP,   A_NORMAL),
120
121         LINE(CURSOR,     "",            COLOR_WHITE,    COLOR_GREEN,    A_BOLD),
122         LINE(STATUS,     "",            COLOR_GREEN,    COLOR_TRANSP,   0),
123         LINE(TITLE,      "",            COLOR_YELLOW,   COLOR_BLUE,     A_BOLD),
124 };
125
126 static struct line_info *
127 get_line_info(char *line)
128 {
129         int linelen = strlen(line);
130         int i;
131
132         for (i = 0; i < ARRAY_SIZE(line_info); i++) {
133                 if (linelen < line_info[i].linelen
134                     || strncmp(line_info[i].line, line, line_info[i].linelen))
135                         continue;
136
137                 return &line_info[i];
138         }
139
140         return NULL;
141 }
142
143 static enum line_type
144 get_line_type(char *line)
145 {
146         struct line_info *info = get_line_info(line);
147
148         return info ? info->type : LINE_UNKNOWN;
149 }
150
151 static int
152 get_line_attr(enum line_type type)
153 {
154         int i;
155
156         for (i = 0; i < ARRAY_SIZE(line_info); i++)
157                 if (line_info[i].type == type)
158                         return COLOR_PAIR(line_info[i].type) | line_info[i].attr;
159
160         return A_NORMAL;
161 }
162
163 static void
164 init_colors(void)
165 {
166         int transparent_bg = COLOR_BLACK;
167         int transparent_fg = COLOR_WHITE;
168         int i;
169
170         start_color();
171
172         if (use_default_colors() != ERR) {
173                 transparent_bg = -1;
174                 transparent_fg = -1;
175         }
176
177         for (i = 0; i < ARRAY_SIZE(line_info); i++) {
178                 struct line_info *info = &line_info[i];
179                 int bg = info->bg == COLOR_TRANSP ? transparent_bg : info->bg;
180                 int fg = info->fg == COLOR_TRANSP ? transparent_fg : info->fg;
181
182                 init_pair(info->type, fg, bg);
183         }
184 }
185
186
187
188
189 /**
190  * KEYS
191  * ----
192  *
193  * d::
194  *      diff
195  * l::
196  *      log
197  * q::
198  *      quit
199  * r::
200  *      redraw screen
201  * s::
202  *      stop all background loading
203  * j::
204  *      down
205  * k::
206  *      up
207  * h, ?::
208  *      help
209  * v::
210  *      version
211  *
212  **/
213
214 #define HELP "(d)iff, (l)og, (m)ain, (q)uit, (v)ersion, (h)elp"
215
216 struct keymap {
217         int alias;
218         int request;
219 };
220
221 struct keymap keymap[] = {
222         /* Cursor navigation */
223         { KEY_UP,       REQ_PREV_LINE },
224         { 'k',          REQ_PREV_LINE },
225         { KEY_DOWN,     REQ_NEXT_LINE },
226         { 'j',          REQ_NEXT_LINE },
227         { KEY_HOME,     REQ_FIRST_LINE },
228         { KEY_END,      REQ_LAST_LINE },
229         { KEY_NPAGE,    REQ_NEXT_PAGE },
230         { KEY_PPAGE,    REQ_PREV_PAGE },
231
232         /* Scrolling */
233         { KEY_IC,       REQ_SCR_BLINE }, /* scroll field backward a line */
234         { KEY_DC,       REQ_SCR_FLINE }, /* scroll field forward a line */
235         { 's',          REQ_SCR_FPAGE }, /* scroll field forward a page */
236         { 'w',          REQ_SCR_BPAGE }, /* scroll field backward a page */
237
238         { 'd',          REQ_DIFF },
239         { 'l',          REQ_LOG },
240         { 'm',          REQ_MAIN },
241
242         /* No input from wgetch() with nodelay() enabled. */
243         { ERR,          REQ_UPDATE },
244
245         { KEY_ESC,      REQ_QUIT },
246         { 'q',          REQ_QUIT },
247         { 's',          REQ_STOP },
248         { 'v',          REQ_VERSION },
249         { 'r',          REQ_REDRAW },
250 };
251
252 static int
253 get_request(int request)
254 {
255         int i;
256
257         for (i = 0; i < ARRAY_SIZE(keymap); i++)
258                 if (keymap[i].alias == request)
259                         return keymap[i].request;
260
261         return request;
262 }
263
264
265 /*
266  * Viewer
267  */
268
269 struct view {
270         char *name;
271         char *cmd;
272         char *id;
273
274
275         /* Rendering */
276         int (*read)(struct view *, char *);
277         int (*draw)(struct view *, unsigned int);
278         size_t objsize;         /* Size of objects in the line index */
279
280         WINDOW *win;
281         int height, width;
282
283         /* Navigation */
284         unsigned long offset;   /* Offset of the window top */
285         unsigned long lineno;   /* Current line number */
286
287         /* Buffering */
288         unsigned long lines;    /* Total number of lines */
289         void **line;            /* Line index */
290
291         /* Loading */
292         FILE *pipe;
293 };
294
295 struct commit {
296         char id[41];
297         char title[75];
298 };
299
300 static int pager_draw(struct view *view, unsigned int lineno);
301 static int pager_read(struct view *view, char *line);
302
303 static int main_draw(struct view *view, unsigned int lineno);
304 static int main_read(struct view *view, char *line);
305
306 #define DIFF_CMD \
307         "git log --stat -n1 %s ; echo; " \
308         "git diff --find-copies-harder -B -C %s^ %s"
309
310 #define LOG_CMD \
311         "git log --stat -n100 %s"
312
313 #define MAIN_CMD \
314         "git log --stat --pretty=raw %s"
315
316 /* The status window at the bottom. Used for polling keystrokes. */
317 static WINDOW *status_win;
318
319 static WINDOW *title_win;
320
321 #define SIZEOF_ID       1024
322 #define SIZEOF_VIEWS    (REQ_VIEWS - REQ_OFFSET)
323
324 char head_id[SIZEOF_ID] = "HEAD";
325 char commit_id[SIZEOF_ID] = "HEAD";
326
327 static unsigned int current_view;
328 static unsigned int nloading;
329
330 static struct view views[];
331 static struct view *display[];
332
333 static struct view views[] = {
334         { "diff",  DIFF_CMD,   commit_id,  pager_read,  pager_draw, sizeof(char) },
335         { "log",   LOG_CMD,    head_id,    pager_read,  pager_draw, sizeof(char) },
336         { "main",  MAIN_CMD,   head_id,    main_read,   main_draw,  sizeof(struct commit) },
337 };
338
339 static struct view *display[ARRAY_SIZE(views)];
340
341
342 #define foreach_view(view, i) \
343         for (i = 0; i < sizeof(display) && (view = display[i]); i++)
344
345 static void
346 redraw_view(struct view *view)
347 {
348         int lineno;
349
350         wclear(view->win);
351         wmove(view->win, 0, 0);
352
353         for (lineno = 0; lineno < view->height; lineno++) {
354                 if (!view->draw(view, lineno))
355                         break;
356         }
357
358         redrawwin(view->win);
359         wrefresh(view->win);
360 }
361
362 /* FIXME: Fix percentage. */
363 static void
364 report_position(struct view *view, int all)
365 {
366         report(all ? "line %d of %d (%d%%) viewing from %d"
367                      : "line %d of %d",
368                view->lineno + 1,
369                view->lines,
370                view->lines ? view->offset * 100 / view->lines : 0,
371                view->offset);
372 }
373
374
375 static void
376 move_view(struct view *view, int lines)
377 {
378         /* The rendering expects the new offset. */
379         view->offset += lines;
380
381         assert(0 <= view->offset && view->offset < view->lines);
382         assert(lines);
383
384         {
385                 int line = lines > 0 ? view->height - lines : 0;
386                 int end = line + (lines > 0 ? lines : -lines);
387
388                 wscrl(view->win, lines);
389
390                 for (; line < end; line++) {
391                         if (!view->draw(view, line))
392                                 break;
393                 }
394         }
395
396         /* Move current line into the view. */
397         if (view->lineno < view->offset) {
398                 view->lineno = view->offset;
399                 view->draw(view, 0);
400
401         } else if (view->lineno >= view->offset + view->height) {
402                 view->lineno = view->offset + view->height - 1;
403                 view->draw(view, view->lineno - view->offset);
404         }
405
406         assert(view->offset <= view->lineno && view->lineno <= view->lines);
407
408         redrawwin(view->win);
409         wrefresh(view->win);
410
411         report_position(view, lines);
412 }
413
414 static void
415 scroll_view(struct view *view, int request)
416 {
417         int lines = 1;
418
419         switch (request) {
420         case REQ_SCR_FPAGE:
421                 lines = view->height;
422         case REQ_SCR_FLINE:
423                 if (view->offset + lines > view->lines)
424                         lines = view->lines - view->offset;
425
426                 if (lines == 0 || view->offset + view->height >= view->lines) {
427                         report("already at last line");
428                         return;
429                 }
430                 break;
431
432         case REQ_SCR_BPAGE:
433                 lines = view->height;
434         case REQ_SCR_BLINE:
435                 if (lines > view->offset)
436                         lines = view->offset;
437
438                 if (lines == 0) {
439                         report("already at first line");
440                         return;
441                 }
442
443                 lines = -lines;
444                 break;
445         }
446
447         move_view(view, lines);
448 }
449
450
451 static void
452 navigate_view(struct view *view, int request)
453 {
454         int steps;
455
456         switch (request) {
457         case REQ_FIRST_LINE:
458                 steps = -view->lineno;
459                 break;
460
461         case REQ_LAST_LINE:
462                 steps = view->lines - view->lineno - 1;
463                 break;
464
465         case REQ_PREV_PAGE:
466                 steps = view->height > view->lineno
467                       ? -view->lineno : -view->height;
468                 break;
469
470         case REQ_NEXT_PAGE:
471                 steps = view->lineno + view->height >= view->lines
472                       ? view->lines - view->lineno - 1 : view->height;
473                 break;
474
475         case REQ_PREV_LINE:
476                 steps = -1;
477                 break;
478
479         case REQ_NEXT_LINE:
480                 steps = 1;
481                 break;
482         }
483
484         if (steps < 0 && view->lineno == 0) {
485                 report("already at first line");
486                 return;
487
488         } else if (steps > 0 && view->lineno + 1 >= view->lines) {
489                 report("already at last line");
490                 return;
491         }
492
493         view->lineno += steps;
494         view->draw(view, view->lineno - steps - view->offset);
495
496         if (view->lineno < view->offset ||
497             view->lineno >= view->offset + view->height) {
498                 if (steps < 0 && -steps > view->offset) {
499                         steps = -view->offset;
500                 } else if (steps > 0 && steps > view->height) {
501                         steps -= view->height - 1;
502                 }
503
504                 move_view(view, steps);
505                 return;
506         }
507
508         view->draw(view, view->lineno - view->offset);
509
510         redrawwin(view->win);
511         wrefresh(view->win);
512
513         report_position(view, view->height);
514 }
515
516 static void
517 resize_view(struct view *view)
518 {
519         int lines, cols;
520
521         getmaxyx(stdscr, lines, cols);
522
523         if (view->win) {
524                 mvwin(view->win, 0, 0);
525                 wresize(view->win, lines - 2, cols);
526
527         } else {
528                 view->win = newwin(lines - 2, 0, 0, 0);
529                 if (!view->win) {
530                         report("failed to create %s view", view->name);
531                         return;
532                 }
533                 scrollok(view->win, TRUE);
534         }
535
536         getmaxyx(view->win, view->height, view->width);
537 }
538
539
540 static bool
541 begin_update(struct view *view)
542 {
543         char buf[1024];
544
545         if (view->cmd) {
546                 char *id = view->id;
547
548                 if (snprintf(buf, sizeof(buf), view->cmd, id, id, id) < sizeof(buf))
549                         view->pipe = popen(buf, "r");
550
551                 if (!view->pipe)
552                         return FALSE;
553
554                 if (nloading++ == 0)
555                         nodelay(status_win, TRUE);
556         }
557
558         display[current_view] = view;
559
560         view->offset = 0;
561         view->lines  = 0;
562         view->lineno = 0;
563
564         return TRUE;
565 }
566
567 static void
568 end_update(struct view *view)
569 {
570         wattrset(view->win, A_NORMAL);
571         pclose(view->pipe);
572         view->pipe = NULL;
573
574         if (nloading-- == 1)
575                 nodelay(status_win, FALSE);
576 }
577
578 static int
579 update_view(struct view *view)
580 {
581         char buffer[BUFSIZ];
582         char *line;
583         void **tmp;
584         int redraw;
585         int lines = view->height;
586
587         if (!view->pipe)
588                 return TRUE;
589
590         /* Only redraw after the first reading session. */
591         /* FIXME: ... and possibly more. */
592         redraw = view->height > view->lines;
593
594         tmp = realloc(view->line, sizeof(*view->line) * (view->lines + lines));
595         if (!tmp)
596                 goto alloc_error;
597
598         view->line = tmp;
599
600         while ((line = fgets(buffer, sizeof(buffer), view->pipe))) {
601                 int linelen;
602
603                 linelen = strlen(line);
604                 if (linelen)
605                         line[linelen - 1] = 0;
606
607                 if (!view->read(view, line))
608                         goto alloc_error;
609
610                 if (lines-- == 1)
611                         break;
612         }
613
614         if (redraw)
615                 redraw_view(view);
616
617         if (ferror(view->pipe)) {
618                 report("failed to read %s", view->cmd);
619                 goto end;
620
621         } else if (feof(view->pipe)) {
622                 report_position(view, 0);
623                 goto end;
624         }
625
626         return TRUE;
627
628 alloc_error:
629         report("allocation failure");
630
631 end:
632         end_update(view);
633         return FALSE;
634 }
635
636
637 static struct view *
638 switch_view(struct view *prev, int request)
639 {
640         struct view *view = &views[request - REQ_OFFSET];
641         struct view *displayed;
642         int i;
643
644         if (view == prev) {
645                 foreach_view (displayed, i) ;
646
647                 if (i == 1)
648                         report("already in %s view", view->name);
649                 else
650                         report("FIXME: Maximize");
651
652                 return view;
653
654         } else {
655                 foreach_view (displayed, i) {
656                         if (view == displayed) {
657                                 current_view = i;
658                                 report("new current view");
659                                 return view;
660                         }
661                 }
662         }
663
664         if (!view->win)
665                 resize_view(view);
666
667         /* Reload */
668
669         if (view->line) {
670                 for (i = 0; i < view->lines; i++)
671                         if (view->line[i])
672                                 free(view->line[i]);
673
674                 free(view->line);
675                 view->line = NULL;
676         }
677
678         if (prev && prev->pipe)
679                 end_update(prev);
680
681         if (begin_update(view)) {
682                 if (!view->cmd)
683                         report("%s", HELP);
684                 else
685                         report("loading...");
686         }
687
688         return view;
689 }
690
691
692 /* Process a keystroke */
693 static int
694 view_driver(struct view *view, int key)
695 {
696         int request = get_request(key);
697         int i;
698
699         switch (request) {
700         case REQ_NEXT_LINE:
701         case REQ_PREV_LINE:
702         case REQ_FIRST_LINE:
703         case REQ_LAST_LINE:
704         case REQ_NEXT_PAGE:
705         case REQ_PREV_PAGE:
706                 if (view)
707                         navigate_view(view, request);
708                 break;
709
710         case REQ_SCR_FLINE:
711         case REQ_SCR_BLINE:
712         case REQ_SCR_FPAGE:
713         case REQ_SCR_BPAGE:
714                 if (view)
715                         scroll_view(view, request);
716                 break;
717
718         case REQ_MAIN:
719         case REQ_LOG:
720         case REQ_DIFF:
721                 view = switch_view(view, request);
722                 break;
723
724         case REQ_REDRAW:
725                 redraw_view(view);
726                 break;
727
728         case REQ_STOP:
729                 foreach_view (view, i) {
730                         if (view->pipe) {
731                                 end_update(view);
732                                 scroll_view(view, 0);
733                         }
734                 }
735                 break;
736
737         case REQ_VERSION:
738                 report("version %s", VERSION);
739                 return TRUE;
740
741         case REQ_UPDATE:
742                 doupdate();
743                 return TRUE;
744
745         case REQ_QUIT:
746                 return FALSE;
747
748         default:
749                 report(HELP);
750                 return TRUE;
751         }
752
753         return TRUE;
754 }
755
756
757 /*
758  * Rendering
759  */
760
761 static int
762 pager_draw(struct view *view, unsigned int lineno)
763 {
764         enum line_type type;
765         char *line;
766         int attr;
767
768         if (view->offset + lineno >= view->lines)
769                 return FALSE;
770
771         line = view->line[view->offset + lineno];
772         type = get_line_type(line);
773
774         if (view->offset + lineno == view->lineno) {
775                 if (type == LINE_COMMIT)
776                         strncpy(commit_id, line + 7, SIZEOF_ID);
777                 type = LINE_CURSOR;
778         }
779
780         attr = get_line_attr(type);
781         wattrset(view->win, attr);
782         //mvwprintw(view->win, lineno, 0, "%4d: %s", view->offset + lineno, line);
783         mvwaddstr(view->win, lineno, 0, line);
784
785         return TRUE;
786 }
787
788 static int
789 pager_read(struct view *view, char *line)
790 {
791         view->line[view->lines] = strdup(line);
792         if (!view->line[view->lines])
793                 return FALSE;
794
795         view->lines++;
796         return TRUE;
797 }
798
799 static int
800 main_draw(struct view *view, unsigned int lineno)
801 {
802         struct commit *commit;
803         enum line_type type;
804
805         if (view->offset + lineno >= view->lines)
806                 return FALSE;
807
808         commit = view->line[view->offset + lineno];
809         if (!commit) return FALSE;
810
811         if (view->offset + lineno == view->lineno) {
812                 strncpy(commit_id, commit->id, SIZEOF_ID);
813                 type = LINE_CURSOR;
814         } else {
815                 type = LINE_COMMIT;
816         }
817
818         mvwaddch(view->win, lineno, 0, ACS_LTEE);
819         wattrset(view->win, get_line_attr(type));
820         mvwaddstr(view->win, lineno, 2, commit->title);
821         wattrset(view->win, A_NORMAL);
822
823         return TRUE;
824 }
825
826 static int
827 main_read(struct view *view, char *line)
828 {
829         enum line_type type = get_line_type(line);
830         struct commit *commit;
831
832         switch (type) {
833         case LINE_COMMIT:
834                 commit = calloc(1, sizeof(struct commit));
835                 if (!commit)
836                         return FALSE;
837
838                 view->line[view->lines++] = commit;
839                 strncpy(commit->id, line + 7, 41);
840                 break;
841
842         default:
843                 commit = view->line[view->lines - 1];
844                 if (!commit->title[0] &&
845                     !strncmp(line, "    ", 4) &&
846                     !isspace(line[5]))
847                         strncpy(commit->title, line + 4, sizeof(commit->title));
848         }
849
850         return TRUE;
851 }
852
853
854 /*
855  * Main
856  */
857
858 static void
859 quit(int sig)
860 {
861         if (status_win)
862                 delwin(status_win);
863         if (title_win)
864                 delwin(title_win);
865         endwin();
866
867         /* FIXME: Shutdown gracefully. */
868
869         exit(0);
870 }
871
872 static void die(const char *err, ...)
873 {
874         va_list args;
875
876         endwin();
877
878         va_start(args, err);
879         fputs("tig: ", stderr);
880         vfprintf(stderr, err, args);
881         fputs("\n", stderr);
882         va_end(args);
883
884         exit(1);
885 }
886
887 static void
888 report(const char *msg, ...)
889 {
890         va_list args;
891
892         va_start(args, msg);
893
894         werase(status_win);
895         wmove(status_win, 0, 0);
896
897 #if 0
898         if (display[current_view])
899                 wprintw(status_win, "%s %4s: ", commit_id, display[current_view]->name);
900 #endif
901         vwprintw(status_win, msg, args);
902         wrefresh(status_win);
903
904         va_end(args);
905
906         werase(title_win);
907         wmove(title_win, 0, 0);
908         wprintw(title_win, "commit %s", commit_id);
909         wrefresh(title_win);
910
911 }
912
913 int
914 main(int argc, char *argv[])
915 {
916         int request = REQ_MAIN;
917         int x, y;
918
919         signal(SIGINT, quit);
920
921         initscr();      /* initialize the curses library */
922         nonl();         /* tell curses not to do NL->CR/NL on output */
923         cbreak();       /* take input chars one at a time, no wait for \n */
924         noecho();       /* don't echo input */
925         leaveok(stdscr, TRUE);
926         /* curs_set(0); */
927
928         if (has_colors())
929                 init_colors();
930
931         getmaxyx(stdscr, y, x);
932         status_win = newwin(1, 0, y - 1, 0);
933         if (!status_win)
934                 die("Failed to create status window");
935
936         title_win = newwin(1, 0, y - 2, 0);
937         if (!title_win)
938                 die("Failed to create title window");
939
940         /* Enable keyboard mapping */
941         keypad(status_win, TRUE);
942         wbkgdset(status_win, get_line_attr(LINE_STATUS));
943         wbkgdset(title_win, get_line_attr(LINE_TITLE));
944
945         while (view_driver(display[current_view], request)) {
946                 struct view *view;
947                 int i;
948
949                 foreach_view (view, i) {
950                         if (view->pipe) {
951                                 update_view(view);
952                         }
953                 }
954
955                 /* Refresh, accept single keystroke of input */
956                 request = wgetch(status_win);
957                 if (request == KEY_RESIZE) {
958                         int lines, cols;
959
960                         getmaxyx(stdscr, lines, cols);
961
962                         mvwin(status_win, lines - 1, 0);
963                         wresize(status_win, 1, cols - 1);
964
965                         mvwin(title_win, lines - 2, 0);
966                         wresize(title_win, 1, cols - 1);
967                 }
968         }
969
970         quit(0);
971
972         return 0;
973 }
974
975 /**
976  * COPYRIGHT
977  * ---------
978  * Copyright (c) Jonas Fonseca, 2006
979  *
980  * This program is free software; you can redistribute it and/or modify
981  * it under the terms of the GNU General Public License as published by
982  * the Free Software Foundation; either version 2 of the License, or
983  * (at your option) any later version.
984  *
985  * SEE ALSO
986  * --------
987  * link:http://www.kernel.org/pub/software/scm/git/docs/[git(7)],
988  * link:http://www.kernel.org/pub/software/scm/cogito/docs/[cogito(7)]
989  **/