Cleanup line matching
[tig] / tig.c
CommitLineData
b801d8b2
JF
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
22f66b0a
JF
34#include <assert.h>
35#include <ctype.h>
36#include <signal.h>
b801d8b2 37#include <stdarg.h>
b801d8b2 38#include <stdio.h>
22f66b0a 39#include <stdlib.h>
b801d8b2 40#include <string.h>
b801d8b2
JF
41
42#include <curses.h>
43#include <form.h>
44
45static void die(const char *err, ...);
46static void report(const char *msg, ...);
47
48#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
49
b801d8b2 50#define KEY_TAB 9
fd85fef1
JF
51#define KEY_ESC 27
52#define KEY_DEL 127
b801d8b2
JF
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)
22f66b0a 60#define REQ_VIEWS (REQ_OFFSET + 3)
b801d8b2
JF
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)
fd85fef1
JF
67#define REQ_FIRST_LINE (REQ_OFFSET + 16)
68#define REQ_LAST_LINE (REQ_OFFSET + 17)
b801d8b2 69
78c70acd
JF
70#define COLOR_TRANSP (-1)
71
72
73enum 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
92struct 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
105static 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
126static struct line_info *
127get_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
143static enum line_type
144get_line_type(char *line)
145{
146 struct line_info *info = get_line_info(line);
147
148 return info ? info->type : LINE_UNKNOWN;
149}
150
151static int
152get_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
163static void
164init_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
b801d8b2
JF
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
216struct keymap {
217 int alias;
218 int request;
219};
220
221struct keymap keymap[] = {
fd85fef1 222 /* Cursor navigation */
b801d8b2
JF
223 { KEY_UP, REQ_PREV_LINE },
224 { 'k', REQ_PREV_LINE },
225 { KEY_DOWN, REQ_NEXT_LINE },
226 { 'j', REQ_NEXT_LINE },
fd85fef1
JF
227 { KEY_HOME, REQ_FIRST_LINE },
228 { KEY_END, REQ_LAST_LINE },
78c70acd
JF
229 { KEY_NPAGE, REQ_NEXT_PAGE },
230 { KEY_PPAGE, REQ_PREV_PAGE },
fd85fef1
JF
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 */
78c70acd
JF
235 { 's', REQ_SCR_FPAGE }, /* scroll field forward a page */
236 { 'w', REQ_SCR_BPAGE }, /* scroll field backward a page */
b801d8b2
JF
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
252static int
253get_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
269struct view {
270 char *name;
271 char *cmd;
fd85fef1 272 char *id;
b801d8b2 273
22f66b0a 274
b801d8b2 275 /* Rendering */
22f66b0a
JF
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
b801d8b2 280 WINDOW *win;
fd85fef1 281 int height, width;
b801d8b2
JF
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 */
22f66b0a 289 void **line; /* Line index */
b801d8b2
JF
290
291 /* Loading */
292 FILE *pipe;
293};
294
22f66b0a
JF
295struct commit {
296 char id[41];
297 char title[75];
298};
299
300static int pager_draw(struct view *view, unsigned int lineno);
301static int pager_read(struct view *view, char *line);
302
303static int main_draw(struct view *view, unsigned int lineno);
304static int main_read(struct view *view, char *line);
b801d8b2 305
fd85fef1 306#define DIFF_CMD \
b801d8b2
JF
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
fd85fef1
JF
313#define MAIN_CMD \
314 "git log --stat --pretty=raw %s"
315
b801d8b2
JF
316/* The status window at the bottom. Used for polling keystrokes. */
317static WINDOW *status_win;
318
78c70acd
JF
319static WINDOW *title_win;
320
fd85fef1 321#define SIZEOF_ID 1024
22f66b0a 322#define SIZEOF_VIEWS (REQ_VIEWS - REQ_OFFSET)
fd85fef1
JF
323
324char head_id[SIZEOF_ID] = "HEAD";
325char commit_id[SIZEOF_ID] = "HEAD";
326
327static unsigned int current_view;
328static unsigned int nloading;
329
22f66b0a
JF
330static struct view views[];
331static struct view *display[];
332
b801d8b2 333static struct view views[] = {
22f66b0a 334 { "diff", DIFF_CMD, commit_id, pager_read, pager_draw, sizeof(char) },
78c70acd
JF
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) },
b801d8b2
JF
337};
338
339static struct view *display[ARRAY_SIZE(views)];
b801d8b2 340
22f66b0a 341
b801d8b2
JF
342#define foreach_view(view, i) \
343 for (i = 0; i < sizeof(display) && (view = display[i]); i++)
344
345static void
346redraw_view(struct view *view)
347{
348 int lineno;
b801d8b2
JF
349
350 wclear(view->win);
351 wmove(view->win, 0, 0);
352
fd85fef1 353 for (lineno = 0; lineno < view->height; lineno++) {
22f66b0a 354 if (!view->draw(view, lineno))
fd85fef1 355 break;
b801d8b2
JF
356 }
357
358 redrawwin(view->win);
359 wrefresh(view->win);
360}
361
362/* FIXME: Fix percentage. */
363static void
364report_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
78c70acd 374
b801d8b2 375static void
fd85fef1 376move_view(struct view *view, int lines)
b801d8b2 377{
fd85fef1
JF
378 /* The rendering expects the new offset. */
379 view->offset += lines;
380
381 assert(0 <= view->offset && view->offset < view->lines);
382 assert(lines);
b801d8b2 383
fd85fef1 384 {
22f66b0a
JF
385 int line = lines > 0 ? view->height - lines : 0;
386 int end = line + (lines > 0 ? lines : -lines);
fd85fef1
JF
387
388 wscrl(view->win, lines);
389
22f66b0a
JF
390 for (; line < end; line++) {
391 if (!view->draw(view, line))
fd85fef1
JF
392 break;
393 }
394 }
395
396 /* Move current line into the view. */
397 if (view->lineno < view->offset) {
398 view->lineno = view->offset;
22f66b0a 399 view->draw(view, 0);
fd85fef1
JF
400
401 } else if (view->lineno >= view->offset + view->height) {
402 view->lineno = view->offset + view->height - 1;
22f66b0a 403 view->draw(view, view->lineno - view->offset);
fd85fef1
JF
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}
78c70acd 413
fd85fef1
JF
414static void
415scroll_view(struct view *view, int request)
416{
417 int lines = 1;
b801d8b2
JF
418
419 switch (request) {
fd85fef1
JF
420 case REQ_SCR_FPAGE:
421 lines = view->height;
422 case REQ_SCR_FLINE:
b801d8b2 423 if (view->offset + lines > view->lines)
bde3653a 424 lines = view->lines - view->offset;
b801d8b2 425
fd85fef1 426 if (lines == 0 || view->offset + view->height >= view->lines) {
b801d8b2
JF
427 report("already at last line");
428 return;
429 }
430 break;
431
fd85fef1
JF
432 case REQ_SCR_BPAGE:
433 lines = view->height;
434 case REQ_SCR_BLINE:
b801d8b2
JF
435 if (lines > view->offset)
436 lines = view->offset;
437
438 if (lines == 0) {
439 report("already at first line");
440 return;
441 }
442
fd85fef1 443 lines = -lines;
b801d8b2 444 break;
b801d8b2
JF
445 }
446
fd85fef1
JF
447 move_view(view, lines);
448}
b801d8b2 449
b801d8b2 450
fd85fef1
JF
451static void
452navigate_view(struct view *view, int request)
453{
454 int steps;
b801d8b2 455
fd85fef1 456 switch (request) {
78c70acd
JF
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
fd85fef1 475 case REQ_PREV_LINE:
fd85fef1
JF
476 steps = -1;
477 break;
b801d8b2 478
fd85fef1 479 case REQ_NEXT_LINE:
fd85fef1
JF
480 steps = 1;
481 break;
78c70acd 482 }
b801d8b2 483
78c70acd
JF
484 if (steps < 0 && view->lineno == 0) {
485 report("already at first line");
486 return;
b801d8b2 487
78c70acd
JF
488 } else if (steps > 0 && view->lineno + 1 >= view->lines) {
489 report("already at last line");
490 return;
fd85fef1
JF
491 }
492
493 view->lineno += steps;
22f66b0a 494 view->draw(view, view->lineno - steps - view->offset);
fd85fef1
JF
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;
78c70acd
JF
500 } else if (steps > 0 && steps > view->height) {
501 steps -= view->height - 1;
b801d8b2 502 }
78c70acd 503
fd85fef1
JF
504 move_view(view, steps);
505 return;
b801d8b2
JF
506 }
507
22f66b0a 508 view->draw(view, view->lineno - view->offset);
fd85fef1 509
b801d8b2
JF
510 redrawwin(view->win);
511 wrefresh(view->win);
512
fd85fef1 513 report_position(view, view->height);
b801d8b2
JF
514}
515
516static void
517resize_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);
78c70acd 525 wresize(view->win, lines - 2, cols);
b801d8b2
JF
526
527 } else {
78c70acd 528 view->win = newwin(lines - 2, 0, 0, 0);
b801d8b2 529 if (!view->win) {
fd85fef1 530 report("failed to create %s view", view->name);
b801d8b2
JF
531 return;
532 }
533 scrollok(view->win, TRUE);
534 }
fd85fef1
JF
535
536 getmaxyx(view->win, view->height, view->width);
b801d8b2
JF
537}
538
539
540static bool
541begin_update(struct view *view)
542{
543 char buf[1024];
544
545 if (view->cmd) {
fd85fef1
JF
546 char *id = view->id;
547
548 if (snprintf(buf, sizeof(buf), view->cmd, id, id, id) < sizeof(buf))
b801d8b2
JF
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
567static void
568end_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
578static int
579update_view(struct view *view)
580{
581 char buffer[BUFSIZ];
582 char *line;
22f66b0a 583 void **tmp;
b801d8b2 584 int redraw;
fd85fef1 585 int lines = view->height;
b801d8b2
JF
586
587 if (!view->pipe)
588 return TRUE;
589
fd85fef1 590 /* Only redraw after the first reading session. */
22f66b0a
JF
591 /* FIXME: ... and possibly more. */
592 redraw = view->height > view->lines;
b801d8b2
JF
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
b801d8b2
JF
603 linelen = strlen(line);
604 if (linelen)
605 line[linelen - 1] = 0;
606
22f66b0a 607 if (!view->read(view, line))
b801d8b2 608 goto alloc_error;
fd85fef1
JF
609
610 if (lines-- == 1)
611 break;
b801d8b2
JF
612 }
613
614 if (redraw)
615 redraw_view(view);
616
617 if (ferror(view->pipe)) {
fd85fef1 618 report("failed to read %s", view->cmd);
b801d8b2
JF
619 goto end;
620
621 } else if (feof(view->pipe)) {
622 report_position(view, 0);
623 goto end;
624 }
625
626 return TRUE;
627
628alloc_error:
fd85fef1 629 report("allocation failure");
b801d8b2
JF
630
631end:
632 end_update(view);
633 return FALSE;
634}
635
636
637static struct view *
638switch_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)
fd85fef1 648 report("already in %s view", view->name);
b801d8b2
JF
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;
fd85fef1 658 report("new current view");
b801d8b2
JF
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 */
693static int
694view_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:
b801d8b2 701 case REQ_PREV_LINE:
fd85fef1
JF
702 case REQ_FIRST_LINE:
703 case REQ_LAST_LINE:
78c70acd
JF
704 case REQ_NEXT_PAGE:
705 case REQ_PREV_PAGE:
fd85fef1
JF
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:
b801d8b2
JF
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
b801d8b2 761static int
22f66b0a 762pager_draw(struct view *view, unsigned int lineno)
b801d8b2 763{
78c70acd 764 enum line_type type;
b801d8b2 765 char *line;
78c70acd 766 int attr;
b801d8b2 767
fd85fef1
JF
768 if (view->offset + lineno >= view->lines)
769 return FALSE;
770
b801d8b2 771 line = view->line[view->offset + lineno];
78c70acd 772 type = get_line_type(line);
b801d8b2 773
fd85fef1 774 if (view->offset + lineno == view->lineno) {
78c70acd 775 if (type == LINE_COMMIT)
fd85fef1 776 strncpy(commit_id, line + 7, SIZEOF_ID);
78c70acd 777 type = LINE_CURSOR;
fd85fef1
JF
778 }
779
78c70acd 780 attr = get_line_attr(type);
b801d8b2 781 wattrset(view->win, attr);
fd85fef1
JF
782 //mvwprintw(view->win, lineno, 0, "%4d: %s", view->offset + lineno, line);
783 mvwaddstr(view->win, lineno, 0, line);
b801d8b2
JF
784
785 return TRUE;
786}
787
22f66b0a
JF
788static int
789pager_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
799static int
800main_draw(struct view *view, unsigned int lineno)
801{
802 struct commit *commit;
78c70acd 803 enum line_type type;
22f66b0a
JF
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
22f66b0a
JF
811 if (view->offset + lineno == view->lineno) {
812 strncpy(commit_id, commit->id, SIZEOF_ID);
78c70acd
JF
813 type = LINE_CURSOR;
814 } else {
815 type = LINE_COMMIT;
22f66b0a
JF
816 }
817
818 mvwaddch(view->win, lineno, 0, ACS_LTEE);
78c70acd 819 wattrset(view->win, get_line_attr(type));
22f66b0a
JF
820 mvwaddstr(view->win, lineno, 2, commit->title);
821 wattrset(view->win, A_NORMAL);
822
823 return TRUE;
824}
825
826static int
827main_read(struct view *view, char *line)
828{
78c70acd
JF
829 enum line_type type = get_line_type(line);
830 struct commit *commit;
22f66b0a 831
78c70acd
JF
832 switch (type) {
833 case LINE_COMMIT:
22f66b0a
JF
834 commit = calloc(1, sizeof(struct commit));
835 if (!commit)
836 return FALSE;
837
838 view->line[view->lines++] = commit;
22f66b0a 839 strncpy(commit->id, line + 7, 41);
78c70acd 840 break;
22f66b0a 841
78c70acd
JF
842 default:
843 commit = view->line[view->lines - 1];
22f66b0a 844 if (!commit->title[0] &&
22f66b0a
JF
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
b801d8b2
JF
854/*
855 * Main
856 */
857
858static void
859quit(int sig)
860{
861 if (status_win)
862 delwin(status_win);
78c70acd
JF
863 if (title_win)
864 delwin(title_win);
b801d8b2
JF
865 endwin();
866
867 /* FIXME: Shutdown gracefully. */
868
869 exit(0);
870}
871
872static 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
887static void
888report(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
fd85fef1 897#if 0
b801d8b2 898 if (display[current_view])
fd85fef1
JF
899 wprintw(status_win, "%s %4s: ", commit_id, display[current_view]->name);
900#endif
b801d8b2
JF
901 vwprintw(status_win, msg, args);
902 wrefresh(status_win);
903
904 va_end(args);
b801d8b2 905
78c70acd
JF
906 werase(title_win);
907 wmove(title_win, 0, 0);
908 wprintw(title_win, "commit %s", commit_id);
909 wrefresh(title_win);
b801d8b2 910
b801d8b2
JF
911}
912
913int
914main(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
78c70acd
JF
936 title_win = newwin(1, 0, y - 2, 0);
937 if (!title_win)
938 die("Failed to create title window");
939
b801d8b2
JF
940 /* Enable keyboard mapping */
941 keypad(status_win, TRUE);
78c70acd
JF
942 wbkgdset(status_win, get_line_attr(LINE_STATUS));
943 wbkgdset(title_win, get_line_attr(LINE_TITLE));
b801d8b2
JF
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);
78c70acd 961
b801d8b2
JF
962 mvwin(status_win, lines - 1, 0);
963 wresize(status_win, 1, cols - 1);
78c70acd
JF
964
965 mvwin(title_win, lines - 2, 0);
966 wresize(title_win, 1, cols - 1);
b801d8b2
JF
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 **/