Simplify the update_rev_graph end-game
[tig] / tig.c
1 /* Copyright (c) 2006 Jonas Fonseca <fonseca@diku.dk>
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU General Public License as
5  * published by the Free Software Foundation; either version 2 of
6  * the License, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #ifndef VERSION
15 #define VERSION "tig-0.4.git"
16 #endif
17
18 #ifndef DEBUG
19 #define NDEBUG
20 #endif
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <signal.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <time.h>
32
33 #include <sys/types.h>
34 #include <regex.h>
35
36 #include <locale.h>
37 #include <langinfo.h>
38 #include <iconv.h>
39
40 #include <curses.h>
41
42 #if __GNUC__ >= 3
43 #define __NORETURN __attribute__((__noreturn__))
44 #else
45 #define __NORETURN
46 #endif
47
48 static void __NORETURN die(const char *err, ...);
49 static void report(const char *msg, ...);
50 static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, int, char *, int));
51 static void set_nonblocking_input(bool loading);
52 static size_t utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed);
53
54 #define ABS(x)          ((x) >= 0  ? (x) : -(x))
55 #define MIN(x, y)       ((x) < (y) ? (x) :  (y))
56
57 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))
58 #define STRING_SIZE(x)  (sizeof(x) - 1)
59
60 #define SIZEOF_STR      1024    /* Default string size. */
61 #define SIZEOF_REF      256     /* Size of symbolic or SHA1 ID. */
62 #define SIZEOF_REV      41      /* Holds a SHA-1 and an ending NUL */
63
64 /* Revision graph */
65
66 #define REVGRAPH_INIT   'I'
67 #define REVGRAPH_MERGE  'M'
68 #define REVGRAPH_BRANCH '+'
69 #define REVGRAPH_COMMIT '*'
70 #define REVGRAPH_LINE   '|'
71
72 #define SIZEOF_REVGRAPH 19      /* Size of revision ancestry graphics. */
73
74 /* Size of rev graph with no  "padding" columns */
75 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
76
77 /* This color name can be used to refer to the default term colors. */
78 #define COLOR_DEFAULT   (-1)
79
80 #define ICONV_NONE      ((iconv_t) -1)
81
82 /* The format and size of the date column in the main view. */
83 #define DATE_FORMAT     "%Y-%m-%d %H:%M"
84 #define DATE_COLS       STRING_SIZE("2006-04-29 14:21 ")
85
86 #define AUTHOR_COLS     20
87
88 /* The default interval between line numbers. */
89 #define NUMBER_INTERVAL 1
90
91 #define TABSIZE         8
92
93 #define SCALE_SPLIT_VIEW(height)        ((height) * 2 / 3)
94
95 #define TIG_LS_REMOTE \
96         "git ls-remote . 2>/dev/null"
97
98 #define TIG_DIFF_CMD \
99         "git show --root --patch-with-stat --find-copies-harder -B -C %s 2>/dev/null"
100
101 #define TIG_LOG_CMD     \
102         "git log --cc --stat -n100 %s 2>/dev/null"
103
104 #define TIG_MAIN_CMD \
105         "git log --topo-order --pretty=raw %s 2>/dev/null"
106
107 #define TIG_TREE_CMD    \
108         "git ls-tree %s %s"
109
110 #define TIG_BLOB_CMD    \
111         "git cat-file blob %s"
112
113 /* XXX: Needs to be defined to the empty string. */
114 #define TIG_HELP_CMD    ""
115 #define TIG_PAGER_CMD   ""
116
117 /* Some ascii-shorthands fitted into the ncurses namespace. */
118 #define KEY_TAB         '\t'
119 #define KEY_RETURN      '\r'
120 #define KEY_ESC         27
121
122
123 struct ref {
124         char *name;             /* Ref name; tag or head names are shortened. */
125         char id[SIZEOF_REV];    /* Commit SHA1 ID */
126         unsigned int tag:1;     /* Is it a tag? */
127         unsigned int next:1;    /* For ref lists: are there more refs? */
128 };
129
130 static struct ref **get_refs(char *id);
131
132 struct int_map {
133         const char *name;
134         int namelen;
135         int value;
136 };
137
138 static int
139 set_from_int_map(struct int_map *map, size_t map_size,
140                  int *value, const char *name, int namelen)
141 {
142
143         int i;
144
145         for (i = 0; i < map_size; i++)
146                 if (namelen == map[i].namelen &&
147                     !strncasecmp(name, map[i].name, namelen)) {
148                         *value = map[i].value;
149                         return OK;
150                 }
151
152         return ERR;
153 }
154
155
156 /*
157  * String helpers
158  */
159
160 static inline void
161 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
162 {
163         if (srclen > dstlen - 1)
164                 srclen = dstlen - 1;
165
166         strncpy(dst, src, srclen);
167         dst[srclen] = 0;
168 }
169
170 /* Shorthands for safely copying into a fixed buffer. */
171
172 #define string_copy(dst, src) \
173         string_ncopy_do(dst, sizeof(dst), src, sizeof(dst))
174
175 #define string_ncopy(dst, src, srclen) \
176         string_ncopy_do(dst, sizeof(dst), src, srclen)
177
178 static char *
179 chomp_string(char *name)
180 {
181         int namelen;
182
183         while (isspace(*name))
184                 name++;
185
186         namelen = strlen(name) - 1;
187         while (namelen > 0 && isspace(name[namelen]))
188                 name[namelen--] = 0;
189
190         return name;
191 }
192
193 static bool
194 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
195 {
196         va_list args;
197         size_t pos = bufpos ? *bufpos : 0;
198
199         va_start(args, fmt);
200         pos += vsnprintf(buf + pos, bufsize - pos, fmt, args);
201         va_end(args);
202
203         if (bufpos)
204                 *bufpos = pos;
205
206         return pos >= bufsize ? FALSE : TRUE;
207 }
208
209 #define string_format(buf, fmt, args...) \
210         string_nformat(buf, sizeof(buf), NULL, fmt, args)
211
212 #define string_format_from(buf, from, fmt, args...) \
213         string_nformat(buf, sizeof(buf), from, fmt, args)
214
215 static int
216 string_enum_compare(const char *str1, const char *str2, int len)
217 {
218         size_t i;
219
220 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
221
222         /* Diff-Header == DIFF_HEADER */
223         for (i = 0; i < len; i++) {
224                 if (toupper(str1[i]) == toupper(str2[i]))
225                         continue;
226
227                 if (string_enum_sep(str1[i]) &&
228                     string_enum_sep(str2[i]))
229                         continue;
230
231                 return str1[i] - str2[i];
232         }
233
234         return 0;
235 }
236
237 /* Shell quoting
238  *
239  * NOTE: The following is a slightly modified copy of the git project's shell
240  * quoting routines found in the quote.c file.
241  *
242  * Help to copy the thing properly quoted for the shell safety.  any single
243  * quote is replaced with '\'', any exclamation point is replaced with '\!',
244  * and the whole thing is enclosed in a
245  *
246  * E.g.
247  *  original     sq_quote     result
248  *  name     ==> name      ==> 'name'
249  *  a b      ==> a b       ==> 'a b'
250  *  a'b      ==> a'\''b    ==> 'a'\''b'
251  *  a!b      ==> a'\!'b    ==> 'a'\!'b'
252  */
253
254 static size_t
255 sq_quote(char buf[SIZEOF_STR], size_t bufsize, const char *src)
256 {
257         char c;
258
259 #define BUFPUT(x) do { if (bufsize < SIZEOF_STR) buf[bufsize++] = (x); } while (0)
260
261         BUFPUT('\'');
262         while ((c = *src++)) {
263                 if (c == '\'' || c == '!') {
264                         BUFPUT('\'');
265                         BUFPUT('\\');
266                         BUFPUT(c);
267                         BUFPUT('\'');
268                 } else {
269                         BUFPUT(c);
270                 }
271         }
272         BUFPUT('\'');
273
274         return bufsize;
275 }
276
277
278 /*
279  * User requests
280  */
281
282 #define REQ_INFO \
283         /* XXX: Keep the view request first and in sync with views[]. */ \
284         REQ_GROUP("View switching") \
285         REQ_(VIEW_MAIN,         "Show main view"), \
286         REQ_(VIEW_DIFF,         "Show diff view"), \
287         REQ_(VIEW_LOG,          "Show log view"), \
288         REQ_(VIEW_TREE,         "Show tree view"), \
289         REQ_(VIEW_BLOB,         "Show blob view"), \
290         REQ_(VIEW_HELP,         "Show help page"), \
291         REQ_(VIEW_PAGER,        "Show pager view"), \
292         \
293         REQ_GROUP("View manipulation") \
294         REQ_(ENTER,             "Enter current line and scroll"), \
295         REQ_(NEXT,              "Move to next"), \
296         REQ_(PREVIOUS,          "Move to previous"), \
297         REQ_(VIEW_NEXT,         "Move focus to next view"), \
298         REQ_(VIEW_CLOSE,        "Close the current view"), \
299         REQ_(QUIT,              "Close all views and quit"), \
300         \
301         REQ_GROUP("Cursor navigation") \
302         REQ_(MOVE_UP,           "Move cursor one line up"), \
303         REQ_(MOVE_DOWN,         "Move cursor one line down"), \
304         REQ_(MOVE_PAGE_DOWN,    "Move cursor one page down"), \
305         REQ_(MOVE_PAGE_UP,      "Move cursor one page up"), \
306         REQ_(MOVE_FIRST_LINE,   "Move cursor to first line"), \
307         REQ_(MOVE_LAST_LINE,    "Move cursor to last line"), \
308         \
309         REQ_GROUP("Scrolling") \
310         REQ_(SCROLL_LINE_UP,    "Scroll one line up"), \
311         REQ_(SCROLL_LINE_DOWN,  "Scroll one line down"), \
312         REQ_(SCROLL_PAGE_UP,    "Scroll one page up"), \
313         REQ_(SCROLL_PAGE_DOWN,  "Scroll one page down"), \
314         \
315         REQ_GROUP("Searching") \
316         REQ_(SEARCH,            "Search the view"), \
317         REQ_(SEARCH_BACK,       "Search backwards in the view"), \
318         REQ_(FIND_NEXT,         "Find next search match"), \
319         REQ_(FIND_PREV,         "Find previous search match"), \
320         \
321         REQ_GROUP("Misc") \
322         REQ_(NONE,              "Do nothing"), \
323         REQ_(PROMPT,            "Bring up the prompt"), \
324         REQ_(SCREEN_REDRAW,     "Redraw the screen"), \
325         REQ_(SCREEN_RESIZE,     "Resize the screen"), \
326         REQ_(SHOW_VERSION,      "Show version information"), \
327         REQ_(STOP_LOADING,      "Stop all loading views"), \
328         REQ_(TOGGLE_LINENO,     "Toggle line numbers"), \
329         REQ_(TOGGLE_REV_GRAPH,  "Toggle revision graph visualization")
330
331
332 /* User action requests. */
333 enum request {
334 #define REQ_GROUP(help)
335 #define REQ_(req, help) REQ_##req
336
337         /* Offset all requests to avoid conflicts with ncurses getch values. */
338         REQ_OFFSET = KEY_MAX + 1,
339         REQ_INFO,
340         REQ_UNKNOWN,
341
342 #undef  REQ_GROUP
343 #undef  REQ_
344 };
345
346 struct request_info {
347         enum request request;
348         char *name;
349         int namelen;
350         char *help;
351 };
352
353 static struct request_info req_info[] = {
354 #define REQ_GROUP(help) { 0, NULL, 0, (help) },
355 #define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) }
356         REQ_INFO
357 #undef  REQ_GROUP
358 #undef  REQ_
359 };
360
361 static enum request
362 get_request(const char *name)
363 {
364         int namelen = strlen(name);
365         int i;
366
367         for (i = 0; i < ARRAY_SIZE(req_info); i++)
368                 if (req_info[i].namelen == namelen &&
369                     !string_enum_compare(req_info[i].name, name, namelen))
370                         return req_info[i].request;
371
372         return REQ_UNKNOWN;
373 }
374
375
376 /*
377  * Options
378  */
379
380 static const char usage[] =
381 VERSION " (" __DATE__ ")\n"
382 "\n"
383 "Usage: tig [options]\n"
384 "   or: tig [options] [--] [git log options]\n"
385 "   or: tig [options] log  [git log options]\n"
386 "   or: tig [options] diff [git diff options]\n"
387 "   or: tig [options] show [git show options]\n"
388 "   or: tig [options] <    [git command output]\n"
389 "\n"
390 "Options:\n"
391 "  -l                          Start up in log view\n"
392 "  -d                          Start up in diff view\n"
393 "  -n[I], --line-number[=I]    Show line numbers with given interval\n"
394 "  -b[N], --tab-size[=N]       Set number of spaces for tab expansion\n"
395 "  --                          Mark end of tig options\n"
396 "  -v, --version               Show version and exit\n"
397 "  -h, --help                  Show help message and exit\n";
398
399 /* Option and state variables. */
400 static bool opt_line_number             = FALSE;
401 static bool opt_rev_graph               = TRUE;
402 static int opt_num_interval             = NUMBER_INTERVAL;
403 static int opt_tab_size                 = TABSIZE;
404 static enum request opt_request         = REQ_VIEW_MAIN;
405 static char opt_cmd[SIZEOF_STR]         = "";
406 static char opt_path[SIZEOF_STR]        = "";
407 static FILE *opt_pipe                   = NULL;
408 static char opt_encoding[20]            = "UTF-8";
409 static bool opt_utf8                    = TRUE;
410 static char opt_codeset[20]             = "UTF-8";
411 static iconv_t opt_iconv                = ICONV_NONE;
412 static char opt_search[SIZEOF_STR]      = "";
413
414 enum option_type {
415         OPT_NONE,
416         OPT_INT,
417 };
418
419 static bool
420 check_option(char *opt, char short_name, char *name, enum option_type type, ...)
421 {
422         va_list args;
423         char *value = "";
424         int *number;
425
426         if (opt[0] != '-')
427                 return FALSE;
428
429         if (opt[1] == '-') {
430                 int namelen = strlen(name);
431
432                 opt += 2;
433
434                 if (strncmp(opt, name, namelen))
435                         return FALSE;
436
437                 if (opt[namelen] == '=')
438                         value = opt + namelen + 1;
439
440         } else {
441                 if (!short_name || opt[1] != short_name)
442                         return FALSE;
443                 value = opt + 2;
444         }
445
446         va_start(args, type);
447         if (type == OPT_INT) {
448                 number = va_arg(args, int *);
449                 if (isdigit(*value))
450                         *number = atoi(value);
451         }
452         va_end(args);
453
454         return TRUE;
455 }
456
457 /* Returns the index of log or diff command or -1 to exit. */
458 static bool
459 parse_options(int argc, char *argv[])
460 {
461         int i;
462
463         for (i = 1; i < argc; i++) {
464                 char *opt = argv[i];
465
466                 if (!strcmp(opt, "-l")) {
467                         opt_request = REQ_VIEW_LOG;
468                         continue;
469                 }
470
471                 if (!strcmp(opt, "-d")) {
472                         opt_request = REQ_VIEW_DIFF;
473                         continue;
474                 }
475
476                 if (check_option(opt, 'n', "line-number", OPT_INT, &opt_num_interval)) {
477                         opt_line_number = TRUE;
478                         continue;
479                 }
480
481                 if (check_option(opt, 'b', "tab-size", OPT_INT, &opt_tab_size)) {
482                         opt_tab_size = MIN(opt_tab_size, TABSIZE);
483                         continue;
484                 }
485
486                 if (check_option(opt, 'v', "version", OPT_NONE)) {
487                         printf("tig version %s\n", VERSION);
488                         return FALSE;
489                 }
490
491                 if (check_option(opt, 'h', "help", OPT_NONE)) {
492                         printf(usage);
493                         return FALSE;
494                 }
495
496                 if (!strcmp(opt, "--")) {
497                         i++;
498                         break;
499                 }
500
501                 if (!strcmp(opt, "log") ||
502                     !strcmp(opt, "diff") ||
503                     !strcmp(opt, "show")) {
504                         opt_request = opt[0] == 'l'
505                                     ? REQ_VIEW_LOG : REQ_VIEW_DIFF;
506                         break;
507                 }
508
509                 if (opt[0] && opt[0] != '-')
510                         break;
511
512                 die("unknown option '%s'\n\n%s", opt, usage);
513         }
514
515         if (!isatty(STDIN_FILENO)) {
516                 opt_request = REQ_VIEW_PAGER;
517                 opt_pipe = stdin;
518
519         } else if (i < argc) {
520                 size_t buf_size;
521
522                 if (opt_request == REQ_VIEW_MAIN)
523                         /* XXX: This is vulnerable to the user overriding
524                          * options required for the main view parser. */
525                         string_copy(opt_cmd, "git log --stat --pretty=raw");
526                 else
527                         string_copy(opt_cmd, "git");
528                 buf_size = strlen(opt_cmd);
529
530                 while (buf_size < sizeof(opt_cmd) && i < argc) {
531                         opt_cmd[buf_size++] = ' ';
532                         buf_size = sq_quote(opt_cmd, buf_size, argv[i++]);
533                 }
534
535                 if (buf_size >= sizeof(opt_cmd))
536                         die("command too long");
537
538                 opt_cmd[buf_size] = 0;
539
540         }
541
542         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
543                 opt_utf8 = FALSE;
544
545         return TRUE;
546 }
547
548
549 /*
550  * Line-oriented content detection.
551  */
552
553 #define LINE_INFO \
554 LINE(DIFF_HEADER,  "diff --git ",       COLOR_YELLOW,   COLOR_DEFAULT,  0), \
555 LINE(DIFF_CHUNK,   "@@",                COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
556 LINE(DIFF_ADD,     "+",                 COLOR_GREEN,    COLOR_DEFAULT,  0), \
557 LINE(DIFF_DEL,     "-",                 COLOR_RED,      COLOR_DEFAULT,  0), \
558 LINE(DIFF_INDEX,        "index ",         COLOR_BLUE,   COLOR_DEFAULT,  0), \
559 LINE(DIFF_OLDMODE,      "old file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
560 LINE(DIFF_NEWMODE,      "new file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
561 LINE(DIFF_COPY_FROM,    "copy from",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
562 LINE(DIFF_COPY_TO,      "copy to",        COLOR_YELLOW, COLOR_DEFAULT,  0), \
563 LINE(DIFF_RENAME_FROM,  "rename from",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
564 LINE(DIFF_RENAME_TO,    "rename to",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
565 LINE(DIFF_SIMILARITY,   "similarity ",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
566 LINE(DIFF_DISSIMILARITY,"dissimilarity ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
567 LINE(DIFF_TREE,         "diff-tree ",     COLOR_BLUE,   COLOR_DEFAULT,  0), \
568 LINE(PP_AUTHOR,    "Author: ",          COLOR_CYAN,     COLOR_DEFAULT,  0), \
569 LINE(PP_COMMIT,    "Commit: ",          COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
570 LINE(PP_MERGE,     "Merge: ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
571 LINE(PP_DATE,      "Date:   ",          COLOR_YELLOW,   COLOR_DEFAULT,  0), \
572 LINE(PP_ADATE,     "AuthorDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
573 LINE(PP_CDATE,     "CommitDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
574 LINE(PP_REFS,      "Refs: ",            COLOR_RED,      COLOR_DEFAULT,  0), \
575 LINE(COMMIT,       "commit ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
576 LINE(PARENT,       "parent ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
577 LINE(TREE,         "tree ",             COLOR_BLUE,     COLOR_DEFAULT,  0), \
578 LINE(AUTHOR,       "author ",           COLOR_CYAN,     COLOR_DEFAULT,  0), \
579 LINE(COMMITTER,    "committer ",        COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
580 LINE(SIGNOFF,      "    Signed-off-by", COLOR_YELLOW,   COLOR_DEFAULT,  0), \
581 LINE(ACKED,        "    Acked-by",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
582 LINE(DEFAULT,      "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
583 LINE(CURSOR,       "",                  COLOR_WHITE,    COLOR_GREEN,    A_BOLD), \
584 LINE(STATUS,       "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
585 LINE(TITLE_BLUR,   "",                  COLOR_WHITE,    COLOR_BLUE,     0), \
586 LINE(TITLE_FOCUS,  "",                  COLOR_WHITE,    COLOR_BLUE,     A_BOLD), \
587 LINE(MAIN_DATE,    "",                  COLOR_BLUE,     COLOR_DEFAULT,  0), \
588 LINE(MAIN_AUTHOR,  "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
589 LINE(MAIN_COMMIT,  "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
590 LINE(MAIN_DELIM,   "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
591 LINE(MAIN_TAG,     "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
592 LINE(MAIN_REF,     "",                  COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD), \
593 LINE(TREE_DIR,     "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
594 LINE(TREE_FILE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL)
595
596 enum line_type {
597 #define LINE(type, line, fg, bg, attr) \
598         LINE_##type
599         LINE_INFO
600 #undef  LINE
601 };
602
603 struct line_info {
604         const char *name;       /* Option name. */
605         int namelen;            /* Size of option name. */
606         const char *line;       /* The start of line to match. */
607         int linelen;            /* Size of string to match. */
608         int fg, bg, attr;       /* Color and text attributes for the lines. */
609 };
610
611 static struct line_info line_info[] = {
612 #define LINE(type, line, fg, bg, attr) \
613         { #type, STRING_SIZE(#type), (line), STRING_SIZE(line), (fg), (bg), (attr) }
614         LINE_INFO
615 #undef  LINE
616 };
617
618 static enum line_type
619 get_line_type(char *line)
620 {
621         int linelen = strlen(line);
622         enum line_type type;
623
624         for (type = 0; type < ARRAY_SIZE(line_info); type++)
625                 /* Case insensitive search matches Signed-off-by lines better. */
626                 if (linelen >= line_info[type].linelen &&
627                     !strncasecmp(line_info[type].line, line, line_info[type].linelen))
628                         return type;
629
630         return LINE_DEFAULT;
631 }
632
633 static inline int
634 get_line_attr(enum line_type type)
635 {
636         assert(type < ARRAY_SIZE(line_info));
637         return COLOR_PAIR(type) | line_info[type].attr;
638 }
639
640 static struct line_info *
641 get_line_info(char *name, int namelen)
642 {
643         enum line_type type;
644
645         for (type = 0; type < ARRAY_SIZE(line_info); type++)
646                 if (namelen == line_info[type].namelen &&
647                     !string_enum_compare(line_info[type].name, name, namelen))
648                         return &line_info[type];
649
650         return NULL;
651 }
652
653 static void
654 init_colors(void)
655 {
656         int default_bg = COLOR_BLACK;
657         int default_fg = COLOR_WHITE;
658         enum line_type type;
659
660         start_color();
661
662         if (use_default_colors() != ERR) {
663                 default_bg = -1;
664                 default_fg = -1;
665         }
666
667         for (type = 0; type < ARRAY_SIZE(line_info); type++) {
668                 struct line_info *info = &line_info[type];
669                 int bg = info->bg == COLOR_DEFAULT ? default_bg : info->bg;
670                 int fg = info->fg == COLOR_DEFAULT ? default_fg : info->fg;
671
672                 init_pair(type, fg, bg);
673         }
674 }
675
676 struct line {
677         enum line_type type;
678
679         /* State flags */
680         unsigned int selected:1;
681
682         void *data;             /* User data */
683 };
684
685
686 /*
687  * Keys
688  */
689
690 struct keybinding {
691         int alias;
692         enum request request;
693         struct keybinding *next;
694 };
695
696 static struct keybinding default_keybindings[] = {
697         /* View switching */
698         { 'm',          REQ_VIEW_MAIN },
699         { 'd',          REQ_VIEW_DIFF },
700         { 'l',          REQ_VIEW_LOG },
701         { 't',          REQ_VIEW_TREE },
702         { 'f',          REQ_VIEW_BLOB },
703         { 'p',          REQ_VIEW_PAGER },
704         { 'h',          REQ_VIEW_HELP },
705
706         /* View manipulation */
707         { 'q',          REQ_VIEW_CLOSE },
708         { KEY_TAB,      REQ_VIEW_NEXT },
709         { KEY_RETURN,   REQ_ENTER },
710         { KEY_UP,       REQ_PREVIOUS },
711         { KEY_DOWN,     REQ_NEXT },
712
713         /* Cursor navigation */
714         { 'k',          REQ_MOVE_UP },
715         { 'j',          REQ_MOVE_DOWN },
716         { KEY_HOME,     REQ_MOVE_FIRST_LINE },
717         { KEY_END,      REQ_MOVE_LAST_LINE },
718         { KEY_NPAGE,    REQ_MOVE_PAGE_DOWN },
719         { ' ',          REQ_MOVE_PAGE_DOWN },
720         { KEY_PPAGE,    REQ_MOVE_PAGE_UP },
721         { 'b',          REQ_MOVE_PAGE_UP },
722         { '-',          REQ_MOVE_PAGE_UP },
723
724         /* Scrolling */
725         { KEY_IC,       REQ_SCROLL_LINE_UP },
726         { KEY_DC,       REQ_SCROLL_LINE_DOWN },
727         { 'w',          REQ_SCROLL_PAGE_UP },
728         { 's',          REQ_SCROLL_PAGE_DOWN },
729
730         /* Searching */
731         { '/',          REQ_SEARCH },
732         { '?',          REQ_SEARCH_BACK },
733         { 'n',          REQ_FIND_NEXT },
734         { 'N',          REQ_FIND_PREV },
735
736         /* Misc */
737         { 'Q',          REQ_QUIT },
738         { 'z',          REQ_STOP_LOADING },
739         { 'v',          REQ_SHOW_VERSION },
740         { 'r',          REQ_SCREEN_REDRAW },
741         { '.',          REQ_TOGGLE_LINENO },
742         { 'g',          REQ_TOGGLE_REV_GRAPH },
743         { ':',          REQ_PROMPT },
744
745         /* wgetch() with nodelay() enabled returns ERR when there's no input. */
746         { ERR,          REQ_NONE },
747
748         /* Using the ncurses SIGWINCH handler. */
749         { KEY_RESIZE,   REQ_SCREEN_RESIZE },
750 };
751
752 #define KEYMAP_INFO \
753         KEYMAP_(GENERIC), \
754         KEYMAP_(MAIN), \
755         KEYMAP_(DIFF), \
756         KEYMAP_(LOG), \
757         KEYMAP_(TREE), \
758         KEYMAP_(BLOB), \
759         KEYMAP_(PAGER), \
760         KEYMAP_(HELP) \
761
762 enum keymap {
763 #define KEYMAP_(name) KEYMAP_##name
764         KEYMAP_INFO
765 #undef  KEYMAP_
766 };
767
768 static struct int_map keymap_table[] = {
769 #define KEYMAP_(name) { #name, STRING_SIZE(#name), KEYMAP_##name }
770         KEYMAP_INFO
771 #undef  KEYMAP_
772 };
773
774 #define set_keymap(map, name) \
775         set_from_int_map(keymap_table, ARRAY_SIZE(keymap_table), map, name, strlen(name))
776
777 static struct keybinding *keybindings[ARRAY_SIZE(keymap_table)];
778
779 static void
780 add_keybinding(enum keymap keymap, enum request request, int key)
781 {
782         struct keybinding *keybinding;
783
784         keybinding = calloc(1, sizeof(*keybinding));
785         if (!keybinding)
786                 die("Failed to allocate keybinding");
787
788         keybinding->alias = key;
789         keybinding->request = request;
790         keybinding->next = keybindings[keymap];
791         keybindings[keymap] = keybinding;
792 }
793
794 /* Looks for a key binding first in the given map, then in the generic map, and
795  * lastly in the default keybindings. */
796 static enum request
797 get_keybinding(enum keymap keymap, int key)
798 {
799         struct keybinding *kbd;
800         int i;
801
802         for (kbd = keybindings[keymap]; kbd; kbd = kbd->next)
803                 if (kbd->alias == key)
804                         return kbd->request;
805
806         for (kbd = keybindings[KEYMAP_GENERIC]; kbd; kbd = kbd->next)
807                 if (kbd->alias == key)
808                         return kbd->request;
809
810         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++)
811                 if (default_keybindings[i].alias == key)
812                         return default_keybindings[i].request;
813
814         return (enum request) key;
815 }
816
817
818 struct key {
819         char *name;
820         int value;
821 };
822
823 static struct key key_table[] = {
824         { "Enter",      KEY_RETURN },
825         { "Space",      ' ' },
826         { "Backspace",  KEY_BACKSPACE },
827         { "Tab",        KEY_TAB },
828         { "Escape",     KEY_ESC },
829         { "Left",       KEY_LEFT },
830         { "Right",      KEY_RIGHT },
831         { "Up",         KEY_UP },
832         { "Down",       KEY_DOWN },
833         { "Insert",     KEY_IC },
834         { "Delete",     KEY_DC },
835         { "Hash",       '#' },
836         { "Home",       KEY_HOME },
837         { "End",        KEY_END },
838         { "PageUp",     KEY_PPAGE },
839         { "PageDown",   KEY_NPAGE },
840         { "F1",         KEY_F(1) },
841         { "F2",         KEY_F(2) },
842         { "F3",         KEY_F(3) },
843         { "F4",         KEY_F(4) },
844         { "F5",         KEY_F(5) },
845         { "F6",         KEY_F(6) },
846         { "F7",         KEY_F(7) },
847         { "F8",         KEY_F(8) },
848         { "F9",         KEY_F(9) },
849         { "F10",        KEY_F(10) },
850         { "F11",        KEY_F(11) },
851         { "F12",        KEY_F(12) },
852 };
853
854 static int
855 get_key_value(const char *name)
856 {
857         int i;
858
859         for (i = 0; i < ARRAY_SIZE(key_table); i++)
860                 if (!strcasecmp(key_table[i].name, name))
861                         return key_table[i].value;
862
863         if (strlen(name) == 1 && isprint(*name))
864                 return (int) *name;
865
866         return ERR;
867 }
868
869 static char *
870 get_key(enum request request)
871 {
872         static char buf[BUFSIZ];
873         static char key_char[] = "'X'";
874         size_t pos = 0;
875         char *sep = "    ";
876         int i;
877
878         buf[pos] = 0;
879
880         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
881                 struct keybinding *keybinding = &default_keybindings[i];
882                 char *seq = NULL;
883                 int key;
884
885                 if (keybinding->request != request)
886                         continue;
887
888                 for (key = 0; key < ARRAY_SIZE(key_table); key++)
889                         if (key_table[key].value == keybinding->alias)
890                                 seq = key_table[key].name;
891
892                 if (seq == NULL &&
893                     keybinding->alias < 127 &&
894                     isprint(keybinding->alias)) {
895                         key_char[1] = (char) keybinding->alias;
896                         seq = key_char;
897                 }
898
899                 if (!seq)
900                         seq = "'?'";
901
902                 if (!string_format_from(buf, &pos, "%s%s", sep, seq))
903                         return "Too many keybindings!";
904                 sep = ", ";
905         }
906
907         return buf;
908 }
909
910
911 /*
912  * User config file handling.
913  */
914
915 static struct int_map color_map[] = {
916 #define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name }
917         COLOR_MAP(DEFAULT),
918         COLOR_MAP(BLACK),
919         COLOR_MAP(BLUE),
920         COLOR_MAP(CYAN),
921         COLOR_MAP(GREEN),
922         COLOR_MAP(MAGENTA),
923         COLOR_MAP(RED),
924         COLOR_MAP(WHITE),
925         COLOR_MAP(YELLOW),
926 };
927
928 #define set_color(color, name) \
929         set_from_int_map(color_map, ARRAY_SIZE(color_map), color, name, strlen(name))
930
931 static struct int_map attr_map[] = {
932 #define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name }
933         ATTR_MAP(NORMAL),
934         ATTR_MAP(BLINK),
935         ATTR_MAP(BOLD),
936         ATTR_MAP(DIM),
937         ATTR_MAP(REVERSE),
938         ATTR_MAP(STANDOUT),
939         ATTR_MAP(UNDERLINE),
940 };
941
942 #define set_attribute(attr, name) \
943         set_from_int_map(attr_map, ARRAY_SIZE(attr_map), attr, name, strlen(name))
944
945 static int   config_lineno;
946 static bool  config_errors;
947 static char *config_msg;
948
949 /* Wants: object fgcolor bgcolor [attr] */
950 static int
951 option_color_command(int argc, char *argv[])
952 {
953         struct line_info *info;
954
955         if (argc != 3 && argc != 4) {
956                 config_msg = "Wrong number of arguments given to color command";
957                 return ERR;
958         }
959
960         info = get_line_info(argv[0], strlen(argv[0]));
961         if (!info) {
962                 config_msg = "Unknown color name";
963                 return ERR;
964         }
965
966         if (set_color(&info->fg, argv[1]) == ERR ||
967             set_color(&info->bg, argv[2]) == ERR) {
968                 config_msg = "Unknown color";
969                 return ERR;
970         }
971
972         if (argc == 4 && set_attribute(&info->attr, argv[3]) == ERR) {
973                 config_msg = "Unknown attribute";
974                 return ERR;
975         }
976
977         return OK;
978 }
979
980 /* Wants: name = value */
981 static int
982 option_set_command(int argc, char *argv[])
983 {
984         if (argc != 3) {
985                 config_msg = "Wrong number of arguments given to set command";
986                 return ERR;
987         }
988
989         if (strcmp(argv[1], "=")) {
990                 config_msg = "No value assigned";
991                 return ERR;
992         }
993
994         if (!strcmp(argv[0], "show-rev-graph")) {
995                 opt_rev_graph = (!strcmp(argv[2], "1") ||
996                                  !strcmp(argv[2], "true") ||
997                                  !strcmp(argv[2], "yes"));
998                 return OK;
999         }
1000
1001         if (!strcmp(argv[0], "line-number-interval")) {
1002                 opt_num_interval = atoi(argv[2]);
1003                 return OK;
1004         }
1005
1006         if (!strcmp(argv[0], "tab-size")) {
1007                 opt_tab_size = atoi(argv[2]);
1008                 return OK;
1009         }
1010
1011         if (!strcmp(argv[0], "commit-encoding")) {
1012                 char *arg = argv[2];
1013                 int delimiter = *arg;
1014                 int i;
1015
1016                 switch (delimiter) {
1017                 case '"':
1018                 case '\'':
1019                         for (arg++, i = 0; arg[i]; i++)
1020                                 if (arg[i] == delimiter) {
1021                                         arg[i] = 0;
1022                                         break;
1023                                 }
1024                 default:
1025                         string_copy(opt_encoding, arg);
1026                         return OK;
1027                 }
1028         }
1029
1030         config_msg = "Unknown variable name";
1031         return ERR;
1032 }
1033
1034 /* Wants: mode request key */
1035 static int
1036 option_bind_command(int argc, char *argv[])
1037 {
1038         enum request request;
1039         int keymap;
1040         int key;
1041
1042         if (argc != 3) {
1043                 config_msg = "Wrong number of arguments given to bind command";
1044                 return ERR;
1045         }
1046
1047         if (set_keymap(&keymap, argv[0]) == ERR) {
1048                 config_msg = "Unknown key map";
1049                 return ERR;
1050         }
1051
1052         key = get_key_value(argv[1]);
1053         if (key == ERR) {
1054                 config_msg = "Unknown key";
1055                 return ERR;
1056         }
1057
1058         request = get_request(argv[2]);
1059         if (request == REQ_UNKNOWN) {
1060                 config_msg = "Unknown request name";
1061                 return ERR;
1062         }
1063
1064         add_keybinding(keymap, request, key);
1065
1066         return OK;
1067 }
1068
1069 static int
1070 set_option(char *opt, char *value)
1071 {
1072         char *argv[16];
1073         int valuelen;
1074         int argc = 0;
1075
1076         /* Tokenize */
1077         while (argc < ARRAY_SIZE(argv) && (valuelen = strcspn(value, " \t"))) {
1078                 argv[argc++] = value;
1079
1080                 value += valuelen;
1081                 if (!*value)
1082                         break;
1083
1084                 *value++ = 0;
1085                 while (isspace(*value))
1086                         value++;
1087         }
1088
1089         if (!strcmp(opt, "color"))
1090                 return option_color_command(argc, argv);
1091
1092         if (!strcmp(opt, "set"))
1093                 return option_set_command(argc, argv);
1094
1095         if (!strcmp(opt, "bind"))
1096                 return option_bind_command(argc, argv);
1097
1098         config_msg = "Unknown option command";
1099         return ERR;
1100 }
1101
1102 static int
1103 read_option(char *opt, int optlen, char *value, int valuelen)
1104 {
1105         int status = OK;
1106
1107         config_lineno++;
1108         config_msg = "Internal error";
1109
1110         /* Check for comment markers, since read_properties() will
1111          * only ensure opt and value are split at first " \t". */
1112         optlen = strcspn(opt, "#");
1113         if (optlen == 0)
1114                 return OK;
1115
1116         if (opt[optlen] != 0) {
1117                 config_msg = "No option value";
1118                 status = ERR;
1119
1120         }  else {
1121                 /* Look for comment endings in the value. */
1122                 int len = strcspn(value, "#");
1123
1124                 if (len < valuelen) {
1125                         valuelen = len;
1126                         value[valuelen] = 0;
1127                 }
1128
1129                 status = set_option(opt, value);
1130         }
1131
1132         if (status == ERR) {
1133                 fprintf(stderr, "Error on line %d, near '%.*s': %s\n",
1134                         config_lineno, optlen, opt, config_msg);
1135                 config_errors = TRUE;
1136         }
1137
1138         /* Always keep going if errors are encountered. */
1139         return OK;
1140 }
1141
1142 static int
1143 load_options(void)
1144 {
1145         char *home = getenv("HOME");
1146         char buf[SIZEOF_STR];
1147         FILE *file;
1148
1149         config_lineno = 0;
1150         config_errors = FALSE;
1151
1152         if (!home || !string_format(buf, "%s/.tigrc", home))
1153                 return ERR;
1154
1155         /* It's ok that the file doesn't exist. */
1156         file = fopen(buf, "r");
1157         if (!file)
1158                 return OK;
1159
1160         if (read_properties(file, " \t", read_option) == ERR ||
1161             config_errors == TRUE)
1162                 fprintf(stderr, "Errors while loading %s.\n", buf);
1163
1164         return OK;
1165 }
1166
1167
1168 /*
1169  * The viewer
1170  */
1171
1172 struct view;
1173 struct view_ops;
1174
1175 /* The display array of active views and the index of the current view. */
1176 static struct view *display[2];
1177 static unsigned int current_view;
1178
1179 #define foreach_displayed_view(view, i) \
1180         for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
1181
1182 #define displayed_views()       (display[1] != NULL ? 2 : 1)
1183
1184 /* Current head and commit ID */
1185 static char ref_blob[SIZEOF_REF]        = "";
1186 static char ref_commit[SIZEOF_REF]      = "HEAD";
1187 static char ref_head[SIZEOF_REF]        = "HEAD";
1188
1189 struct view {
1190         const char *name;       /* View name */
1191         const char *cmd_fmt;    /* Default command line format */
1192         const char *cmd_env;    /* Command line set via environment */
1193         const char *id;         /* Points to either of ref_{head,commit,blob} */
1194
1195         struct view_ops *ops;   /* View operations */
1196
1197         enum keymap keymap;     /* What keymap does this view have */
1198
1199         char cmd[SIZEOF_STR];   /* Command buffer */
1200         char ref[SIZEOF_REF];   /* Hovered commit reference */
1201         char vid[SIZEOF_REF];   /* View ID. Set to id member when updating. */
1202
1203         int height, width;      /* The width and height of the main window */
1204         WINDOW *win;            /* The main window */
1205         WINDOW *title;          /* The title window living below the main window */
1206
1207         /* Navigation */
1208         unsigned long offset;   /* Offset of the window top */
1209         unsigned long lineno;   /* Current line number */
1210
1211         /* Searching */
1212         char grep[SIZEOF_STR];  /* Search string */
1213         regex_t *regex;         /* Pre-compiled regex */
1214
1215         /* If non-NULL, points to the view that opened this view. If this view
1216          * is closed tig will switch back to the parent view. */
1217         struct view *parent;
1218
1219         /* Buffering */
1220         unsigned long lines;    /* Total number of lines */
1221         struct line *line;      /* Line index */
1222         unsigned long line_size;/* Total number of allocated lines */
1223         unsigned int digits;    /* Number of digits in the lines member. */
1224
1225         /* Loading */
1226         FILE *pipe;
1227         time_t start_time;
1228 };
1229
1230 struct view_ops {
1231         /* What type of content being displayed. Used in the title bar. */
1232         const char *type;
1233         /* Draw one line; @lineno must be < view->height. */
1234         bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected);
1235         /* Read one line; updates view->line. */
1236         bool (*read)(struct view *view, char *data);
1237         /* Depending on view, change display based on current line. */
1238         bool (*enter)(struct view *view, struct line *line);
1239         /* Search for regex in a line. */
1240         bool (*grep)(struct view *view, struct line *line);
1241         /* Select line */
1242         void (*select)(struct view *view, struct line *line);
1243 };
1244
1245 static struct view_ops pager_ops;
1246 static struct view_ops main_ops;
1247 static struct view_ops tree_ops;
1248 static struct view_ops blob_ops;
1249
1250 #define VIEW_STR(name, cmd, env, ref, ops, map) \
1251         { name, cmd, #env, ref, ops, map}
1252
1253 #define VIEW_(id, name, ops, ref) \
1254         VIEW_STR(name, TIG_##id##_CMD,  TIG_##id##_CMD, ref, ops, KEYMAP_##id)
1255
1256
1257 static struct view views[] = {
1258         VIEW_(MAIN,  "main",  &main_ops,  ref_head),
1259         VIEW_(DIFF,  "diff",  &pager_ops, ref_commit),
1260         VIEW_(LOG,   "log",   &pager_ops, ref_head),
1261         VIEW_(TREE,  "tree",  &tree_ops,  ref_commit),
1262         VIEW_(BLOB,  "blob",  &blob_ops,  ref_blob),
1263         VIEW_(HELP,  "help",  &pager_ops, "static"),
1264         VIEW_(PAGER, "pager", &pager_ops, "static"),
1265 };
1266
1267 #define VIEW(req) (&views[(req) - REQ_OFFSET - 1])
1268
1269 #define foreach_view(view, i) \
1270         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
1271
1272 #define view_is_displayed(view) \
1273         (view == display[0] || view == display[1])
1274
1275 static bool
1276 draw_view_line(struct view *view, unsigned int lineno)
1277 {
1278         struct line *line;
1279         bool selected = (view->offset + lineno == view->lineno);
1280
1281         assert(view_is_displayed(view));
1282
1283         if (view->offset + lineno >= view->lines)
1284                 return FALSE;
1285
1286         line = &view->line[view->offset + lineno];
1287
1288         if (selected) {
1289                 line->selected = TRUE;
1290                 view->ops->select(view, line);
1291         } else if (line->selected) {
1292                 line->selected = FALSE;
1293                 wmove(view->win, lineno, 0);
1294                 wclrtoeol(view->win);
1295         }
1296
1297         return view->ops->draw(view, line, lineno, selected);
1298 }
1299
1300 static void
1301 redraw_view_from(struct view *view, int lineno)
1302 {
1303         assert(0 <= lineno && lineno < view->height);
1304
1305         for (; lineno < view->height; lineno++) {
1306                 if (!draw_view_line(view, lineno))
1307                         break;
1308         }
1309
1310         redrawwin(view->win);
1311         wrefresh(view->win);
1312 }
1313
1314 static void
1315 redraw_view(struct view *view)
1316 {
1317         wclear(view->win);
1318         redraw_view_from(view, 0);
1319 }
1320
1321
1322 static void
1323 update_view_title(struct view *view)
1324 {
1325         assert(view_is_displayed(view));
1326
1327         if (view == display[current_view])
1328                 wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
1329         else
1330                 wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
1331
1332         werase(view->title);
1333         wmove(view->title, 0, 0);
1334
1335         if (*view->ref)
1336                 wprintw(view->title, "[%s] %s", view->name, view->ref);
1337         else
1338                 wprintw(view->title, "[%s]", view->name);
1339
1340         if (view->lines || view->pipe) {
1341                 unsigned int view_lines = view->offset + view->height;
1342                 unsigned int lines = view->lines
1343                                    ? MIN(view_lines, view->lines) * 100 / view->lines
1344                                    : 0;
1345
1346                 wprintw(view->title, " - %s %d of %d (%d%%)",
1347                         view->ops->type,
1348                         view->lineno + 1,
1349                         view->lines,
1350                         lines);
1351         }
1352
1353         if (view->pipe) {
1354                 time_t secs = time(NULL) - view->start_time;
1355
1356                 /* Three git seconds are a long time ... */
1357                 if (secs > 2)
1358                         wprintw(view->title, " %lds", secs);
1359         }
1360
1361         wmove(view->title, 0, view->width - 1);
1362         wrefresh(view->title);
1363 }
1364
1365 static void
1366 resize_display(void)
1367 {
1368         int offset, i;
1369         struct view *base = display[0];
1370         struct view *view = display[1] ? display[1] : display[0];
1371
1372         /* Setup window dimensions */
1373
1374         getmaxyx(stdscr, base->height, base->width);
1375
1376         /* Make room for the status window. */
1377         base->height -= 1;
1378
1379         if (view != base) {
1380                 /* Horizontal split. */
1381                 view->width   = base->width;
1382                 view->height  = SCALE_SPLIT_VIEW(base->height);
1383                 base->height -= view->height;
1384
1385                 /* Make room for the title bar. */
1386                 view->height -= 1;
1387         }
1388
1389         /* Make room for the title bar. */
1390         base->height -= 1;
1391
1392         offset = 0;
1393
1394         foreach_displayed_view (view, i) {
1395                 if (!view->win) {
1396                         view->win = newwin(view->height, 0, offset, 0);
1397                         if (!view->win)
1398                                 die("Failed to create %s view", view->name);
1399
1400                         scrollok(view->win, TRUE);
1401
1402                         view->title = newwin(1, 0, offset + view->height, 0);
1403                         if (!view->title)
1404                                 die("Failed to create title window");
1405
1406                 } else {
1407                         wresize(view->win, view->height, view->width);
1408                         mvwin(view->win,   offset, 0);
1409                         mvwin(view->title, offset + view->height, 0);
1410                 }
1411
1412                 offset += view->height + 1;
1413         }
1414 }
1415
1416 static void
1417 redraw_display(void)
1418 {
1419         struct view *view;
1420         int i;
1421
1422         foreach_displayed_view (view, i) {
1423                 redraw_view(view);
1424                 update_view_title(view);
1425         }
1426 }
1427
1428 static void
1429 update_display_cursor(void)
1430 {
1431         struct view *view = display[current_view];
1432
1433         /* Move the cursor to the right-most column of the cursor line.
1434          *
1435          * XXX: This could turn out to be a bit expensive, but it ensures that
1436          * the cursor does not jump around. */
1437         if (view->lines) {
1438                 wmove(view->win, view->lineno - view->offset, view->width - 1);
1439                 wrefresh(view->win);
1440         }
1441 }
1442
1443 /*
1444  * Navigation
1445  */
1446
1447 /* Scrolling backend */
1448 static void
1449 do_scroll_view(struct view *view, int lines)
1450 {
1451         bool redraw_current_line = FALSE;
1452
1453         /* The rendering expects the new offset. */
1454         view->offset += lines;
1455
1456         assert(0 <= view->offset && view->offset < view->lines);
1457         assert(lines);
1458
1459         /* Move current line into the view. */
1460         if (view->lineno < view->offset) {
1461                 view->lineno = view->offset;
1462                 redraw_current_line = TRUE;
1463         } else if (view->lineno >= view->offset + view->height) {
1464                 view->lineno = view->offset + view->height - 1;
1465                 redraw_current_line = TRUE;
1466         }
1467
1468         assert(view->offset <= view->lineno && view->lineno < view->lines);
1469
1470         /* Redraw the whole screen if scrolling is pointless. */
1471         if (view->height < ABS(lines)) {
1472                 redraw_view(view);
1473
1474         } else {
1475                 int line = lines > 0 ? view->height - lines : 0;
1476                 int end = line + ABS(lines);
1477
1478                 wscrl(view->win, lines);
1479
1480                 for (; line < end; line++) {
1481                         if (!draw_view_line(view, line))
1482                                 break;
1483                 }
1484
1485                 if (redraw_current_line)
1486                         draw_view_line(view, view->lineno - view->offset);
1487         }
1488
1489         redrawwin(view->win);
1490         wrefresh(view->win);
1491         report("");
1492 }
1493
1494 /* Scroll frontend */
1495 static void
1496 scroll_view(struct view *view, enum request request)
1497 {
1498         int lines = 1;
1499
1500         assert(view_is_displayed(view));
1501
1502         switch (request) {
1503         case REQ_SCROLL_PAGE_DOWN:
1504                 lines = view->height;
1505         case REQ_SCROLL_LINE_DOWN:
1506                 if (view->offset + lines > view->lines)
1507                         lines = view->lines - view->offset;
1508
1509                 if (lines == 0 || view->offset + view->height >= view->lines) {
1510                         report("Cannot scroll beyond the last line");
1511                         return;
1512                 }
1513                 break;
1514
1515         case REQ_SCROLL_PAGE_UP:
1516                 lines = view->height;
1517         case REQ_SCROLL_LINE_UP:
1518                 if (lines > view->offset)
1519                         lines = view->offset;
1520
1521                 if (lines == 0) {
1522                         report("Cannot scroll beyond the first line");
1523                         return;
1524                 }
1525
1526                 lines = -lines;
1527                 break;
1528
1529         default:
1530                 die("request %d not handled in switch", request);
1531         }
1532
1533         do_scroll_view(view, lines);
1534 }
1535
1536 /* Cursor moving */
1537 static void
1538 move_view(struct view *view, enum request request)
1539 {
1540         int scroll_steps = 0;
1541         int steps;
1542
1543         switch (request) {
1544         case REQ_MOVE_FIRST_LINE:
1545                 steps = -view->lineno;
1546                 break;
1547
1548         case REQ_MOVE_LAST_LINE:
1549                 steps = view->lines - view->lineno - 1;
1550                 break;
1551
1552         case REQ_MOVE_PAGE_UP:
1553                 steps = view->height > view->lineno
1554                       ? -view->lineno : -view->height;
1555                 break;
1556
1557         case REQ_MOVE_PAGE_DOWN:
1558                 steps = view->lineno + view->height >= view->lines
1559                       ? view->lines - view->lineno - 1 : view->height;
1560                 break;
1561
1562         case REQ_MOVE_UP:
1563                 steps = -1;
1564                 break;
1565
1566         case REQ_MOVE_DOWN:
1567                 steps = 1;
1568                 break;
1569
1570         default:
1571                 die("request %d not handled in switch", request);
1572         }
1573
1574         if (steps <= 0 && view->lineno == 0) {
1575                 report("Cannot move beyond the first line");
1576                 return;
1577
1578         } else if (steps >= 0 && view->lineno + 1 >= view->lines) {
1579                 report("Cannot move beyond the last line");
1580                 return;
1581         }
1582
1583         /* Move the current line */
1584         view->lineno += steps;
1585         assert(0 <= view->lineno && view->lineno < view->lines);
1586
1587         /* Check whether the view needs to be scrolled */
1588         if (view->lineno < view->offset ||
1589             view->lineno >= view->offset + view->height) {
1590                 scroll_steps = steps;
1591                 if (steps < 0 && -steps > view->offset) {
1592                         scroll_steps = -view->offset;
1593
1594                 } else if (steps > 0) {
1595                         if (view->lineno == view->lines - 1 &&
1596                             view->lines > view->height) {
1597                                 scroll_steps = view->lines - view->offset - 1;
1598                                 if (scroll_steps >= view->height)
1599                                         scroll_steps -= view->height - 1;
1600                         }
1601                 }
1602         }
1603
1604         if (!view_is_displayed(view)) {
1605                 view->offset += steps;
1606                 view->ops->select(view, &view->line[view->lineno]);
1607                 return;
1608         }
1609
1610         /* Repaint the old "current" line if we be scrolling */
1611         if (ABS(steps) < view->height)
1612                 draw_view_line(view, view->lineno - steps - view->offset);
1613
1614         if (scroll_steps) {
1615                 do_scroll_view(view, scroll_steps);
1616                 return;
1617         }
1618
1619         /* Draw the current line */
1620         draw_view_line(view, view->lineno - view->offset);
1621
1622         redrawwin(view->win);
1623         wrefresh(view->win);
1624         report("");
1625 }
1626
1627
1628 /*
1629  * Searching
1630  */
1631
1632 static void search_view(struct view *view, enum request request);
1633
1634 static bool
1635 find_next_line(struct view *view, unsigned long lineno, struct line *line)
1636 {
1637         assert(view_is_displayed(view));
1638
1639         if (!view->ops->grep(view, line))
1640                 return FALSE;
1641
1642         if (lineno - view->offset >= view->height) {
1643                 view->offset = lineno;
1644                 view->lineno = lineno;
1645                 redraw_view(view);
1646
1647         } else {
1648                 unsigned long old_lineno = view->lineno - view->offset;
1649
1650                 view->lineno = lineno;
1651                 draw_view_line(view, old_lineno);
1652
1653                 draw_view_line(view, view->lineno - view->offset);
1654                 redrawwin(view->win);
1655                 wrefresh(view->win);
1656         }
1657
1658         report("Line %ld matches '%s'", lineno + 1, view->grep);
1659         return TRUE;
1660 }
1661
1662 static void
1663 find_next(struct view *view, enum request request)
1664 {
1665         unsigned long lineno = view->lineno;
1666         int direction;
1667
1668         if (!*view->grep) {
1669                 if (!*opt_search)
1670                         report("No previous search");
1671                 else
1672                         search_view(view, request);
1673                 return;
1674         }
1675
1676         switch (request) {
1677         case REQ_SEARCH:
1678         case REQ_FIND_NEXT:
1679                 direction = 1;
1680                 break;
1681
1682         case REQ_SEARCH_BACK:
1683         case REQ_FIND_PREV:
1684                 direction = -1;
1685                 break;
1686
1687         default:
1688                 return;
1689         }
1690
1691         if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
1692                 lineno += direction;
1693
1694         /* Note, lineno is unsigned long so will wrap around in which case it
1695          * will become bigger than view->lines. */
1696         for (; lineno < view->lines; lineno += direction) {
1697                 struct line *line = &view->line[lineno];
1698
1699                 if (find_next_line(view, lineno, line))
1700                         return;
1701         }
1702
1703         report("No match found for '%s'", view->grep);
1704 }
1705
1706 static void
1707 search_view(struct view *view, enum request request)
1708 {
1709         int regex_err;
1710
1711         if (view->regex) {
1712                 regfree(view->regex);
1713                 *view->grep = 0;
1714         } else {
1715                 view->regex = calloc(1, sizeof(*view->regex));
1716                 if (!view->regex)
1717                         return;
1718         }
1719
1720         regex_err = regcomp(view->regex, opt_search, REG_EXTENDED);
1721         if (regex_err != 0) {
1722                 char buf[SIZEOF_STR] = "unknown error";
1723
1724                 regerror(regex_err, view->regex, buf, sizeof(buf));
1725                 report("Search failed: %s", buf);
1726                 return;
1727         }
1728
1729         string_copy(view->grep, opt_search);
1730
1731         find_next(view, request);
1732 }
1733
1734 /*
1735  * Incremental updating
1736  */
1737
1738 static void
1739 end_update(struct view *view)
1740 {
1741         if (!view->pipe)
1742                 return;
1743         set_nonblocking_input(FALSE);
1744         if (view->pipe == stdin)
1745                 fclose(view->pipe);
1746         else
1747                 pclose(view->pipe);
1748         view->pipe = NULL;
1749 }
1750
1751 static bool
1752 begin_update(struct view *view)
1753 {
1754         const char *id = view->id;
1755
1756         if (view->pipe)
1757                 end_update(view);
1758
1759         if (opt_cmd[0]) {
1760                 string_copy(view->cmd, opt_cmd);
1761                 opt_cmd[0] = 0;
1762                 /* When running random commands, the view ref could have become
1763                  * invalid so clear it. */
1764                 view->ref[0] = 0;
1765
1766         } else if (view == VIEW(REQ_VIEW_TREE)) {
1767                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1768
1769                 if (strcmp(view->vid, view->id))
1770                         opt_path[0] = 0;
1771
1772                 if (!string_format(view->cmd, format, id, opt_path))
1773                         return FALSE;
1774
1775         } else {
1776                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1777
1778                 if (!string_format(view->cmd, format, id, id, id, id, id))
1779                         return FALSE;
1780         }
1781
1782         /* Special case for the pager view. */
1783         if (opt_pipe) {
1784                 view->pipe = opt_pipe;
1785                 opt_pipe = NULL;
1786         } else {
1787                 view->pipe = popen(view->cmd, "r");
1788         }
1789
1790         if (!view->pipe)
1791                 return FALSE;
1792
1793         set_nonblocking_input(TRUE);
1794
1795         view->offset = 0;
1796         view->lines  = 0;
1797         view->lineno = 0;
1798         string_copy(view->vid, id);
1799
1800         if (view->line) {
1801                 int i;
1802
1803                 for (i = 0; i < view->lines; i++)
1804                         if (view->line[i].data)
1805                                 free(view->line[i].data);
1806
1807                 free(view->line);
1808                 view->line = NULL;
1809         }
1810
1811         view->start_time = time(NULL);
1812
1813         return TRUE;
1814 }
1815
1816 static struct line *
1817 realloc_lines(struct view *view, size_t line_size)
1818 {
1819         struct line *tmp = realloc(view->line, sizeof(*view->line) * line_size);
1820
1821         if (!tmp)
1822                 return NULL;
1823
1824         view->line = tmp;
1825         view->line_size = line_size;
1826         return view->line;
1827 }
1828
1829 static bool
1830 update_view(struct view *view)
1831 {
1832         char in_buffer[BUFSIZ];
1833         char out_buffer[BUFSIZ * 2];
1834         char *line;
1835         /* The number of lines to read. If too low it will cause too much
1836          * redrawing (and possible flickering), if too high responsiveness
1837          * will suffer. */
1838         unsigned long lines = view->height;
1839         int redraw_from = -1;
1840
1841         if (!view->pipe)
1842                 return TRUE;
1843
1844         /* Only redraw if lines are visible. */
1845         if (view->offset + view->height >= view->lines)
1846                 redraw_from = view->lines - view->offset;
1847
1848         /* FIXME: This is probably not perfect for backgrounded views. */
1849         if (!realloc_lines(view, view->lines + lines))
1850                 goto alloc_error;
1851
1852         while ((line = fgets(in_buffer, sizeof(in_buffer), view->pipe))) {
1853                 size_t linelen = strlen(line);
1854
1855                 if (linelen)
1856                         line[linelen - 1] = 0;
1857
1858                 if (opt_iconv != ICONV_NONE) {
1859                         char *inbuf = line;
1860                         size_t inlen = linelen;
1861
1862                         char *outbuf = out_buffer;
1863                         size_t outlen = sizeof(out_buffer);
1864
1865                         size_t ret;
1866
1867                         ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
1868                         if (ret != (size_t) -1) {
1869                                 line = out_buffer;
1870                                 linelen = strlen(out_buffer);
1871                         }
1872                 }
1873
1874                 if (!view->ops->read(view, line))
1875                         goto alloc_error;
1876
1877                 if (lines-- == 1)
1878                         break;
1879         }
1880
1881         {
1882                 int digits;
1883
1884                 lines = view->lines;
1885                 for (digits = 0; lines; digits++)
1886                         lines /= 10;
1887
1888                 /* Keep the displayed view in sync with line number scaling. */
1889                 if (digits != view->digits) {
1890                         view->digits = digits;
1891                         redraw_from = 0;
1892                 }
1893         }
1894
1895         if (!view_is_displayed(view))
1896                 goto check_pipe;
1897
1898         if (view == VIEW(REQ_VIEW_TREE)) {
1899                 /* Clear the view and redraw everything since the tree sorting
1900                  * might have rearranged things. */
1901                 redraw_view(view);
1902
1903         } else if (redraw_from >= 0) {
1904                 /* If this is an incremental update, redraw the previous line
1905                  * since for commits some members could have changed when
1906                  * loading the main view. */
1907                 if (redraw_from > 0)
1908                         redraw_from--;
1909
1910                 /* Incrementally draw avoids flickering. */
1911                 redraw_view_from(view, redraw_from);
1912         }
1913
1914         /* Update the title _after_ the redraw so that if the redraw picks up a
1915          * commit reference in view->ref it'll be available here. */
1916         update_view_title(view);
1917
1918 check_pipe:
1919         if (ferror(view->pipe)) {
1920                 report("Failed to read: %s", strerror(errno));
1921                 goto end;
1922
1923         } else if (feof(view->pipe)) {
1924                 report("");
1925                 goto end;
1926         }
1927
1928         return TRUE;
1929
1930 alloc_error:
1931         report("Allocation failure");
1932
1933 end:
1934         end_update(view);
1935         return FALSE;
1936 }
1937
1938
1939 /*
1940  * View opening
1941  */
1942
1943 static void open_help_view(struct view *view)
1944 {
1945         char buf[BUFSIZ];
1946         int lines = ARRAY_SIZE(req_info) + 2;
1947         int i;
1948
1949         if (view->lines > 0)
1950                 return;
1951
1952         for (i = 0; i < ARRAY_SIZE(req_info); i++)
1953                 if (!req_info[i].request)
1954                         lines++;
1955
1956         view->line = calloc(lines, sizeof(*view->line));
1957         if (!view->line) {
1958                 report("Allocation failure");
1959                 return;
1960         }
1961
1962         view->ops->read(view, "Quick reference for tig keybindings:");
1963
1964         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
1965                 char *key;
1966
1967                 if (!req_info[i].request) {
1968                         view->ops->read(view, "");
1969                         view->ops->read(view, req_info[i].help);
1970                         continue;
1971                 }
1972
1973                 key = get_key(req_info[i].request);
1974                 if (!string_format(buf, "%-25s %s", key, req_info[i].help))
1975                         continue;
1976
1977                 view->ops->read(view, buf);
1978         }
1979 }
1980
1981 enum open_flags {
1982         OPEN_DEFAULT = 0,       /* Use default view switching. */
1983         OPEN_SPLIT = 1,         /* Split current view. */
1984         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
1985         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
1986 };
1987
1988 static void
1989 open_view(struct view *prev, enum request request, enum open_flags flags)
1990 {
1991         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
1992         bool split = !!(flags & OPEN_SPLIT);
1993         bool reload = !!(flags & OPEN_RELOAD);
1994         struct view *view = VIEW(request);
1995         int nviews = displayed_views();
1996         struct view *base_view = display[0];
1997
1998         if (view == prev && nviews == 1 && !reload) {
1999                 report("Already in %s view", view->name);
2000                 return;
2001         }
2002
2003         if (view == VIEW(REQ_VIEW_HELP)) {
2004                 open_help_view(view);
2005
2006         } else if ((reload || strcmp(view->vid, view->id)) &&
2007                    !begin_update(view)) {
2008                 report("Failed to load %s view", view->name);
2009                 return;
2010         }
2011
2012         if (split) {
2013                 display[1] = view;
2014                 if (!backgrounded)
2015                         current_view = 1;
2016         } else {
2017                 /* Maximize the current view. */
2018                 memset(display, 0, sizeof(display));
2019                 current_view = 0;
2020                 display[current_view] = view;
2021         }
2022
2023         /* Resize the view when switching between split- and full-screen,
2024          * or when switching between two different full-screen views. */
2025         if (nviews != displayed_views() ||
2026             (nviews == 1 && base_view != display[0]))
2027                 resize_display();
2028
2029         if (split && prev->lineno - prev->offset >= prev->height) {
2030                 /* Take the title line into account. */
2031                 int lines = prev->lineno - prev->offset - prev->height + 1;
2032
2033                 /* Scroll the view that was split if the current line is
2034                  * outside the new limited view. */
2035                 do_scroll_view(prev, lines);
2036         }
2037
2038         if (prev && view != prev) {
2039                 if (split && !backgrounded) {
2040                         /* "Blur" the previous view. */
2041                         update_view_title(prev);
2042                 }
2043
2044                 view->parent = prev;
2045         }
2046
2047         if (view->pipe && view->lines == 0) {
2048                 /* Clear the old view and let the incremental updating refill
2049                  * the screen. */
2050                 wclear(view->win);
2051                 report("");
2052         } else {
2053                 redraw_view(view);
2054                 report("");
2055         }
2056
2057         /* If the view is backgrounded the above calls to report()
2058          * won't redraw the view title. */
2059         if (backgrounded)
2060                 update_view_title(view);
2061 }
2062
2063
2064 /*
2065  * User request switch noodle
2066  */
2067
2068 static int
2069 view_driver(struct view *view, enum request request)
2070 {
2071         int i;
2072
2073         switch (request) {
2074         case REQ_MOVE_UP:
2075         case REQ_MOVE_DOWN:
2076         case REQ_MOVE_PAGE_UP:
2077         case REQ_MOVE_PAGE_DOWN:
2078         case REQ_MOVE_FIRST_LINE:
2079         case REQ_MOVE_LAST_LINE:
2080                 move_view(view, request);
2081                 break;
2082
2083         case REQ_SCROLL_LINE_DOWN:
2084         case REQ_SCROLL_LINE_UP:
2085         case REQ_SCROLL_PAGE_DOWN:
2086         case REQ_SCROLL_PAGE_UP:
2087                 scroll_view(view, request);
2088                 break;
2089
2090         case REQ_VIEW_BLOB:
2091                 if (!ref_blob[0]) {
2092                         report("No file chosen, press 't' to open tree view");
2093                         break;
2094                 }
2095                 /* Fall-through */
2096         case REQ_VIEW_MAIN:
2097         case REQ_VIEW_DIFF:
2098         case REQ_VIEW_LOG:
2099         case REQ_VIEW_TREE:
2100         case REQ_VIEW_HELP:
2101         case REQ_VIEW_PAGER:
2102                 open_view(view, request, OPEN_DEFAULT);
2103                 break;
2104
2105         case REQ_NEXT:
2106         case REQ_PREVIOUS:
2107                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
2108
2109                 if ((view == VIEW(REQ_VIEW_DIFF) &&
2110                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
2111                    (view == VIEW(REQ_VIEW_BLOB) &&
2112                      view->parent == VIEW(REQ_VIEW_TREE))) {
2113                         view = view->parent;
2114                         move_view(view, request);
2115                         if (view_is_displayed(view))
2116                                 update_view_title(view);
2117                 } else {
2118                         move_view(view, request);
2119                         break;
2120                 }
2121                 /* Fall-through */
2122
2123         case REQ_ENTER:
2124                 if (!view->lines) {
2125                         report("Nothing to enter");
2126                         break;
2127                 }
2128                 return view->ops->enter(view, &view->line[view->lineno]);
2129
2130         case REQ_VIEW_NEXT:
2131         {
2132                 int nviews = displayed_views();
2133                 int next_view = (current_view + 1) % nviews;
2134
2135                 if (next_view == current_view) {
2136                         report("Only one view is displayed");
2137                         break;
2138                 }
2139
2140                 current_view = next_view;
2141                 /* Blur out the title of the previous view. */
2142                 update_view_title(view);
2143                 report("");
2144                 break;
2145         }
2146         case REQ_TOGGLE_LINENO:
2147                 opt_line_number = !opt_line_number;
2148                 redraw_display();
2149                 break;
2150
2151         case REQ_TOGGLE_REV_GRAPH:
2152                 opt_rev_graph = !opt_rev_graph;
2153                 redraw_display();
2154                 break;
2155
2156         case REQ_PROMPT:
2157                 /* Always reload^Wrerun commands from the prompt. */
2158                 open_view(view, opt_request, OPEN_RELOAD);
2159                 break;
2160
2161         case REQ_SEARCH:
2162         case REQ_SEARCH_BACK:
2163                 search_view(view, request);
2164                 break;
2165
2166         case REQ_FIND_NEXT:
2167         case REQ_FIND_PREV:
2168                 find_next(view, request);
2169                 break;
2170
2171         case REQ_STOP_LOADING:
2172                 for (i = 0; i < ARRAY_SIZE(views); i++) {
2173                         view = &views[i];
2174                         if (view->pipe)
2175                                 report("Stopped loading the %s view", view->name),
2176                         end_update(view);
2177                 }
2178                 break;
2179
2180         case REQ_SHOW_VERSION:
2181                 report("%s (built %s)", VERSION, __DATE__);
2182                 return TRUE;
2183
2184         case REQ_SCREEN_RESIZE:
2185                 resize_display();
2186                 /* Fall-through */
2187         case REQ_SCREEN_REDRAW:
2188                 redraw_display();
2189                 break;
2190
2191         case REQ_NONE:
2192                 doupdate();
2193                 return TRUE;
2194
2195         case REQ_VIEW_CLOSE:
2196                 /* XXX: Mark closed views by letting view->parent point to the
2197                  * view itself. Parents to closed view should never be
2198                  * followed. */
2199                 if (view->parent &&
2200                     view->parent->parent != view->parent) {
2201                         memset(display, 0, sizeof(display));
2202                         current_view = 0;
2203                         display[current_view] = view->parent;
2204                         view->parent = view;
2205                         resize_display();
2206                         redraw_display();
2207                         break;
2208                 }
2209                 /* Fall-through */
2210         case REQ_QUIT:
2211                 return FALSE;
2212
2213         default:
2214                 /* An unknown key will show most commonly used commands. */
2215                 report("Unknown key, press 'h' for help");
2216                 return TRUE;
2217         }
2218
2219         return TRUE;
2220 }
2221
2222
2223 /*
2224  * Pager backend
2225  */
2226
2227 static bool
2228 pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2229 {
2230         char *text = line->data;
2231         enum line_type type = line->type;
2232         int textlen = strlen(text);
2233         int attr;
2234
2235         wmove(view->win, lineno, 0);
2236
2237         if (selected) {
2238                 type = LINE_CURSOR;
2239                 wchgat(view->win, -1, 0, type, NULL);
2240         }
2241
2242         attr = get_line_attr(type);
2243         wattrset(view->win, attr);
2244
2245         if (opt_line_number || opt_tab_size < TABSIZE) {
2246                 static char spaces[] = "                    ";
2247                 int col_offset = 0, col = 0;
2248
2249                 if (opt_line_number) {
2250                         unsigned long real_lineno = view->offset + lineno + 1;
2251
2252                         if (real_lineno == 1 ||
2253                             (real_lineno % opt_num_interval) == 0) {
2254                                 wprintw(view->win, "%.*d", view->digits, real_lineno);
2255
2256                         } else {
2257                                 waddnstr(view->win, spaces,
2258                                          MIN(view->digits, STRING_SIZE(spaces)));
2259                         }
2260                         waddstr(view->win, ": ");
2261                         col_offset = view->digits + 2;
2262                 }
2263
2264                 while (text && col_offset + col < view->width) {
2265                         int cols_max = view->width - col_offset - col;
2266                         char *pos = text;
2267                         int cols;
2268
2269                         if (*text == '\t') {
2270                                 text++;
2271                                 assert(sizeof(spaces) > TABSIZE);
2272                                 pos = spaces;
2273                                 cols = opt_tab_size - (col % opt_tab_size);
2274
2275                         } else {
2276                                 text = strchr(text, '\t');
2277                                 cols = line ? text - pos : strlen(pos);
2278                         }
2279
2280                         waddnstr(view->win, pos, MIN(cols, cols_max));
2281                         col += cols;
2282                 }
2283
2284         } else {
2285                 int col = 0, pos = 0;
2286
2287                 for (; pos < textlen && col < view->width; pos++, col++)
2288                         if (text[pos] == '\t')
2289                                 col += TABSIZE - (col % TABSIZE) - 1;
2290
2291                 waddnstr(view->win, text, pos);
2292         }
2293
2294         return TRUE;
2295 }
2296
2297 static bool
2298 add_describe_ref(char *buf, size_t *bufpos, char *commit_id, const char *sep)
2299 {
2300         char refbuf[SIZEOF_STR];
2301         char *ref = NULL;
2302         FILE *pipe;
2303
2304         if (!string_format(refbuf, "git describe %s", commit_id))
2305                 return TRUE;
2306
2307         pipe = popen(refbuf, "r");
2308         if (!pipe)
2309                 return TRUE;
2310
2311         if ((ref = fgets(refbuf, sizeof(refbuf), pipe)))
2312                 ref = chomp_string(ref);
2313         pclose(pipe);
2314
2315         if (!ref || !*ref)
2316                 return TRUE;
2317
2318         /* This is the only fatal call, since it can "corrupt" the buffer. */
2319         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
2320                 return FALSE;
2321
2322         return TRUE;
2323 }
2324
2325 static void
2326 add_pager_refs(struct view *view, struct line *line)
2327 {
2328         char buf[SIZEOF_STR];
2329         char *commit_id = line->data + STRING_SIZE("commit ");
2330         struct ref **refs;
2331         size_t bufpos = 0, refpos = 0;
2332         const char *sep = "Refs: ";
2333         bool is_tag = FALSE;
2334
2335         assert(line->type == LINE_COMMIT);
2336
2337         refs = get_refs(commit_id);
2338         if (!refs) {
2339                 if (view == VIEW(REQ_VIEW_DIFF))
2340                         goto try_add_describe_ref;
2341                 return;
2342         }
2343
2344         do {
2345                 struct ref *ref = refs[refpos];
2346                 char *fmt = ref->tag ? "%s[%s]" : "%s%s";
2347
2348                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
2349                         return;
2350                 sep = ", ";
2351                 if (ref->tag)
2352                         is_tag = TRUE;
2353         } while (refs[refpos++]->next);
2354
2355         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
2356 try_add_describe_ref:
2357                 /* Add <tag>-g<commit_id> "fake" reference. */
2358                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
2359                         return;
2360         }
2361
2362         if (bufpos == 0)
2363                 return;
2364
2365         if (!realloc_lines(view, view->line_size + 1))
2366                 return;
2367
2368         line = &view->line[view->lines];
2369         line->data = strdup(buf);
2370         if (!line->data)
2371                 return;
2372
2373         line->type = LINE_PP_REFS;
2374         view->lines++;
2375 }
2376
2377 static bool
2378 pager_read(struct view *view, char *data)
2379 {
2380         struct line *line = &view->line[view->lines];
2381
2382         line->data = strdup(data);
2383         if (!line->data)
2384                 return FALSE;
2385
2386         line->type = get_line_type(line->data);
2387         view->lines++;
2388
2389         if (line->type == LINE_COMMIT &&
2390             (view == VIEW(REQ_VIEW_DIFF) ||
2391              view == VIEW(REQ_VIEW_LOG)))
2392                 add_pager_refs(view, line);
2393
2394         return TRUE;
2395 }
2396
2397 static bool
2398 pager_enter(struct view *view, struct line *line)
2399 {
2400         int split = 0;
2401
2402         if (line->type == LINE_COMMIT &&
2403            (view == VIEW(REQ_VIEW_LOG) ||
2404             view == VIEW(REQ_VIEW_PAGER))) {
2405                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
2406                 split = 1;
2407         }
2408
2409         /* Always scroll the view even if it was split. That way
2410          * you can use Enter to scroll through the log view and
2411          * split open each commit diff. */
2412         scroll_view(view, REQ_SCROLL_LINE_DOWN);
2413
2414         /* FIXME: A minor workaround. Scrolling the view will call report("")
2415          * but if we are scrolling a non-current view this won't properly
2416          * update the view title. */
2417         if (split)
2418                 update_view_title(view);
2419
2420         return TRUE;
2421 }
2422
2423 static bool
2424 pager_grep(struct view *view, struct line *line)
2425 {
2426         regmatch_t pmatch;
2427         char *text = line->data;
2428
2429         if (!*text)
2430                 return FALSE;
2431
2432         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
2433                 return FALSE;
2434
2435         return TRUE;
2436 }
2437
2438 static void
2439 pager_select(struct view *view, struct line *line)
2440 {
2441         if (line->type == LINE_COMMIT) {
2442                 char *text = line->data;
2443
2444                 string_copy(view->ref, text + STRING_SIZE("commit "));
2445                 string_copy(ref_commit, view->ref);
2446         }
2447 }
2448
2449 static struct view_ops pager_ops = {
2450         "line",
2451         pager_draw,
2452         pager_read,
2453         pager_enter,
2454         pager_grep,
2455         pager_select,
2456 };
2457
2458
2459 /*
2460  * Tree backend
2461  */
2462
2463 /* Parse output from git-ls-tree(1):
2464  *
2465  * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile
2466  * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README
2467  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
2468  * 100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38 web.conf
2469  */
2470
2471 #define SIZEOF_TREE_ATTR \
2472         STRING_SIZE("100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38\t")
2473
2474 #define TREE_UP_FORMAT "040000 tree %s\t.."
2475
2476 static int
2477 tree_compare_entry(enum line_type type1, char *name1,
2478                    enum line_type type2, char *name2)
2479 {
2480         if (type1 != type2) {
2481                 if (type1 == LINE_TREE_DIR)
2482                         return -1;
2483                 return 1;
2484         }
2485
2486         return strcmp(name1, name2);
2487 }
2488
2489 static bool
2490 tree_read(struct view *view, char *text)
2491 {
2492         size_t textlen = strlen(text);
2493         char buf[SIZEOF_STR];
2494         unsigned long pos;
2495         enum line_type type;
2496         bool first_read = view->lines == 0;
2497
2498         if (textlen <= SIZEOF_TREE_ATTR)
2499                 return FALSE;
2500
2501         type = text[STRING_SIZE("100644 ")] == 't'
2502              ? LINE_TREE_DIR : LINE_TREE_FILE;
2503
2504         if (first_read) {
2505                 /* Add path info line */
2506                 if (string_format(buf, "Directory path /%s", opt_path) &&
2507                     realloc_lines(view, view->line_size + 1) &&
2508                     pager_read(view, buf))
2509                         view->line[view->lines - 1].type = LINE_DEFAULT;
2510                 else
2511                         return FALSE;
2512
2513                 /* Insert "link" to parent directory. */
2514                 if (*opt_path &&
2515                     string_format(buf, TREE_UP_FORMAT, view->ref) &&
2516                     realloc_lines(view, view->line_size + 1) &&
2517                     pager_read(view, buf))
2518                         view->line[view->lines - 1].type = LINE_TREE_DIR;
2519                 else if (*opt_path)
2520                         return FALSE;
2521         }
2522
2523         /* Strip the path part ... */
2524         if (*opt_path) {
2525                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
2526                 size_t striplen = strlen(opt_path);
2527                 char *path = text + SIZEOF_TREE_ATTR;
2528
2529                 if (pathlen > striplen)
2530                         memmove(path, path + striplen,
2531                                 pathlen - striplen + 1);
2532         }
2533
2534         /* Skip "Directory ..." and ".." line. */
2535         for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
2536                 struct line *line = &view->line[pos];
2537                 char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR;
2538                 char *path2 = text + SIZEOF_TREE_ATTR;
2539                 int cmp = tree_compare_entry(line->type, path1, type, path2);
2540
2541                 if (cmp <= 0)
2542                         continue;
2543
2544                 text = strdup(text);
2545                 if (!text)
2546                         return FALSE;
2547
2548                 if (view->lines > pos)
2549                         memmove(&view->line[pos + 1], &view->line[pos],
2550                                 (view->lines - pos) * sizeof(*line));
2551
2552                 line = &view->line[pos];
2553                 line->data = text;
2554                 line->type = type;
2555                 view->lines++;
2556                 return TRUE;
2557         }
2558
2559         if (!pager_read(view, text))
2560                 return FALSE;
2561
2562         /* Move the current line to the first tree entry. */
2563         if (first_read)
2564                 view->lineno++;
2565
2566         view->line[view->lines - 1].type = type;
2567         return TRUE;
2568 }
2569
2570 static bool
2571 tree_enter(struct view *view, struct line *line)
2572 {
2573         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
2574         enum request request;
2575
2576         switch (line->type) {
2577         case LINE_TREE_DIR:
2578                 /* Depending on whether it is a subdir or parent (updir?) link
2579                  * mangle the path buffer. */
2580                 if (line == &view->line[1] && *opt_path) {
2581                         size_t path_len = strlen(opt_path);
2582                         char *dirsep = opt_path + path_len - 1;
2583
2584                         while (dirsep > opt_path && dirsep[-1] != '/')
2585                                 dirsep--;
2586
2587                         dirsep[0] = 0;
2588
2589                 } else {
2590                         size_t pathlen = strlen(opt_path);
2591                         size_t origlen = pathlen;
2592                         char *data = line->data;
2593                         char *basename = data + SIZEOF_TREE_ATTR;
2594
2595                         if (!string_format_from(opt_path, &pathlen, "%s/", basename)) {
2596                                 opt_path[origlen] = 0;
2597                                 return TRUE;
2598                         }
2599                 }
2600
2601                 /* Trees and subtrees share the same ID, so they are not not
2602                  * unique like blobs. */
2603                 flags |= OPEN_RELOAD;
2604                 request = REQ_VIEW_TREE;
2605                 break;
2606
2607         case LINE_TREE_FILE:
2608                 request = REQ_VIEW_BLOB;
2609                 break;
2610
2611         default:
2612                 return TRUE;
2613         }
2614
2615         open_view(view, request, flags);
2616
2617         return TRUE;
2618 }
2619
2620 static void
2621 tree_select(struct view *view, struct line *line)
2622 {
2623         char *text = line->data;
2624
2625         text += STRING_SIZE("100644 blob ");
2626
2627         if (line->type == LINE_TREE_FILE) {
2628                 string_ncopy(ref_blob, text, 40);
2629                 /* Also update the blob view's ref, since all there must always
2630                  * be in sync. */
2631                 string_copy(VIEW(REQ_VIEW_BLOB)->ref, ref_blob);
2632
2633         } else if (line->type != LINE_TREE_DIR) {
2634                 return;
2635         }
2636
2637         string_ncopy(view->ref, text, 40);
2638 }
2639
2640 static struct view_ops tree_ops = {
2641         "file",
2642         pager_draw,
2643         tree_read,
2644         tree_enter,
2645         pager_grep,
2646         tree_select,
2647 };
2648
2649 static bool
2650 blob_read(struct view *view, char *line)
2651 {
2652         bool state = pager_read(view, line);
2653
2654         if (state == TRUE)
2655                 view->line[view->lines - 1].type = LINE_DEFAULT;
2656
2657         return state;
2658 }
2659
2660 static struct view_ops blob_ops = {
2661         "line",
2662         pager_draw,
2663         blob_read,
2664         pager_enter,
2665         pager_grep,
2666         pager_select,
2667 };
2668
2669
2670 /*
2671  * Main view backend
2672  */
2673
2674 struct commit {
2675         char id[SIZEOF_REV];            /* SHA1 ID. */
2676         char title[75];                 /* First line of the commit message. */
2677         char author[75];                /* Author of the commit. */
2678         struct tm time;                 /* Date from the author ident. */
2679         struct ref **refs;              /* Repository references. */
2680         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
2681         size_t graph_size;              /* The width of the graph array. */
2682 };
2683
2684 static bool
2685 main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2686 {
2687         char buf[DATE_COLS + 1];
2688         struct commit *commit = line->data;
2689         enum line_type type;
2690         int col = 0;
2691         size_t timelen;
2692         size_t authorlen;
2693         int trimmed = 1;
2694
2695         if (!*commit->author)
2696                 return FALSE;
2697
2698         wmove(view->win, lineno, col);
2699
2700         if (selected) {
2701                 type = LINE_CURSOR;
2702                 wattrset(view->win, get_line_attr(type));
2703                 wchgat(view->win, -1, 0, type, NULL);
2704
2705         } else {
2706                 type = LINE_MAIN_COMMIT;
2707                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
2708         }
2709
2710         timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
2711         waddnstr(view->win, buf, timelen);
2712         waddstr(view->win, " ");
2713
2714         col += DATE_COLS;
2715         wmove(view->win, lineno, col);
2716         if (type != LINE_CURSOR)
2717                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
2718
2719         if (opt_utf8) {
2720                 authorlen = utf8_length(commit->author, AUTHOR_COLS - 2, &col, &trimmed);
2721         } else {
2722                 authorlen = strlen(commit->author);
2723                 if (authorlen > AUTHOR_COLS - 2) {
2724                         authorlen = AUTHOR_COLS - 2;
2725                         trimmed = 1;
2726                 }
2727         }
2728
2729         if (trimmed) {
2730                 waddnstr(view->win, commit->author, authorlen);
2731                 if (type != LINE_CURSOR)
2732                         wattrset(view->win, get_line_attr(LINE_MAIN_DELIM));
2733                 waddch(view->win, '~');
2734         } else {
2735                 waddstr(view->win, commit->author);
2736         }
2737
2738         col += AUTHOR_COLS;
2739         if (type != LINE_CURSOR)
2740                 wattrset(view->win, A_NORMAL);
2741
2742         if (opt_rev_graph && commit->graph_size) {
2743                 size_t i;
2744
2745                 wmove(view->win, lineno, col);
2746                 /* Using waddch() instead of waddnstr() ensures that
2747                  * they'll be rendered correctly for the cursor line. */
2748                 for (i = 0; i < commit->graph_size; i++)
2749                         waddch(view->win, commit->graph[i]);
2750
2751                 col += commit->graph_size + 1;
2752         }
2753
2754         wmove(view->win, lineno, col);
2755
2756         if (commit->refs) {
2757                 size_t i = 0;
2758
2759                 do {
2760                         if (type == LINE_CURSOR)
2761                                 ;
2762                         else if (commit->refs[i]->tag)
2763                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
2764                         else
2765                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
2766                         waddstr(view->win, "[");
2767                         waddstr(view->win, commit->refs[i]->name);
2768                         waddstr(view->win, "]");
2769                         if (type != LINE_CURSOR)
2770                                 wattrset(view->win, A_NORMAL);
2771                         waddstr(view->win, " ");
2772                         col += strlen(commit->refs[i]->name) + STRING_SIZE("[] ");
2773                 } while (commit->refs[i++]->next);
2774         }
2775
2776         if (type != LINE_CURSOR)
2777                 wattrset(view->win, get_line_attr(type));
2778
2779         {
2780                 int titlelen = strlen(commit->title);
2781
2782                 if (col + titlelen > view->width)
2783                         titlelen = view->width - col;
2784
2785                 waddnstr(view->win, commit->title, titlelen);
2786         }
2787
2788         return TRUE;
2789 }
2790
2791
2792 struct rev_stack {
2793         struct rev_stack *prev, *next, *parents;
2794         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
2795         size_t size;
2796         struct commit *commit;
2797         size_t pos;
2798 };
2799
2800 /* Parents of the commit being visualized. */
2801 static struct rev_stack graph_parents[3];
2802
2803 /* The current stack of revisions on the graph. */
2804 static struct rev_stack graph_stacks[3] = {
2805         { &graph_stacks[2], &graph_stacks[1], &graph_parents[0] },
2806         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
2807         { &graph_stacks[1], &graph_stacks[0], &graph_parents[2] },
2808 };
2809
2810 static inline bool
2811 graph_parent_is_merge(struct rev_stack *graph)
2812 {
2813         return graph->parents->size > 1;
2814 }
2815
2816 static void
2817 reset_rev_graph(struct rev_stack *graph)
2818 {
2819         graph->size = graph->pos = 0;
2820         graph->commit = NULL;
2821         memset(graph->parents, 0, sizeof(*graph->parents));
2822 }
2823
2824 static inline void
2825 append_to_rev_graph(struct rev_stack *stack, chtype symbol)
2826 {
2827         if (stack->commit->graph_size < ARRAY_SIZE(stack->commit->graph) - 1)
2828                 stack->commit->graph[stack->commit->graph_size++] = symbol;
2829 }
2830
2831 static void
2832 push_rev_stack(struct rev_stack *stack, char *parent)
2833 {
2834         /* Combine duplicate parents lines. */
2835         if (stack->size > 0 &&
2836             !strncmp(stack->rev[stack->size - 1], parent, SIZEOF_REV))
2837                 return;
2838
2839         if (stack->size < SIZEOF_REVITEMS) {
2840                 string_ncopy(stack->rev[stack->size++], parent, SIZEOF_REV);
2841         }
2842 }
2843
2844 static void
2845 draw_rev_graph(struct rev_stack *graph)
2846 {
2847         chtype symbol, separator, line;
2848         size_t i;
2849
2850         /* Place the symbol for this commit. */
2851         if (graph->parents->size == 0)
2852                 symbol = REVGRAPH_INIT;
2853         else if (graph->parents->size > 1)
2854                 symbol = REVGRAPH_MERGE;
2855         else if (graph->pos >= graph->size)
2856                 symbol = REVGRAPH_BRANCH;
2857         else
2858                 symbol = REVGRAPH_COMMIT;
2859
2860         separator = ' ';
2861         line = REVGRAPH_LINE;
2862
2863         for (i = 0; i < graph->pos; i++) {
2864                 append_to_rev_graph(graph, line);
2865                 if (graph_parent_is_merge(graph->prev) &&
2866                     graph->prev->pos == i) {
2867                         separator = '`';
2868                         line = '.';
2869                 }
2870                 append_to_rev_graph(graph, separator);
2871         }
2872
2873         append_to_rev_graph(graph, symbol);
2874
2875         if (graph->prev->size > graph->size) {
2876                 separator = '\'';
2877                 line = ' ';
2878         } else {
2879                 separator = ' ';
2880                 line = REVGRAPH_LINE;
2881         }
2882         i++;
2883
2884         for (; i < graph->size; i++) {
2885                 append_to_rev_graph(graph, separator);
2886                 append_to_rev_graph(graph, line);
2887                 if (graph_parent_is_merge(graph->prev)) {
2888                         if (i < graph->prev->pos + graph->parents->size) {
2889                                 separator = '`';
2890                                 line = '.';
2891                         }
2892                 }
2893                 if (graph->prev->size > graph->size) {
2894                         separator = '/';
2895                         line = ' ';
2896                 }
2897         }
2898
2899         if (graph->prev->size > graph->size) {
2900                 append_to_rev_graph(graph, separator);
2901                 if (line != ' ')
2902                         append_to_rev_graph(graph, line);
2903         }
2904 }
2905
2906 void
2907 update_rev_graph(struct rev_stack *graph)
2908 {
2909         size_t i;
2910
2911         /* First traverse all lines of revisions up to the active one. */
2912         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
2913                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
2914                         break;
2915
2916                 push_rev_stack(graph->next, graph->rev[graph->pos]);
2917         }
2918
2919         for (i = 0; i < graph->parents->size; i++)
2920                 push_rev_stack(graph->next, graph->parents->rev[i]);
2921
2922         /* FIXME: Moving branches left and right when collapsing a branch. */
2923         for (i = graph->pos + 1; i < graph->size; i++)
2924                 push_rev_stack(graph->next, graph->rev[i]);
2925
2926         draw_rev_graph(graph);
2927
2928         graph = graph->prev;
2929
2930         if (graph_parent_is_merge(graph) &&
2931             graph->pos < graph->size - 1 &&
2932             graph->next->size == graph->size + graph->parents->size - 1) {
2933                 i = graph->pos + graph->parents->size - 1;
2934                 graph->commit->graph_size = i * 2;
2935                 while (i < graph->next->size - 1) {
2936                         append_to_rev_graph(graph, ' ');
2937                         append_to_rev_graph(graph, '\\');
2938                         i++;
2939                 }
2940         }
2941
2942         reset_rev_graph(graph);
2943 }
2944
2945 /* Reads git log --pretty=raw output and parses it into the commit struct. */
2946 static bool
2947 main_read(struct view *view, char *line)
2948 {
2949         static struct rev_stack *graph = graph_stacks;
2950         enum line_type type = get_line_type(line);
2951         struct commit *commit = view->lines
2952                               ? view->line[view->lines - 1].data : NULL;
2953
2954         switch (type) {
2955         case LINE_COMMIT:
2956                 commit = calloc(1, sizeof(struct commit));
2957                 if (!commit)
2958                         return FALSE;
2959
2960                 line += STRING_SIZE("commit ");
2961
2962                 view->line[view->lines++].data = commit;
2963                 string_copy(commit->id, line);
2964                 commit->refs = get_refs(commit->id);
2965                 graph->commit = commit;
2966                 break;
2967
2968         case LINE_PARENT:
2969                 if (commit) {
2970                         line += STRING_SIZE("parent ");
2971                         push_rev_stack(graph->parents, line);
2972                 }
2973                 break;
2974
2975         case LINE_AUTHOR:
2976         {
2977                 char *ident = line + STRING_SIZE("author ");
2978                 char *end = strchr(ident, '<');
2979
2980                 if (!commit)
2981                         break;
2982
2983                 update_rev_graph(graph);
2984                 graph = graph->next;
2985
2986                 if (end) {
2987                         char *email = end + 1;
2988
2989                         for (; end > ident && isspace(end[-1]); end--) ;
2990
2991                         if (end == ident && *email) {
2992                                 ident = email;
2993                                 end = strchr(ident, '>');
2994                                 for (; end > ident && isspace(end[-1]); end--) ;
2995                         }
2996                         *end = 0;
2997                 }
2998
2999                 /* End is NULL or ident meaning there's no author. */
3000                 if (end <= ident)
3001                         ident = "Unknown";
3002
3003                 string_copy(commit->author, ident);
3004
3005                 /* Parse epoch and timezone */
3006                 if (end) {
3007                         char *secs = strchr(end + 1, '>');
3008                         char *zone;
3009                         time_t time;
3010
3011                         if (!secs || secs[1] != ' ')
3012                                 break;
3013
3014                         secs += 2;
3015                         time = (time_t) atol(secs);
3016                         zone = strchr(secs, ' ');
3017                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
3018                                 long tz;
3019
3020                                 zone++;
3021                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
3022                                 tz += ('0' - zone[2]) * 60 * 60;
3023                                 tz += ('0' - zone[3]) * 60;
3024                                 tz += ('0' - zone[4]) * 60;
3025
3026                                 if (zone[0] == '-')
3027                                         tz = -tz;
3028
3029                                 time -= tz;
3030                         }
3031                         gmtime_r(&time, &commit->time);
3032                 }
3033                 break;
3034         }
3035         default:
3036                 if (!commit)
3037                         break;
3038
3039                 /* Fill in the commit title if it has not already been set. */
3040                 if (commit->title[0])
3041                         break;
3042
3043                 /* Require titles to start with a non-space character at the
3044                  * offset used by git log. */
3045                 /* FIXME: More gracefull handling of titles; append "..." to
3046                  * shortened titles, etc. */
3047                 if (strncmp(line, "    ", 4) ||
3048                     isspace(line[4]))
3049                         break;
3050
3051                 string_copy(commit->title, line + 4);
3052         }
3053
3054         return TRUE;
3055 }
3056
3057 static bool
3058 main_enter(struct view *view, struct line *line)
3059 {
3060         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3061
3062         open_view(view, REQ_VIEW_DIFF, flags);
3063         return TRUE;
3064 }
3065
3066 static bool
3067 main_grep(struct view *view, struct line *line)
3068 {
3069         struct commit *commit = line->data;
3070         enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
3071         char buf[DATE_COLS + 1];
3072         regmatch_t pmatch;
3073
3074         for (state = S_TITLE; state < S_END; state++) {
3075                 char *text;
3076
3077                 switch (state) {
3078                 case S_TITLE:   text = commit->title;   break;
3079                 case S_AUTHOR:  text = commit->author;  break;
3080                 case S_DATE:
3081                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
3082                                 continue;
3083                         text = buf;
3084                         break;
3085
3086                 default:
3087                         return FALSE;
3088                 }
3089
3090                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
3091                         return TRUE;
3092         }
3093
3094         return FALSE;
3095 }
3096
3097 static void
3098 main_select(struct view *view, struct line *line)
3099 {
3100         struct commit *commit = line->data;
3101
3102         string_copy(view->ref, commit->id);
3103         string_copy(ref_commit, view->ref);
3104 }
3105
3106 static struct view_ops main_ops = {
3107         "commit",
3108         main_draw,
3109         main_read,
3110         main_enter,
3111         main_grep,
3112         main_select,
3113 };
3114
3115
3116 /*
3117  * Unicode / UTF-8 handling
3118  *
3119  * NOTE: Much of the following code for dealing with unicode is derived from
3120  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
3121  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
3122  */
3123
3124 /* I've (over)annotated a lot of code snippets because I am not entirely
3125  * confident that the approach taken by this small UTF-8 interface is correct.
3126  * --jonas */
3127
3128 static inline int
3129 unicode_width(unsigned long c)
3130 {
3131         if (c >= 0x1100 &&
3132            (c <= 0x115f                         /* Hangul Jamo */
3133             || c == 0x2329
3134             || c == 0x232a
3135             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
3136                                                 /* CJK ... Yi */
3137             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
3138             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
3139             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
3140             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
3141             || (c >= 0xffe0  && c <= 0xffe6)
3142             || (c >= 0x20000 && c <= 0x2fffd)
3143             || (c >= 0x30000 && c <= 0x3fffd)))
3144                 return 2;
3145
3146         return 1;
3147 }
3148
3149 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
3150  * Illegal bytes are set one. */
3151 static const unsigned char utf8_bytes[256] = {
3152         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
3153         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
3154         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
3155         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
3156         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
3157         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
3158         2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
3159         3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4, 5,5,5,5,6,6,1,1,
3160 };
3161
3162 /* Decode UTF-8 multi-byte representation into a unicode character. */
3163 static inline unsigned long
3164 utf8_to_unicode(const char *string, size_t length)
3165 {
3166         unsigned long unicode;
3167
3168         switch (length) {
3169         case 1:
3170                 unicode  =   string[0];
3171                 break;
3172         case 2:
3173                 unicode  =  (string[0] & 0x1f) << 6;
3174                 unicode +=  (string[1] & 0x3f);
3175                 break;
3176         case 3:
3177                 unicode  =  (string[0] & 0x0f) << 12;
3178                 unicode += ((string[1] & 0x3f) << 6);
3179                 unicode +=  (string[2] & 0x3f);
3180                 break;
3181         case 4:
3182                 unicode  =  (string[0] & 0x0f) << 18;
3183                 unicode += ((string[1] & 0x3f) << 12);
3184                 unicode += ((string[2] & 0x3f) << 6);
3185                 unicode +=  (string[3] & 0x3f);
3186                 break;
3187         case 5:
3188                 unicode  =  (string[0] & 0x0f) << 24;
3189                 unicode += ((string[1] & 0x3f) << 18);
3190                 unicode += ((string[2] & 0x3f) << 12);
3191                 unicode += ((string[3] & 0x3f) << 6);
3192                 unicode +=  (string[4] & 0x3f);
3193                 break;
3194         case 6:
3195                 unicode  =  (string[0] & 0x01) << 30;
3196                 unicode += ((string[1] & 0x3f) << 24);
3197                 unicode += ((string[2] & 0x3f) << 18);
3198                 unicode += ((string[3] & 0x3f) << 12);
3199                 unicode += ((string[4] & 0x3f) << 6);
3200                 unicode +=  (string[5] & 0x3f);
3201                 break;
3202         default:
3203                 die("Invalid unicode length");
3204         }
3205
3206         /* Invalid characters could return the special 0xfffd value but NUL
3207          * should be just as good. */
3208         return unicode > 0xffff ? 0 : unicode;
3209 }
3210
3211 /* Calculates how much of string can be shown within the given maximum width
3212  * and sets trimmed parameter to non-zero value if all of string could not be
3213  * shown.
3214  *
3215  * Additionally, adds to coloffset how many many columns to move to align with
3216  * the expected position. Takes into account how multi-byte and double-width
3217  * characters will effect the cursor position.
3218  *
3219  * Returns the number of bytes to output from string to satisfy max_width. */
3220 static size_t
3221 utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed)
3222 {
3223         const char *start = string;
3224         const char *end = strchr(string, '\0');
3225         size_t mbwidth = 0;
3226         size_t width = 0;
3227
3228         *trimmed = 0;
3229
3230         while (string < end) {
3231                 int c = *(unsigned char *) string;
3232                 unsigned char bytes = utf8_bytes[c];
3233                 size_t ucwidth;
3234                 unsigned long unicode;
3235
3236                 if (string + bytes > end)
3237                         break;
3238
3239                 /* Change representation to figure out whether
3240                  * it is a single- or double-width character. */
3241
3242                 unicode = utf8_to_unicode(string, bytes);
3243                 /* FIXME: Graceful handling of invalid unicode character. */
3244                 if (!unicode)
3245                         break;
3246
3247                 ucwidth = unicode_width(unicode);
3248                 width  += ucwidth;
3249                 if (width > max_width) {
3250                         *trimmed = 1;
3251                         break;
3252                 }
3253
3254                 /* The column offset collects the differences between the
3255                  * number of bytes encoding a character and the number of
3256                  * columns will be used for rendering said character.
3257                  *
3258                  * So if some character A is encoded in 2 bytes, but will be
3259                  * represented on the screen using only 1 byte this will and up
3260                  * adding 1 to the multi-byte column offset.
3261                  *
3262                  * Assumes that no double-width character can be encoding in
3263                  * less than two bytes. */
3264                 if (bytes > ucwidth)
3265                         mbwidth += bytes - ucwidth;
3266
3267                 string  += bytes;
3268         }
3269
3270         *coloffset += mbwidth;
3271
3272         return string - start;
3273 }
3274
3275
3276 /*
3277  * Status management
3278  */
3279
3280 /* Whether or not the curses interface has been initialized. */
3281 static bool cursed = FALSE;
3282
3283 /* The status window is used for polling keystrokes. */
3284 static WINDOW *status_win;
3285
3286 /* Update status and title window. */
3287 static void
3288 report(const char *msg, ...)
3289 {
3290         static bool empty = TRUE;
3291         struct view *view = display[current_view];
3292
3293         if (!empty || *msg) {
3294                 va_list args;
3295
3296                 va_start(args, msg);
3297
3298                 werase(status_win);
3299                 wmove(status_win, 0, 0);
3300                 if (*msg) {
3301                         vwprintw(status_win, msg, args);
3302                         empty = FALSE;
3303                 } else {
3304                         empty = TRUE;
3305                 }
3306                 wrefresh(status_win);
3307
3308                 va_end(args);
3309         }
3310
3311         update_view_title(view);
3312         update_display_cursor();
3313 }
3314
3315 /* Controls when nodelay should be in effect when polling user input. */
3316 static void
3317 set_nonblocking_input(bool loading)
3318 {
3319         static unsigned int loading_views;
3320
3321         if ((loading == FALSE && loading_views-- == 1) ||
3322             (loading == TRUE  && loading_views++ == 0))
3323                 nodelay(status_win, loading);
3324 }
3325
3326 static void
3327 init_display(void)
3328 {
3329         int x, y;
3330
3331         /* Initialize the curses library */
3332         if (isatty(STDIN_FILENO)) {
3333                 cursed = !!initscr();
3334         } else {
3335                 /* Leave stdin and stdout alone when acting as a pager. */
3336                 FILE *io = fopen("/dev/tty", "r+");
3337
3338                 if (!io)
3339                         die("Failed to open /dev/tty");
3340                 cursed = !!newterm(NULL, io, io);
3341         }
3342
3343         if (!cursed)
3344                 die("Failed to initialize curses");
3345
3346         nonl();         /* Tell curses not to do NL->CR/NL on output */
3347         cbreak();       /* Take input chars one at a time, no wait for \n */
3348         noecho();       /* Don't echo input */
3349         leaveok(stdscr, TRUE);
3350
3351         if (has_colors())
3352                 init_colors();
3353
3354         getmaxyx(stdscr, y, x);
3355         status_win = newwin(1, 0, y - 1, 0);
3356         if (!status_win)
3357                 die("Failed to create status window");
3358
3359         /* Enable keyboard mapping */
3360         keypad(status_win, TRUE);
3361         wbkgdset(status_win, get_line_attr(LINE_STATUS));
3362 }
3363
3364 static char *
3365 read_prompt(const char *prompt)
3366 {
3367         enum { READING, STOP, CANCEL } status = READING;
3368         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
3369         int pos = 0;
3370
3371         while (status == READING) {
3372                 struct view *view;
3373                 int i, key;
3374
3375                 foreach_view (view, i)
3376                         update_view(view);
3377
3378                 report("%s%.*s", prompt, pos, buf);
3379                 /* Refresh, accept single keystroke of input */
3380                 key = wgetch(status_win);
3381                 switch (key) {
3382                 case KEY_RETURN:
3383                 case KEY_ENTER:
3384                 case '\n':
3385                         status = pos ? STOP : CANCEL;
3386                         break;
3387
3388                 case KEY_BACKSPACE:
3389                         if (pos > 0)
3390                                 pos--;
3391                         else
3392                                 status = CANCEL;
3393                         break;
3394
3395                 case KEY_ESC:
3396                         status = CANCEL;
3397                         break;
3398
3399                 case ERR:
3400                         break;
3401
3402                 default:
3403                         if (pos >= sizeof(buf)) {
3404                                 report("Input string too long");
3405                                 return NULL;
3406                         }
3407
3408                         if (isprint(key))
3409                                 buf[pos++] = (char) key;
3410                 }
3411         }
3412
3413         if (status == CANCEL) {
3414                 /* Clear the status window */
3415                 report("");
3416                 return NULL;
3417         }
3418
3419         buf[pos++] = 0;
3420
3421         return buf;
3422 }
3423
3424 /*
3425  * Repository references
3426  */
3427
3428 static struct ref *refs;
3429 static size_t refs_size;
3430
3431 /* Id <-> ref store */
3432 static struct ref ***id_refs;
3433 static size_t id_refs_size;
3434
3435 static struct ref **
3436 get_refs(char *id)
3437 {
3438         struct ref ***tmp_id_refs;
3439         struct ref **ref_list = NULL;
3440         size_t ref_list_size = 0;
3441         size_t i;
3442
3443         for (i = 0; i < id_refs_size; i++)
3444                 if (!strcmp(id, id_refs[i][0]->id))
3445                         return id_refs[i];
3446
3447         tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
3448         if (!tmp_id_refs)
3449                 return NULL;
3450
3451         id_refs = tmp_id_refs;
3452
3453         for (i = 0; i < refs_size; i++) {
3454                 struct ref **tmp;
3455
3456                 if (strcmp(id, refs[i].id))
3457                         continue;
3458
3459                 tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
3460                 if (!tmp) {
3461                         if (ref_list)
3462                                 free(ref_list);
3463                         return NULL;
3464                 }
3465
3466                 ref_list = tmp;
3467                 if (ref_list_size > 0)
3468                         ref_list[ref_list_size - 1]->next = 1;
3469                 ref_list[ref_list_size] = &refs[i];
3470
3471                 /* XXX: The properties of the commit chains ensures that we can
3472                  * safely modify the shared ref. The repo references will
3473                  * always be similar for the same id. */
3474                 ref_list[ref_list_size]->next = 0;
3475                 ref_list_size++;
3476         }
3477
3478         if (ref_list)
3479                 id_refs[id_refs_size++] = ref_list;
3480
3481         return ref_list;
3482 }
3483
3484 static int
3485 read_ref(char *id, int idlen, char *name, int namelen)
3486 {
3487         struct ref *ref;
3488         bool tag = FALSE;
3489
3490         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
3491                 /* Commits referenced by tags has "^{}" appended. */
3492                 if (name[namelen - 1] != '}')
3493                         return OK;
3494
3495                 while (namelen > 0 && name[namelen] != '^')
3496                         namelen--;
3497
3498                 tag = TRUE;
3499                 namelen -= STRING_SIZE("refs/tags/");
3500                 name    += STRING_SIZE("refs/tags/");
3501
3502         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
3503                 namelen -= STRING_SIZE("refs/heads/");
3504                 name    += STRING_SIZE("refs/heads/");
3505
3506         } else if (!strcmp(name, "HEAD")) {
3507                 return OK;
3508         }
3509
3510         refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
3511         if (!refs)
3512                 return ERR;
3513
3514         ref = &refs[refs_size++];
3515         ref->name = malloc(namelen + 1);
3516         if (!ref->name)
3517                 return ERR;
3518
3519         strncpy(ref->name, name, namelen);
3520         ref->name[namelen] = 0;
3521         ref->tag = tag;
3522         string_copy(ref->id, id);
3523
3524         return OK;
3525 }
3526
3527 static int
3528 load_refs(void)
3529 {
3530         const char *cmd_env = getenv("TIG_LS_REMOTE");
3531         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
3532
3533         return read_properties(popen(cmd, "r"), "\t", read_ref);
3534 }
3535
3536 static int
3537 read_repo_config_option(char *name, int namelen, char *value, int valuelen)
3538 {
3539         if (!strcmp(name, "i18n.commitencoding"))
3540                 string_copy(opt_encoding, value);
3541
3542         return OK;
3543 }
3544
3545 static int
3546 load_repo_config(void)
3547 {
3548         return read_properties(popen("git repo-config --list", "r"),
3549                                "=", read_repo_config_option);
3550 }
3551
3552 static int
3553 read_properties(FILE *pipe, const char *separators,
3554                 int (*read_property)(char *, int, char *, int))
3555 {
3556         char buffer[BUFSIZ];
3557         char *name;
3558         int state = OK;
3559
3560         if (!pipe)
3561                 return ERR;
3562
3563         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
3564                 char *value;
3565                 size_t namelen;
3566                 size_t valuelen;
3567
3568                 name = chomp_string(name);
3569                 namelen = strcspn(name, separators);
3570
3571                 if (name[namelen]) {
3572                         name[namelen] = 0;
3573                         value = chomp_string(name + namelen + 1);
3574                         valuelen = strlen(value);
3575
3576                 } else {
3577                         value = "";
3578                         valuelen = 0;
3579                 }
3580
3581                 state = read_property(name, namelen, value, valuelen);
3582         }
3583
3584         if (state != ERR && ferror(pipe))
3585                 state = ERR;
3586
3587         pclose(pipe);
3588
3589         return state;
3590 }
3591
3592
3593 /*
3594  * Main
3595  */
3596
3597 static void __NORETURN
3598 quit(int sig)
3599 {
3600         /* XXX: Restore tty modes and let the OS cleanup the rest! */
3601         if (cursed)
3602                 endwin();
3603         exit(0);
3604 }
3605
3606 static void __NORETURN
3607 die(const char *err, ...)
3608 {
3609         va_list args;
3610
3611         endwin();
3612
3613         va_start(args, err);
3614         fputs("tig: ", stderr);
3615         vfprintf(stderr, err, args);
3616         fputs("\n", stderr);
3617         va_end(args);
3618
3619         exit(1);
3620 }
3621
3622 int
3623 main(int argc, char *argv[])
3624 {
3625         struct view *view;
3626         enum request request;
3627         size_t i;
3628
3629         signal(SIGINT, quit);
3630
3631         if (setlocale(LC_ALL, "")) {
3632                 string_copy(opt_codeset, nl_langinfo(CODESET));
3633         }
3634
3635         if (load_options() == ERR)
3636                 die("Failed to load user config.");
3637
3638         /* Load the repo config file so options can be overwritten from
3639          * the command line.  */
3640         if (load_repo_config() == ERR)
3641                 die("Failed to load repo config.");
3642
3643         if (!parse_options(argc, argv))
3644                 return 0;
3645
3646         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
3647                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
3648                 if (opt_iconv == ICONV_NONE)
3649                         die("Failed to initialize character set conversion");
3650         }
3651
3652         if (load_refs() == ERR)
3653                 die("Failed to load refs.");
3654
3655         /* Require a git repository unless when running in pager mode. */
3656         if (refs_size == 0 && opt_request != REQ_VIEW_PAGER)
3657                 die("Not a git repository");
3658
3659         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
3660                 view->cmd_env = getenv(view->cmd_env);
3661
3662         request = opt_request;
3663
3664         init_display();
3665
3666         while (view_driver(display[current_view], request)) {
3667                 int key;
3668                 int i;
3669
3670                 foreach_view (view, i)
3671                         update_view(view);
3672
3673                 /* Refresh, accept single keystroke of input */
3674                 key = wgetch(status_win);
3675
3676                 request = get_keybinding(display[current_view]->keymap, key);
3677
3678                 /* Some low-level request handling. This keeps access to
3679                  * status_win restricted. */
3680                 switch (request) {
3681                 case REQ_PROMPT:
3682                 {
3683                         char *cmd = read_prompt(":");
3684
3685                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
3686                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
3687                                         opt_request = REQ_VIEW_DIFF;
3688                                 } else {
3689                                         opt_request = REQ_VIEW_PAGER;
3690                                 }
3691                                 break;
3692                         }
3693
3694                         request = REQ_NONE;
3695                         break;
3696                 }
3697                 case REQ_SEARCH:
3698                 case REQ_SEARCH_BACK:
3699                 {
3700                         const char *prompt = request == REQ_SEARCH
3701                                            ? "/" : "?";
3702                         char *search = read_prompt(prompt);
3703
3704                         if (search)
3705                                 string_copy(opt_search, search);
3706                         else
3707                                 request = REQ_NONE;
3708                         break;
3709                 }
3710                 case REQ_SCREEN_RESIZE:
3711                 {
3712                         int height, width;
3713
3714                         getmaxyx(stdscr, height, width);
3715
3716                         /* Resize the status view and let the view driver take
3717                          * care of resizing the displayed views. */
3718                         wresize(status_win, 1, width);
3719                         mvwin(status_win, height - 1, 0);
3720                         wrefresh(status_win);
3721                         break;
3722                 }
3723                 default:
3724                         break;
3725                 }
3726         }
3727
3728         quit(0);
3729
3730         return 0;
3731 }