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