Bump the version number to 0.6
[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"
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                 /* Incrementally draw avoids flickering. */
1931                 redraw_view_from(view, redraw_from);
1932         }
1933
1934         /* Update the title _after_ the redraw so that if the redraw picks up a
1935          * commit reference in view->ref it'll be available here. */
1936         update_view_title(view);
1937
1938 check_pipe:
1939         if (ferror(view->pipe)) {
1940                 report("Failed to read: %s", strerror(errno));
1941                 goto end;
1942
1943         } else if (feof(view->pipe)) {
1944                 report("");
1945                 goto end;
1946         }
1947
1948         return TRUE;
1949
1950 alloc_error:
1951         report("Allocation failure");
1952
1953 end:
1954         view->ops->read(view, NULL);
1955         end_update(view);
1956         return FALSE;
1957 }
1958
1959
1960 /*
1961  * View opening
1962  */
1963
1964 static void open_help_view(struct view *view)
1965 {
1966         char buf[BUFSIZ];
1967         int lines = ARRAY_SIZE(req_info) + 2;
1968         int i;
1969
1970         if (view->lines > 0)
1971                 return;
1972
1973         for (i = 0; i < ARRAY_SIZE(req_info); i++)
1974                 if (!req_info[i].request)
1975                         lines++;
1976
1977         view->line = calloc(lines, sizeof(*view->line));
1978         if (!view->line) {
1979                 report("Allocation failure");
1980                 return;
1981         }
1982
1983         view->ops->read(view, "Quick reference for tig keybindings:");
1984
1985         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
1986                 char *key;
1987
1988                 if (!req_info[i].request) {
1989                         view->ops->read(view, "");
1990                         view->ops->read(view, req_info[i].help);
1991                         continue;
1992                 }
1993
1994                 key = get_key(req_info[i].request);
1995                 if (!string_format(buf, "%-25s %s", key, req_info[i].help))
1996                         continue;
1997
1998                 view->ops->read(view, buf);
1999         }
2000 }
2001
2002 enum open_flags {
2003         OPEN_DEFAULT = 0,       /* Use default view switching. */
2004         OPEN_SPLIT = 1,         /* Split current view. */
2005         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
2006         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2007 };
2008
2009 static void
2010 open_view(struct view *prev, enum request request, enum open_flags flags)
2011 {
2012         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
2013         bool split = !!(flags & OPEN_SPLIT);
2014         bool reload = !!(flags & OPEN_RELOAD);
2015         struct view *view = VIEW(request);
2016         int nviews = displayed_views();
2017         struct view *base_view = display[0];
2018
2019         if (view == prev && nviews == 1 && !reload) {
2020                 report("Already in %s view", view->name);
2021                 return;
2022         }
2023
2024         if (view == VIEW(REQ_VIEW_HELP)) {
2025                 open_help_view(view);
2026
2027         } else if ((reload || strcmp(view->vid, view->id)) &&
2028                    !begin_update(view)) {
2029                 report("Failed to load %s view", view->name);
2030                 return;
2031         }
2032
2033         if (split) {
2034                 display[1] = view;
2035                 if (!backgrounded)
2036                         current_view = 1;
2037         } else {
2038                 /* Maximize the current view. */
2039                 memset(display, 0, sizeof(display));
2040                 current_view = 0;
2041                 display[current_view] = view;
2042         }
2043
2044         /* Resize the view when switching between split- and full-screen,
2045          * or when switching between two different full-screen views. */
2046         if (nviews != displayed_views() ||
2047             (nviews == 1 && base_view != display[0]))
2048                 resize_display();
2049
2050         if (split && prev->lineno - prev->offset >= prev->height) {
2051                 /* Take the title line into account. */
2052                 int lines = prev->lineno - prev->offset - prev->height + 1;
2053
2054                 /* Scroll the view that was split if the current line is
2055                  * outside the new limited view. */
2056                 do_scroll_view(prev, lines);
2057         }
2058
2059         if (prev && view != prev) {
2060                 if (split && !backgrounded) {
2061                         /* "Blur" the previous view. */
2062                         update_view_title(prev);
2063                 }
2064
2065                 view->parent = prev;
2066         }
2067
2068         if (view->pipe && view->lines == 0) {
2069                 /* Clear the old view and let the incremental updating refill
2070                  * the screen. */
2071                 wclear(view->win);
2072                 report("");
2073         } else {
2074                 redraw_view(view);
2075                 report("");
2076         }
2077
2078         /* If the view is backgrounded the above calls to report()
2079          * won't redraw the view title. */
2080         if (backgrounded)
2081                 update_view_title(view);
2082 }
2083
2084
2085 /*
2086  * User request switch noodle
2087  */
2088
2089 static int
2090 view_driver(struct view *view, enum request request)
2091 {
2092         int i;
2093
2094         switch (request) {
2095         case REQ_MOVE_UP:
2096         case REQ_MOVE_DOWN:
2097         case REQ_MOVE_PAGE_UP:
2098         case REQ_MOVE_PAGE_DOWN:
2099         case REQ_MOVE_FIRST_LINE:
2100         case REQ_MOVE_LAST_LINE:
2101                 move_view(view, request);
2102                 break;
2103
2104         case REQ_SCROLL_LINE_DOWN:
2105         case REQ_SCROLL_LINE_UP:
2106         case REQ_SCROLL_PAGE_DOWN:
2107         case REQ_SCROLL_PAGE_UP:
2108                 scroll_view(view, request);
2109                 break;
2110
2111         case REQ_VIEW_BLOB:
2112                 if (!ref_blob[0]) {
2113                         report("No file chosen, press 't' to open tree view");
2114                         break;
2115                 }
2116                 /* Fall-through */
2117         case REQ_VIEW_MAIN:
2118         case REQ_VIEW_DIFF:
2119         case REQ_VIEW_LOG:
2120         case REQ_VIEW_TREE:
2121         case REQ_VIEW_HELP:
2122         case REQ_VIEW_PAGER:
2123                 open_view(view, request, OPEN_DEFAULT);
2124                 break;
2125
2126         case REQ_NEXT:
2127         case REQ_PREVIOUS:
2128                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
2129
2130                 if ((view == VIEW(REQ_VIEW_DIFF) &&
2131                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
2132                    (view == VIEW(REQ_VIEW_BLOB) &&
2133                      view->parent == VIEW(REQ_VIEW_TREE))) {
2134                         view = view->parent;
2135                         move_view(view, request);
2136                         if (view_is_displayed(view))
2137                                 update_view_title(view);
2138                 } else {
2139                         move_view(view, request);
2140                         break;
2141                 }
2142                 /* Fall-through */
2143
2144         case REQ_ENTER:
2145                 if (!view->lines) {
2146                         report("Nothing to enter");
2147                         break;
2148                 }
2149                 return view->ops->enter(view, &view->line[view->lineno]);
2150
2151         case REQ_VIEW_NEXT:
2152         {
2153                 int nviews = displayed_views();
2154                 int next_view = (current_view + 1) % nviews;
2155
2156                 if (next_view == current_view) {
2157                         report("Only one view is displayed");
2158                         break;
2159                 }
2160
2161                 current_view = next_view;
2162                 /* Blur out the title of the previous view. */
2163                 update_view_title(view);
2164                 report("");
2165                 break;
2166         }
2167         case REQ_TOGGLE_LINENO:
2168                 opt_line_number = !opt_line_number;
2169                 redraw_display();
2170                 break;
2171
2172         case REQ_TOGGLE_REV_GRAPH:
2173                 opt_rev_graph = !opt_rev_graph;
2174                 redraw_display();
2175                 break;
2176
2177         case REQ_PROMPT:
2178                 /* Always reload^Wrerun commands from the prompt. */
2179                 open_view(view, opt_request, OPEN_RELOAD);
2180                 break;
2181
2182         case REQ_SEARCH:
2183         case REQ_SEARCH_BACK:
2184                 search_view(view, request);
2185                 break;
2186
2187         case REQ_FIND_NEXT:
2188         case REQ_FIND_PREV:
2189                 find_next(view, request);
2190                 break;
2191
2192         case REQ_STOP_LOADING:
2193                 for (i = 0; i < ARRAY_SIZE(views); i++) {
2194                         view = &views[i];
2195                         if (view->pipe)
2196                                 report("Stopped loading the %s view", view->name),
2197                         end_update(view);
2198                 }
2199                 break;
2200
2201         case REQ_SHOW_VERSION:
2202                 report("%s (built %s)", VERSION, __DATE__);
2203                 return TRUE;
2204
2205         case REQ_SCREEN_RESIZE:
2206                 resize_display();
2207                 /* Fall-through */
2208         case REQ_SCREEN_REDRAW:
2209                 redraw_display();
2210                 break;
2211
2212         case REQ_NONE:
2213                 doupdate();
2214                 return TRUE;
2215
2216         case REQ_VIEW_CLOSE:
2217                 /* XXX: Mark closed views by letting view->parent point to the
2218                  * view itself. Parents to closed view should never be
2219                  * followed. */
2220                 if (view->parent &&
2221                     view->parent->parent != view->parent) {
2222                         memset(display, 0, sizeof(display));
2223                         current_view = 0;
2224                         display[current_view] = view->parent;
2225                         view->parent = view;
2226                         resize_display();
2227                         redraw_display();
2228                         break;
2229                 }
2230                 /* Fall-through */
2231         case REQ_QUIT:
2232                 return FALSE;
2233
2234         default:
2235                 /* An unknown key will show most commonly used commands. */
2236                 report("Unknown key, press 'h' for help");
2237                 return TRUE;
2238         }
2239
2240         return TRUE;
2241 }
2242
2243
2244 /*
2245  * Pager backend
2246  */
2247
2248 static bool
2249 pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2250 {
2251         char *text = line->data;
2252         enum line_type type = line->type;
2253         int textlen = strlen(text);
2254         int attr;
2255
2256         wmove(view->win, lineno, 0);
2257
2258         if (selected) {
2259                 type = LINE_CURSOR;
2260                 wchgat(view->win, -1, 0, type, NULL);
2261         }
2262
2263         attr = get_line_attr(type);
2264         wattrset(view->win, attr);
2265
2266         if (opt_line_number || opt_tab_size < TABSIZE) {
2267                 static char spaces[] = "                    ";
2268                 int col_offset = 0, col = 0;
2269
2270                 if (opt_line_number) {
2271                         unsigned long real_lineno = view->offset + lineno + 1;
2272
2273                         if (real_lineno == 1 ||
2274                             (real_lineno % opt_num_interval) == 0) {
2275                                 wprintw(view->win, "%.*d", view->digits, real_lineno);
2276
2277                         } else {
2278                                 waddnstr(view->win, spaces,
2279                                          MIN(view->digits, STRING_SIZE(spaces)));
2280                         }
2281                         waddstr(view->win, ": ");
2282                         col_offset = view->digits + 2;
2283                 }
2284
2285                 while (text && col_offset + col < view->width) {
2286                         int cols_max = view->width - col_offset - col;
2287                         char *pos = text;
2288                         int cols;
2289
2290                         if (*text == '\t') {
2291                                 text++;
2292                                 assert(sizeof(spaces) > TABSIZE);
2293                                 pos = spaces;
2294                                 cols = opt_tab_size - (col % opt_tab_size);
2295
2296                         } else {
2297                                 text = strchr(text, '\t');
2298                                 cols = line ? text - pos : strlen(pos);
2299                         }
2300
2301                         waddnstr(view->win, pos, MIN(cols, cols_max));
2302                         col += cols;
2303                 }
2304
2305         } else {
2306                 int col = 0, pos = 0;
2307
2308                 for (; pos < textlen && col < view->width; pos++, col++)
2309                         if (text[pos] == '\t')
2310                                 col += TABSIZE - (col % TABSIZE) - 1;
2311
2312                 waddnstr(view->win, text, pos);
2313         }
2314
2315         return TRUE;
2316 }
2317
2318 static bool
2319 add_describe_ref(char *buf, size_t *bufpos, char *commit_id, const char *sep)
2320 {
2321         char refbuf[SIZEOF_STR];
2322         char *ref = NULL;
2323         FILE *pipe;
2324
2325         if (!string_format(refbuf, "git describe %s 2>/dev/null", commit_id))
2326                 return TRUE;
2327
2328         pipe = popen(refbuf, "r");
2329         if (!pipe)
2330                 return TRUE;
2331
2332         if ((ref = fgets(refbuf, sizeof(refbuf), pipe)))
2333                 ref = chomp_string(ref);
2334         pclose(pipe);
2335
2336         if (!ref || !*ref)
2337                 return TRUE;
2338
2339         /* This is the only fatal call, since it can "corrupt" the buffer. */
2340         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
2341                 return FALSE;
2342
2343         return TRUE;
2344 }
2345
2346 static void
2347 add_pager_refs(struct view *view, struct line *line)
2348 {
2349         char buf[SIZEOF_STR];
2350         char *commit_id = line->data + STRING_SIZE("commit ");
2351         struct ref **refs;
2352         size_t bufpos = 0, refpos = 0;
2353         const char *sep = "Refs: ";
2354         bool is_tag = FALSE;
2355
2356         assert(line->type == LINE_COMMIT);
2357
2358         refs = get_refs(commit_id);
2359         if (!refs) {
2360                 if (view == VIEW(REQ_VIEW_DIFF))
2361                         goto try_add_describe_ref;
2362                 return;
2363         }
2364
2365         do {
2366                 struct ref *ref = refs[refpos];
2367                 char *fmt = ref->tag    ? "%s[%s]" :
2368                             ref->remote ? "%s<%s>" : "%s%s";
2369
2370                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
2371                         return;
2372                 sep = ", ";
2373                 if (ref->tag)
2374                         is_tag = TRUE;
2375         } while (refs[refpos++]->next);
2376
2377         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
2378 try_add_describe_ref:
2379                 /* Add <tag>-g<commit_id> "fake" reference. */
2380                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
2381                         return;
2382         }
2383
2384         if (bufpos == 0)
2385                 return;
2386
2387         if (!realloc_lines(view, view->line_size + 1))
2388                 return;
2389
2390         line = &view->line[view->lines];
2391         line->data = strdup(buf);
2392         if (!line->data)
2393                 return;
2394
2395         line->type = LINE_PP_REFS;
2396         view->lines++;
2397 }
2398
2399 static bool
2400 pager_read(struct view *view, char *data)
2401 {
2402         struct line *line = &view->line[view->lines];
2403
2404         if (!data)
2405                 return TRUE;
2406
2407         line->data = strdup(data);
2408         if (!line->data)
2409                 return FALSE;
2410
2411         line->type = get_line_type(line->data);
2412         view->lines++;
2413
2414         if (line->type == LINE_COMMIT &&
2415             (view == VIEW(REQ_VIEW_DIFF) ||
2416              view == VIEW(REQ_VIEW_LOG)))
2417                 add_pager_refs(view, line);
2418
2419         return TRUE;
2420 }
2421
2422 static bool
2423 pager_enter(struct view *view, struct line *line)
2424 {
2425         int split = 0;
2426
2427         if (line->type == LINE_COMMIT &&
2428            (view == VIEW(REQ_VIEW_LOG) ||
2429             view == VIEW(REQ_VIEW_PAGER))) {
2430                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
2431                 split = 1;
2432         }
2433
2434         /* Always scroll the view even if it was split. That way
2435          * you can use Enter to scroll through the log view and
2436          * split open each commit diff. */
2437         scroll_view(view, REQ_SCROLL_LINE_DOWN);
2438
2439         /* FIXME: A minor workaround. Scrolling the view will call report("")
2440          * but if we are scrolling a non-current view this won't properly
2441          * update the view title. */
2442         if (split)
2443                 update_view_title(view);
2444
2445         return TRUE;
2446 }
2447
2448 static bool
2449 pager_grep(struct view *view, struct line *line)
2450 {
2451         regmatch_t pmatch;
2452         char *text = line->data;
2453
2454         if (!*text)
2455                 return FALSE;
2456
2457         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
2458                 return FALSE;
2459
2460         return TRUE;
2461 }
2462
2463 static void
2464 pager_select(struct view *view, struct line *line)
2465 {
2466         if (line->type == LINE_COMMIT) {
2467                 char *text = line->data;
2468
2469                 string_copy(view->ref, text + STRING_SIZE("commit "));
2470                 string_copy(ref_commit, view->ref);
2471         }
2472 }
2473
2474 static struct view_ops pager_ops = {
2475         "line",
2476         pager_draw,
2477         pager_read,
2478         pager_enter,
2479         pager_grep,
2480         pager_select,
2481 };
2482
2483
2484 /*
2485  * Tree backend
2486  */
2487
2488 /* Parse output from git-ls-tree(1):
2489  *
2490  * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile
2491  * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README
2492  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
2493  * 100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38 web.conf
2494  */
2495
2496 #define SIZEOF_TREE_ATTR \
2497         STRING_SIZE("100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38\t")
2498
2499 #define TREE_UP_FORMAT "040000 tree %s\t.."
2500
2501 static int
2502 tree_compare_entry(enum line_type type1, char *name1,
2503                    enum line_type type2, char *name2)
2504 {
2505         if (type1 != type2) {
2506                 if (type1 == LINE_TREE_DIR)
2507                         return -1;
2508                 return 1;
2509         }
2510
2511         return strcmp(name1, name2);
2512 }
2513
2514 static bool
2515 tree_read(struct view *view, char *text)
2516 {
2517         size_t textlen = text ? strlen(text) : 0;
2518         char buf[SIZEOF_STR];
2519         unsigned long pos;
2520         enum line_type type;
2521         bool first_read = view->lines == 0;
2522
2523         if (textlen <= SIZEOF_TREE_ATTR)
2524                 return FALSE;
2525
2526         type = text[STRING_SIZE("100644 ")] == 't'
2527              ? LINE_TREE_DIR : LINE_TREE_FILE;
2528
2529         if (first_read) {
2530                 /* Add path info line */
2531                 if (string_format(buf, "Directory path /%s", opt_path) &&
2532                     realloc_lines(view, view->line_size + 1) &&
2533                     pager_read(view, buf))
2534                         view->line[view->lines - 1].type = LINE_DEFAULT;
2535                 else
2536                         return FALSE;
2537
2538                 /* Insert "link" to parent directory. */
2539                 if (*opt_path &&
2540                     string_format(buf, TREE_UP_FORMAT, view->ref) &&
2541                     realloc_lines(view, view->line_size + 1) &&
2542                     pager_read(view, buf))
2543                         view->line[view->lines - 1].type = LINE_TREE_DIR;
2544                 else if (*opt_path)
2545                         return FALSE;
2546         }
2547
2548         /* Strip the path part ... */
2549         if (*opt_path) {
2550                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
2551                 size_t striplen = strlen(opt_path);
2552                 char *path = text + SIZEOF_TREE_ATTR;
2553
2554                 if (pathlen > striplen)
2555                         memmove(path, path + striplen,
2556                                 pathlen - striplen + 1);
2557         }
2558
2559         /* Skip "Directory ..." and ".." line. */
2560         for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
2561                 struct line *line = &view->line[pos];
2562                 char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR;
2563                 char *path2 = text + SIZEOF_TREE_ATTR;
2564                 int cmp = tree_compare_entry(line->type, path1, type, path2);
2565
2566                 if (cmp <= 0)
2567                         continue;
2568
2569                 text = strdup(text);
2570                 if (!text)
2571                         return FALSE;
2572
2573                 if (view->lines > pos)
2574                         memmove(&view->line[pos + 1], &view->line[pos],
2575                                 (view->lines - pos) * sizeof(*line));
2576
2577                 line = &view->line[pos];
2578                 line->data = text;
2579                 line->type = type;
2580                 view->lines++;
2581                 return TRUE;
2582         }
2583
2584         if (!pager_read(view, text))
2585                 return FALSE;
2586
2587         /* Move the current line to the first tree entry. */
2588         if (first_read)
2589                 view->lineno++;
2590
2591         view->line[view->lines - 1].type = type;
2592         return TRUE;
2593 }
2594
2595 static bool
2596 tree_enter(struct view *view, struct line *line)
2597 {
2598         enum open_flags flags;
2599         enum request request;
2600
2601         switch (line->type) {
2602         case LINE_TREE_DIR:
2603                 /* Depending on whether it is a subdir or parent (updir?) link
2604                  * mangle the path buffer. */
2605                 if (line == &view->line[1] && *opt_path) {
2606                         size_t path_len = strlen(opt_path);
2607                         char *dirsep = opt_path + path_len - 1;
2608
2609                         while (dirsep > opt_path && dirsep[-1] != '/')
2610                                 dirsep--;
2611
2612                         dirsep[0] = 0;
2613
2614                 } else {
2615                         size_t pathlen = strlen(opt_path);
2616                         size_t origlen = pathlen;
2617                         char *data = line->data;
2618                         char *basename = data + SIZEOF_TREE_ATTR;
2619
2620                         if (!string_format_from(opt_path, &pathlen, "%s/", basename)) {
2621                                 opt_path[origlen] = 0;
2622                                 return TRUE;
2623                         }
2624                 }
2625
2626                 /* Trees and subtrees share the same ID, so they are not not
2627                  * unique like blobs. */
2628                 flags = OPEN_RELOAD;
2629                 request = REQ_VIEW_TREE;
2630                 break;
2631
2632         case LINE_TREE_FILE:
2633                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
2634                 request = REQ_VIEW_BLOB;
2635                 break;
2636
2637         default:
2638                 return TRUE;
2639         }
2640
2641         open_view(view, request, flags);
2642
2643         return TRUE;
2644 }
2645
2646 static void
2647 tree_select(struct view *view, struct line *line)
2648 {
2649         char *text = line->data;
2650
2651         text += STRING_SIZE("100644 blob ");
2652
2653         if (line->type == LINE_TREE_FILE) {
2654                 string_ncopy(ref_blob, text, 40);
2655                 /* Also update the blob view's ref, since all there must always
2656                  * be in sync. */
2657                 string_copy(VIEW(REQ_VIEW_BLOB)->ref, ref_blob);
2658
2659         } else if (line->type != LINE_TREE_DIR) {
2660                 return;
2661         }
2662
2663         string_ncopy(view->ref, text, 40);
2664 }
2665
2666 static struct view_ops tree_ops = {
2667         "file",
2668         pager_draw,
2669         tree_read,
2670         tree_enter,
2671         pager_grep,
2672         tree_select,
2673 };
2674
2675 static bool
2676 blob_read(struct view *view, char *line)
2677 {
2678         bool state = pager_read(view, line);
2679
2680         if (state == TRUE)
2681                 view->line[view->lines - 1].type = LINE_DEFAULT;
2682
2683         return state;
2684 }
2685
2686 static struct view_ops blob_ops = {
2687         "line",
2688         pager_draw,
2689         blob_read,
2690         pager_enter,
2691         pager_grep,
2692         pager_select,
2693 };
2694
2695
2696 /*
2697  * Revision graph
2698  */
2699
2700 struct commit {
2701         char id[SIZEOF_REV];            /* SHA1 ID. */
2702         char title[128];                /* First line of the commit message. */
2703         char author[75];                /* Author of the commit. */
2704         struct tm time;                 /* Date from the author ident. */
2705         struct ref **refs;              /* Repository references. */
2706         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
2707         size_t graph_size;              /* The width of the graph array. */
2708 };
2709
2710 /* Size of rev graph with no  "padding" columns */
2711 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
2712
2713 struct rev_graph {
2714         struct rev_graph *prev, *next, *parents;
2715         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
2716         size_t size;
2717         struct commit *commit;
2718         size_t pos;
2719 };
2720
2721 /* Parents of the commit being visualized. */
2722 static struct rev_graph graph_parents[4];
2723
2724 /* The current stack of revisions on the graph. */
2725 static struct rev_graph graph_stacks[4] = {
2726         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
2727         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
2728         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
2729         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
2730 };
2731
2732 static inline bool
2733 graph_parent_is_merge(struct rev_graph *graph)
2734 {
2735         return graph->parents->size > 1;
2736 }
2737
2738 static inline void
2739 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
2740 {
2741         struct commit *commit = graph->commit;
2742
2743         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
2744                 commit->graph[commit->graph_size++] = symbol;
2745 }
2746
2747 static void
2748 done_rev_graph(struct rev_graph *graph)
2749 {
2750         if (graph_parent_is_merge(graph) &&
2751             graph->pos < graph->size - 1 &&
2752             graph->next->size == graph->size + graph->parents->size - 1) {
2753                 size_t i = graph->pos + graph->parents->size - 1;
2754
2755                 graph->commit->graph_size = i * 2;
2756                 while (i < graph->next->size - 1) {
2757                         append_to_rev_graph(graph, ' ');
2758                         append_to_rev_graph(graph, '\\');
2759                         i++;
2760                 }
2761         }
2762
2763         graph->size = graph->pos = 0;
2764         graph->commit = NULL;
2765         memset(graph->parents, 0, sizeof(*graph->parents));
2766 }
2767
2768 static void
2769 push_rev_graph(struct rev_graph *graph, char *parent)
2770 {
2771         int i;
2772
2773         /* "Collapse" duplicate parents lines.
2774          *
2775          * FIXME: This needs to also update update the drawn graph but
2776          * for now it just serves as a method for pruning graph lines. */
2777         for (i = 0; i < graph->size; i++)
2778                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
2779                         return;
2780
2781         if (graph->size < SIZEOF_REVITEMS) {
2782                 string_ncopy(graph->rev[graph->size++], parent, SIZEOF_REV);
2783         }
2784 }
2785
2786 static chtype
2787 get_rev_graph_symbol(struct rev_graph *graph)
2788 {
2789         chtype symbol;
2790
2791         if (graph->parents->size == 0)
2792                 symbol = REVGRAPH_INIT;
2793         else if (graph_parent_is_merge(graph))
2794                 symbol = REVGRAPH_MERGE;
2795         else if (graph->pos >= graph->size)
2796                 symbol = REVGRAPH_BRANCH;
2797         else
2798                 symbol = REVGRAPH_COMMIT;
2799
2800         return symbol;
2801 }
2802
2803 static void
2804 draw_rev_graph(struct rev_graph *graph)
2805 {
2806         struct rev_filler {
2807                 chtype separator, line;
2808         };
2809         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
2810         static struct rev_filler fillers[] = {
2811                 { ' ',  REVGRAPH_LINE },
2812                 { '`',  '.' },
2813                 { '\'', ' ' },
2814                 { '/',  ' ' },
2815         };
2816         chtype symbol = get_rev_graph_symbol(graph);
2817         struct rev_filler *filler;
2818         size_t i;
2819
2820         filler = &fillers[DEFAULT];
2821
2822         for (i = 0; i < graph->pos; i++) {
2823                 append_to_rev_graph(graph, filler->line);
2824                 if (graph_parent_is_merge(graph->prev) &&
2825                     graph->prev->pos == i)
2826                         filler = &fillers[RSHARP];
2827
2828                 append_to_rev_graph(graph, filler->separator);
2829         }
2830
2831         /* Place the symbol for this revision. */
2832         append_to_rev_graph(graph, symbol);
2833
2834         if (graph->prev->size > graph->size)
2835                 filler = &fillers[RDIAG];
2836         else
2837                 filler = &fillers[DEFAULT];
2838
2839         i++;
2840
2841         for (; i < graph->size; i++) {
2842                 append_to_rev_graph(graph, filler->separator);
2843                 append_to_rev_graph(graph, filler->line);
2844                 if (graph_parent_is_merge(graph->prev) &&
2845                     i < graph->prev->pos + graph->parents->size)
2846                         filler = &fillers[RSHARP];
2847                 if (graph->prev->size > graph->size)
2848                         filler = &fillers[LDIAG];
2849         }
2850
2851         if (graph->prev->size > graph->size) {
2852                 append_to_rev_graph(graph, filler->separator);
2853                 if (filler->line != ' ')
2854                         append_to_rev_graph(graph, filler->line);
2855         }
2856 }
2857
2858 /* Prepare the next rev graph */
2859 static void
2860 prepare_rev_graph(struct rev_graph *graph)
2861 {
2862         size_t i;
2863
2864         /* First, traverse all lines of revisions up to the active one. */
2865         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
2866                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
2867                         break;
2868
2869                 push_rev_graph(graph->next, graph->rev[graph->pos]);
2870         }
2871
2872         /* Interleave the new revision parent(s). */
2873         for (i = 0; i < graph->parents->size; i++)
2874                 push_rev_graph(graph->next, graph->parents->rev[i]);
2875
2876         /* Lastly, put any remaining revisions. */
2877         for (i = graph->pos + 1; i < graph->size; i++)
2878                 push_rev_graph(graph->next, graph->rev[i]);
2879 }
2880
2881 static void
2882 update_rev_graph(struct rev_graph *graph)
2883 {
2884         /* If this is the finalizing update ... */
2885         if (graph->commit)
2886                 prepare_rev_graph(graph);
2887
2888         /* Graph visualization needs a one rev look-ahead,
2889          * so the first update doesn't visualize anything. */
2890         if (!graph->prev->commit)
2891                 return;
2892
2893         draw_rev_graph(graph->prev);
2894         done_rev_graph(graph->prev->prev);
2895 }
2896
2897
2898 /*
2899  * Main view backend
2900  */
2901
2902 static bool
2903 main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2904 {
2905         char buf[DATE_COLS + 1];
2906         struct commit *commit = line->data;
2907         enum line_type type;
2908         int col = 0;
2909         size_t timelen;
2910         size_t authorlen;
2911         int trimmed = 1;
2912
2913         if (!*commit->author)
2914                 return FALSE;
2915
2916         wmove(view->win, lineno, col);
2917
2918         if (selected) {
2919                 type = LINE_CURSOR;
2920                 wattrset(view->win, get_line_attr(type));
2921                 wchgat(view->win, -1, 0, type, NULL);
2922
2923         } else {
2924                 type = LINE_MAIN_COMMIT;
2925                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
2926         }
2927
2928         timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
2929         waddnstr(view->win, buf, timelen);
2930         waddstr(view->win, " ");
2931
2932         col += DATE_COLS;
2933         wmove(view->win, lineno, col);
2934         if (type != LINE_CURSOR)
2935                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
2936
2937         if (opt_utf8) {
2938                 authorlen = utf8_length(commit->author, AUTHOR_COLS - 2, &col, &trimmed);
2939         } else {
2940                 authorlen = strlen(commit->author);
2941                 if (authorlen > AUTHOR_COLS - 2) {
2942                         authorlen = AUTHOR_COLS - 2;
2943                         trimmed = 1;
2944                 }
2945         }
2946
2947         if (trimmed) {
2948                 waddnstr(view->win, commit->author, authorlen);
2949                 if (type != LINE_CURSOR)
2950                         wattrset(view->win, get_line_attr(LINE_MAIN_DELIM));
2951                 waddch(view->win, '~');
2952         } else {
2953                 waddstr(view->win, commit->author);
2954         }
2955
2956         col += AUTHOR_COLS;
2957         if (type != LINE_CURSOR)
2958                 wattrset(view->win, A_NORMAL);
2959
2960         if (opt_rev_graph && commit->graph_size) {
2961                 size_t i;
2962
2963                 wmove(view->win, lineno, col);
2964                 /* Using waddch() instead of waddnstr() ensures that
2965                  * they'll be rendered correctly for the cursor line. */
2966                 for (i = 0; i < commit->graph_size; i++)
2967                         waddch(view->win, commit->graph[i]);
2968
2969                 waddch(view->win, ' ');
2970                 col += commit->graph_size + 1;
2971         }
2972
2973         wmove(view->win, lineno, col);
2974
2975         if (commit->refs) {
2976                 size_t i = 0;
2977
2978                 do {
2979                         if (type == LINE_CURSOR)
2980                                 ;
2981                         else if (commit->refs[i]->tag)
2982                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
2983                         else if (commit->refs[i]->remote)
2984                                 wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
2985                         else
2986                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
2987                         waddstr(view->win, "[");
2988                         waddstr(view->win, commit->refs[i]->name);
2989                         waddstr(view->win, "]");
2990                         if (type != LINE_CURSOR)
2991                                 wattrset(view->win, A_NORMAL);
2992                         waddstr(view->win, " ");
2993                         col += strlen(commit->refs[i]->name) + STRING_SIZE("[] ");
2994                 } while (commit->refs[i++]->next);
2995         }
2996
2997         if (type != LINE_CURSOR)
2998                 wattrset(view->win, get_line_attr(type));
2999
3000         {
3001                 int titlelen = strlen(commit->title);
3002
3003                 if (col + titlelen > view->width)
3004                         titlelen = view->width - col;
3005
3006                 waddnstr(view->win, commit->title, titlelen);
3007         }
3008
3009         return TRUE;
3010 }
3011
3012 /* Reads git log --pretty=raw output and parses it into the commit struct. */
3013 static bool
3014 main_read(struct view *view, char *line)
3015 {
3016         static struct rev_graph *graph = graph_stacks;
3017         enum line_type type;
3018         struct commit *commit = view->lines
3019                               ? view->line[view->lines - 1].data : NULL;
3020
3021         if (!line) {
3022                 update_rev_graph(graph);
3023                 return TRUE;
3024         }
3025
3026         type = get_line_type(line);
3027
3028         switch (type) {
3029         case LINE_COMMIT:
3030                 commit = calloc(1, sizeof(struct commit));
3031                 if (!commit)
3032                         return FALSE;
3033
3034                 line += STRING_SIZE("commit ");
3035
3036                 view->line[view->lines++].data = commit;
3037                 string_copy(commit->id, line);
3038                 commit->refs = get_refs(commit->id);
3039                 graph->commit = commit;
3040                 break;
3041
3042         case LINE_PARENT:
3043                 if (commit) {
3044                         line += STRING_SIZE("parent ");
3045                         push_rev_graph(graph->parents, line);
3046                 }
3047                 break;
3048
3049         case LINE_AUTHOR:
3050         {
3051                 /* Parse author lines where the name may be empty:
3052                  *      author  <email@address.tld> 1138474660 +0100
3053                  */
3054                 char *ident = line + STRING_SIZE("author ");
3055                 char *nameend = strchr(ident, '<');
3056                 char *emailend = strchr(ident, '>');
3057
3058                 if (!commit || !nameend || !emailend)
3059                         break;
3060
3061                 update_rev_graph(graph);
3062                 graph = graph->next;
3063
3064                 *nameend = *emailend = 0;
3065                 ident = chomp_string(ident);
3066                 if (!*ident) {
3067                         ident = chomp_string(nameend + 1);
3068                         if (!*ident)
3069                                 ident = "Unknown";
3070                 }
3071
3072                 string_copy(commit->author, ident);
3073
3074                 /* Parse epoch and timezone */
3075                 if (emailend[1] == ' ') {
3076                         char *secs = emailend + 2;
3077                         char *zone = strchr(secs, ' ');
3078                         time_t time = (time_t) atol(secs);
3079
3080                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
3081                                 long tz;
3082
3083                                 zone++;
3084                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
3085                                 tz += ('0' - zone[2]) * 60 * 60;
3086                                 tz += ('0' - zone[3]) * 60;
3087                                 tz += ('0' - zone[4]) * 60;
3088
3089                                 if (zone[0] == '-')
3090                                         tz = -tz;
3091
3092                                 time -= tz;
3093                         }
3094
3095                         gmtime_r(&time, &commit->time);
3096                 }
3097                 break;
3098         }
3099         default:
3100                 if (!commit)
3101                         break;
3102
3103                 /* Fill in the commit title if it has not already been set. */
3104                 if (commit->title[0])
3105                         break;
3106
3107                 /* Require titles to start with a non-space character at the
3108                  * offset used by git log. */
3109                 if (strncmp(line, "    ", 4))
3110                         break;
3111                 line += 4;
3112                 /* Well, if the title starts with a whitespace character,
3113                  * try to be forgiving.  Otherwise we end up with no title. */
3114                 while (isspace(*line))
3115                         line++;
3116                 if (*line == '\0')
3117                         break;
3118                 /* FIXME: More graceful handling of titles; append "..." to
3119                  * shortened titles, etc. */
3120
3121                 string_copy(commit->title, line);
3122         }
3123
3124         return TRUE;
3125 }
3126
3127 static bool
3128 main_enter(struct view *view, struct line *line)
3129 {
3130         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3131
3132         open_view(view, REQ_VIEW_DIFF, flags);
3133         return TRUE;
3134 }
3135
3136 static bool
3137 main_grep(struct view *view, struct line *line)
3138 {
3139         struct commit *commit = line->data;
3140         enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
3141         char buf[DATE_COLS + 1];
3142         regmatch_t pmatch;
3143
3144         for (state = S_TITLE; state < S_END; state++) {
3145                 char *text;
3146
3147                 switch (state) {
3148                 case S_TITLE:   text = commit->title;   break;
3149                 case S_AUTHOR:  text = commit->author;  break;
3150                 case S_DATE:
3151                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
3152                                 continue;
3153                         text = buf;
3154                         break;
3155
3156                 default:
3157                         return FALSE;
3158                 }
3159
3160                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
3161                         return TRUE;
3162         }
3163
3164         return FALSE;
3165 }
3166
3167 static void
3168 main_select(struct view *view, struct line *line)
3169 {
3170         struct commit *commit = line->data;
3171
3172         string_copy(view->ref, commit->id);
3173         string_copy(ref_commit, view->ref);
3174 }
3175
3176 static struct view_ops main_ops = {
3177         "commit",
3178         main_draw,
3179         main_read,
3180         main_enter,
3181         main_grep,
3182         main_select,
3183 };
3184
3185
3186 /*
3187  * Unicode / UTF-8 handling
3188  *
3189  * NOTE: Much of the following code for dealing with unicode is derived from
3190  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
3191  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
3192  */
3193
3194 /* I've (over)annotated a lot of code snippets because I am not entirely
3195  * confident that the approach taken by this small UTF-8 interface is correct.
3196  * --jonas */
3197
3198 static inline int
3199 unicode_width(unsigned long c)
3200 {
3201         if (c >= 0x1100 &&
3202            (c <= 0x115f                         /* Hangul Jamo */
3203             || c == 0x2329
3204             || c == 0x232a
3205             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
3206                                                 /* CJK ... Yi */
3207             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
3208             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
3209             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
3210             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
3211             || (c >= 0xffe0  && c <= 0xffe6)
3212             || (c >= 0x20000 && c <= 0x2fffd)
3213             || (c >= 0x30000 && c <= 0x3fffd)))
3214                 return 2;
3215
3216         return 1;
3217 }
3218
3219 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
3220  * Illegal bytes are set one. */
3221 static const unsigned char utf8_bytes[256] = {
3222         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,
3223         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,
3224         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,
3225         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,
3226         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,
3227         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,
3228         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,
3229         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,
3230 };
3231
3232 /* Decode UTF-8 multi-byte representation into a unicode character. */
3233 static inline unsigned long
3234 utf8_to_unicode(const char *string, size_t length)
3235 {
3236         unsigned long unicode;
3237
3238         switch (length) {
3239         case 1:
3240                 unicode  =   string[0];
3241                 break;
3242         case 2:
3243                 unicode  =  (string[0] & 0x1f) << 6;
3244                 unicode +=  (string[1] & 0x3f);
3245                 break;
3246         case 3:
3247                 unicode  =  (string[0] & 0x0f) << 12;
3248                 unicode += ((string[1] & 0x3f) << 6);
3249                 unicode +=  (string[2] & 0x3f);
3250                 break;
3251         case 4:
3252                 unicode  =  (string[0] & 0x0f) << 18;
3253                 unicode += ((string[1] & 0x3f) << 12);
3254                 unicode += ((string[2] & 0x3f) << 6);
3255                 unicode +=  (string[3] & 0x3f);
3256                 break;
3257         case 5:
3258                 unicode  =  (string[0] & 0x0f) << 24;
3259                 unicode += ((string[1] & 0x3f) << 18);
3260                 unicode += ((string[2] & 0x3f) << 12);
3261                 unicode += ((string[3] & 0x3f) << 6);
3262                 unicode +=  (string[4] & 0x3f);
3263                 break;
3264         case 6:
3265                 unicode  =  (string[0] & 0x01) << 30;
3266                 unicode += ((string[1] & 0x3f) << 24);
3267                 unicode += ((string[2] & 0x3f) << 18);
3268                 unicode += ((string[3] & 0x3f) << 12);
3269                 unicode += ((string[4] & 0x3f) << 6);
3270                 unicode +=  (string[5] & 0x3f);
3271                 break;
3272         default:
3273                 die("Invalid unicode length");
3274         }
3275
3276         /* Invalid characters could return the special 0xfffd value but NUL
3277          * should be just as good. */
3278         return unicode > 0xffff ? 0 : unicode;
3279 }
3280
3281 /* Calculates how much of string can be shown within the given maximum width
3282  * and sets trimmed parameter to non-zero value if all of string could not be
3283  * shown.
3284  *
3285  * Additionally, adds to coloffset how many many columns to move to align with
3286  * the expected position. Takes into account how multi-byte and double-width
3287  * characters will effect the cursor position.
3288  *
3289  * Returns the number of bytes to output from string to satisfy max_width. */
3290 static size_t
3291 utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed)
3292 {
3293         const char *start = string;
3294         const char *end = strchr(string, '\0');
3295         size_t mbwidth = 0;
3296         size_t width = 0;
3297
3298         *trimmed = 0;
3299
3300         while (string < end) {
3301                 int c = *(unsigned char *) string;
3302                 unsigned char bytes = utf8_bytes[c];
3303                 size_t ucwidth;
3304                 unsigned long unicode;
3305
3306                 if (string + bytes > end)
3307                         break;
3308
3309                 /* Change representation to figure out whether
3310                  * it is a single- or double-width character. */
3311
3312                 unicode = utf8_to_unicode(string, bytes);
3313                 /* FIXME: Graceful handling of invalid unicode character. */
3314                 if (!unicode)
3315                         break;
3316
3317                 ucwidth = unicode_width(unicode);
3318                 width  += ucwidth;
3319                 if (width > max_width) {
3320                         *trimmed = 1;
3321                         break;
3322                 }
3323
3324                 /* The column offset collects the differences between the
3325                  * number of bytes encoding a character and the number of
3326                  * columns will be used for rendering said character.
3327                  *
3328                  * So if some character A is encoded in 2 bytes, but will be
3329                  * represented on the screen using only 1 byte this will and up
3330                  * adding 1 to the multi-byte column offset.
3331                  *
3332                  * Assumes that no double-width character can be encoding in
3333                  * less than two bytes. */
3334                 if (bytes > ucwidth)
3335                         mbwidth += bytes - ucwidth;
3336
3337                 string  += bytes;
3338         }
3339
3340         *coloffset += mbwidth;
3341
3342         return string - start;
3343 }
3344
3345
3346 /*
3347  * Status management
3348  */
3349
3350 /* Whether or not the curses interface has been initialized. */
3351 static bool cursed = FALSE;
3352
3353 /* The status window is used for polling keystrokes. */
3354 static WINDOW *status_win;
3355
3356 static bool status_empty = TRUE;
3357
3358 /* Update status and title window. */
3359 static void
3360 report(const char *msg, ...)
3361 {
3362         struct view *view = display[current_view];
3363
3364         if (input_mode)
3365                 return;
3366
3367         if (!status_empty || *msg) {
3368                 va_list args;
3369
3370                 va_start(args, msg);
3371
3372                 wmove(status_win, 0, 0);
3373                 if (*msg) {
3374                         vwprintw(status_win, msg, args);
3375                         status_empty = FALSE;
3376                 } else {
3377                         status_empty = TRUE;
3378                 }
3379                 wclrtoeol(status_win);
3380                 wrefresh(status_win);
3381
3382                 va_end(args);
3383         }
3384
3385         update_view_title(view);
3386         update_display_cursor(view);
3387 }
3388
3389 /* Controls when nodelay should be in effect when polling user input. */
3390 static void
3391 set_nonblocking_input(bool loading)
3392 {
3393         static unsigned int loading_views;
3394
3395         if ((loading == FALSE && loading_views-- == 1) ||
3396             (loading == TRUE  && loading_views++ == 0))
3397                 nodelay(status_win, loading);
3398 }
3399
3400 static void
3401 init_display(void)
3402 {
3403         int x, y;
3404
3405         /* Initialize the curses library */
3406         if (isatty(STDIN_FILENO)) {
3407                 cursed = !!initscr();
3408         } else {
3409                 /* Leave stdin and stdout alone when acting as a pager. */
3410                 FILE *io = fopen("/dev/tty", "r+");
3411
3412                 if (!io)
3413                         die("Failed to open /dev/tty");
3414                 cursed = !!newterm(NULL, io, io);
3415         }
3416
3417         if (!cursed)
3418                 die("Failed to initialize curses");
3419
3420         nonl();         /* Tell curses not to do NL->CR/NL on output */
3421         cbreak();       /* Take input chars one at a time, no wait for \n */
3422         noecho();       /* Don't echo input */
3423         leaveok(stdscr, TRUE);
3424
3425         if (has_colors())
3426                 init_colors();
3427
3428         getmaxyx(stdscr, y, x);
3429         status_win = newwin(1, 0, y - 1, 0);
3430         if (!status_win)
3431                 die("Failed to create status window");
3432
3433         /* Enable keyboard mapping */
3434         keypad(status_win, TRUE);
3435         wbkgdset(status_win, get_line_attr(LINE_STATUS));
3436 }
3437
3438 static char *
3439 read_prompt(const char *prompt)
3440 {
3441         enum { READING, STOP, CANCEL } status = READING;
3442         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
3443         int pos = 0;
3444
3445         while (status == READING) {
3446                 struct view *view;
3447                 int i, key;
3448
3449                 input_mode = TRUE;
3450
3451                 foreach_view (view, i)
3452                         update_view(view);
3453
3454                 input_mode = FALSE;
3455
3456                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
3457                 wclrtoeol(status_win);
3458
3459                 /* Refresh, accept single keystroke of input */
3460                 key = wgetch(status_win);
3461                 switch (key) {
3462                 case KEY_RETURN:
3463                 case KEY_ENTER:
3464                 case '\n':
3465                         status = pos ? STOP : CANCEL;
3466                         break;
3467
3468                 case KEY_BACKSPACE:
3469                         if (pos > 0)
3470                                 pos--;
3471                         else
3472                                 status = CANCEL;
3473                         break;
3474
3475                 case KEY_ESC:
3476                         status = CANCEL;
3477                         break;
3478
3479                 case ERR:
3480                         break;
3481
3482                 default:
3483                         if (pos >= sizeof(buf)) {
3484                                 report("Input string too long");
3485                                 return NULL;
3486                         }
3487
3488                         if (isprint(key))
3489                                 buf[pos++] = (char) key;
3490                 }
3491         }
3492
3493         /* Clear the status window */
3494         status_empty = FALSE;
3495         report("");
3496
3497         if (status == CANCEL)
3498                 return NULL;
3499
3500         buf[pos++] = 0;
3501
3502         return buf;
3503 }
3504
3505 /*
3506  * Repository references
3507  */
3508
3509 static struct ref *refs;
3510 static size_t refs_size;
3511
3512 /* Id <-> ref store */
3513 static struct ref ***id_refs;
3514 static size_t id_refs_size;
3515
3516 static struct ref **
3517 get_refs(char *id)
3518 {
3519         struct ref ***tmp_id_refs;
3520         struct ref **ref_list = NULL;
3521         size_t ref_list_size = 0;
3522         size_t i;
3523
3524         for (i = 0; i < id_refs_size; i++)
3525                 if (!strcmp(id, id_refs[i][0]->id))
3526                         return id_refs[i];
3527
3528         tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
3529         if (!tmp_id_refs)
3530                 return NULL;
3531
3532         id_refs = tmp_id_refs;
3533
3534         for (i = 0; i < refs_size; i++) {
3535                 struct ref **tmp;
3536
3537                 if (strcmp(id, refs[i].id))
3538                         continue;
3539
3540                 tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
3541                 if (!tmp) {
3542                         if (ref_list)
3543                                 free(ref_list);
3544                         return NULL;
3545                 }
3546
3547                 ref_list = tmp;
3548                 if (ref_list_size > 0)
3549                         ref_list[ref_list_size - 1]->next = 1;
3550                 ref_list[ref_list_size] = &refs[i];
3551
3552                 /* XXX: The properties of the commit chains ensures that we can
3553                  * safely modify the shared ref. The repo references will
3554                  * always be similar for the same id. */
3555                 ref_list[ref_list_size]->next = 0;
3556                 ref_list_size++;
3557         }
3558
3559         if (ref_list)
3560                 id_refs[id_refs_size++] = ref_list;
3561
3562         return ref_list;
3563 }
3564
3565 static int
3566 read_ref(char *id, int idlen, char *name, int namelen)
3567 {
3568         struct ref *ref;
3569         bool tag = FALSE;
3570         bool remote = FALSE;
3571
3572         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
3573                 /* Commits referenced by tags has "^{}" appended. */
3574                 if (name[namelen - 1] != '}')
3575                         return OK;
3576
3577                 while (namelen > 0 && name[namelen] != '^')
3578                         namelen--;
3579
3580                 tag = TRUE;
3581                 namelen -= STRING_SIZE("refs/tags/");
3582                 name    += STRING_SIZE("refs/tags/");
3583
3584         } else if (!strncmp(name, "refs/remotes/", STRING_SIZE("refs/remotes/"))) {
3585                 remote = TRUE;
3586                 namelen -= STRING_SIZE("refs/remotes/");
3587                 name    += STRING_SIZE("refs/remotes/");
3588
3589         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
3590                 namelen -= STRING_SIZE("refs/heads/");
3591                 name    += STRING_SIZE("refs/heads/");
3592
3593         } else if (!strcmp(name, "HEAD")) {
3594                 return OK;
3595         }
3596
3597         refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
3598         if (!refs)
3599                 return ERR;
3600
3601         ref = &refs[refs_size++];
3602         ref->name = malloc(namelen + 1);
3603         if (!ref->name)
3604                 return ERR;
3605
3606         strncpy(ref->name, name, namelen);
3607         ref->name[namelen] = 0;
3608         ref->tag = tag;
3609         ref->remote = remote;
3610         string_copy(ref->id, id);
3611
3612         return OK;
3613 }
3614
3615 static int
3616 load_refs(void)
3617 {
3618         const char *cmd_env = getenv("TIG_LS_REMOTE");
3619         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
3620
3621         return read_properties(popen(cmd, "r"), "\t", read_ref);
3622 }
3623
3624 static int
3625 read_repo_config_option(char *name, int namelen, char *value, int valuelen)
3626 {
3627         if (!strcmp(name, "i18n.commitencoding"))
3628                 string_copy(opt_encoding, value);
3629
3630         return OK;
3631 }
3632
3633 static int
3634 load_repo_config(void)
3635 {
3636         return read_properties(popen("git repo-config --list", "r"),
3637                                "=", read_repo_config_option);
3638 }
3639
3640 static int
3641 read_properties(FILE *pipe, const char *separators,
3642                 int (*read_property)(char *, int, char *, int))
3643 {
3644         char buffer[BUFSIZ];
3645         char *name;
3646         int state = OK;
3647
3648         if (!pipe)
3649                 return ERR;
3650
3651         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
3652                 char *value;
3653                 size_t namelen;
3654                 size_t valuelen;
3655
3656                 name = chomp_string(name);
3657                 namelen = strcspn(name, separators);
3658
3659                 if (name[namelen]) {
3660                         name[namelen] = 0;
3661                         value = chomp_string(name + namelen + 1);
3662                         valuelen = strlen(value);
3663
3664                 } else {
3665                         value = "";
3666                         valuelen = 0;
3667                 }
3668
3669                 state = read_property(name, namelen, value, valuelen);
3670         }
3671
3672         if (state != ERR && ferror(pipe))
3673                 state = ERR;
3674
3675         pclose(pipe);
3676
3677         return state;
3678 }
3679
3680
3681 /*
3682  * Main
3683  */
3684
3685 static void __NORETURN
3686 quit(int sig)
3687 {
3688         /* XXX: Restore tty modes and let the OS cleanup the rest! */
3689         if (cursed)
3690                 endwin();
3691         exit(0);
3692 }
3693
3694 static void __NORETURN
3695 die(const char *err, ...)
3696 {
3697         va_list args;
3698
3699         endwin();
3700
3701         va_start(args, err);
3702         fputs("tig: ", stderr);
3703         vfprintf(stderr, err, args);
3704         fputs("\n", stderr);
3705         va_end(args);
3706
3707         exit(1);
3708 }
3709
3710 int
3711 main(int argc, char *argv[])
3712 {
3713         struct view *view;
3714         enum request request;
3715         size_t i;
3716
3717         signal(SIGINT, quit);
3718
3719         if (setlocale(LC_ALL, "")) {
3720                 string_copy(opt_codeset, nl_langinfo(CODESET));
3721         }
3722
3723         if (load_options() == ERR)
3724                 die("Failed to load user config.");
3725
3726         /* Load the repo config file so options can be overwritten from
3727          * the command line.  */
3728         if (load_repo_config() == ERR)
3729                 die("Failed to load repo config.");
3730
3731         if (!parse_options(argc, argv))
3732                 return 0;
3733
3734         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
3735                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
3736                 if (opt_iconv == ICONV_NONE)
3737                         die("Failed to initialize character set conversion");
3738         }
3739
3740         if (load_refs() == ERR)
3741                 die("Failed to load refs.");
3742
3743         /* Require a git repository unless when running in pager mode. */
3744         if (refs_size == 0 && opt_request != REQ_VIEW_PAGER)
3745                 die("Not a git repository");
3746
3747         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
3748                 view->cmd_env = getenv(view->cmd_env);
3749
3750         request = opt_request;
3751
3752         init_display();
3753
3754         while (view_driver(display[current_view], request)) {
3755                 int key;
3756                 int i;
3757
3758                 foreach_view (view, i)
3759                         update_view(view);
3760
3761                 /* Refresh, accept single keystroke of input */
3762                 key = wgetch(status_win);
3763
3764                 /* wgetch() with nodelay() enabled returns ERR when there's no
3765                  * input. */
3766                 if (key == ERR) {
3767                         request = REQ_NONE;
3768                         continue;
3769                 }
3770
3771                 request = get_keybinding(display[current_view]->keymap, key);
3772
3773                 /* Some low-level request handling. This keeps access to
3774                  * status_win restricted. */
3775                 switch (request) {
3776                 case REQ_PROMPT:
3777                 {
3778                         char *cmd = read_prompt(":");
3779
3780                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
3781                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
3782                                         opt_request = REQ_VIEW_DIFF;
3783                                 } else {
3784                                         opt_request = REQ_VIEW_PAGER;
3785                                 }
3786                                 break;
3787                         }
3788
3789                         request = REQ_NONE;
3790                         break;
3791                 }
3792                 case REQ_SEARCH:
3793                 case REQ_SEARCH_BACK:
3794                 {
3795                         const char *prompt = request == REQ_SEARCH
3796                                            ? "/" : "?";
3797                         char *search = read_prompt(prompt);
3798
3799                         if (search)
3800                                 string_copy(opt_search, search);
3801                         else
3802                                 request = REQ_NONE;
3803                         break;
3804                 }
3805                 case REQ_SCREEN_RESIZE:
3806                 {
3807                         int height, width;
3808
3809                         getmaxyx(stdscr, height, width);
3810
3811                         /* Resize the status view and let the view driver take
3812                          * care of resizing the displayed views. */
3813                         wresize(status_win, 1, width);
3814                         mvwin(status_win, height - 1, 0);
3815                         wrefresh(status_win);
3816                         break;
3817                 }
3818                 default:
3819                         break;
3820                 }
3821         }
3822
3823         quit(0);
3824
3825         return 0;
3826 }