New config options show-author, show-date, show-refs, show-line-numbers.
[tig] / tig.c
1 /* Copyright (c) 2006-2007 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 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17
18 #ifndef TIG_VERSION
19 #define TIG_VERSION "unknown-version"
20 #endif
21
22 #ifndef DEBUG
23 #define NDEBUG
24 #endif
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <signal.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <time.h>
38
39 #include <regex.h>
40
41 #include <locale.h>
42 #include <langinfo.h>
43 #include <iconv.h>
44
45 /* ncurses(3): Must be defined to have extended wide-character functions. */
46 #define _XOPEN_SOURCE_EXTENDED
47
48 #include <curses.h>
49
50 #if __GNUC__ >= 3
51 #define __NORETURN __attribute__((__noreturn__))
52 #else
53 #define __NORETURN
54 #endif
55
56 static void __NORETURN die(const char *err, ...);
57 static void warn(const char *msg, ...);
58 static void report(const char *msg, ...);
59 static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, size_t, char *, size_t));
60 static void set_nonblocking_input(bool loading);
61 static size_t utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve);
62
63 #define ABS(x)          ((x) >= 0  ? (x) : -(x))
64 #define MIN(x, y)       ((x) < (y) ? (x) :  (y))
65
66 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))
67 #define STRING_SIZE(x)  (sizeof(x) - 1)
68
69 #define SIZEOF_STR      1024    /* Default string size. */
70 #define SIZEOF_REF      256     /* Size of symbolic or SHA1 ID. */
71 #define SIZEOF_REV      41      /* Holds a SHA-1 and an ending NUL */
72
73 /* Revision graph */
74
75 #define REVGRAPH_INIT   'I'
76 #define REVGRAPH_MERGE  'M'
77 #define REVGRAPH_BRANCH '+'
78 #define REVGRAPH_COMMIT '*'
79 #define REVGRAPH_BOUND  '^'
80 #define REVGRAPH_LINE   '|'
81
82 #define SIZEOF_REVGRAPH 19      /* Size of revision ancestry graphics. */
83
84 /* This color name can be used to refer to the default term colors. */
85 #define COLOR_DEFAULT   (-1)
86
87 #define ICONV_NONE      ((iconv_t) -1)
88 #ifndef ICONV_CONST
89 #define ICONV_CONST     /* nothing */
90 #endif
91
92 /* The format and size of the date column in the main view. */
93 #define DATE_FORMAT     "%Y-%m-%d %H:%M"
94 #define DATE_COLS       STRING_SIZE("2006-04-29 14:21 ")
95
96 #define AUTHOR_COLS     20
97
98 /* The default interval between line numbers. */
99 #define NUMBER_INTERVAL 1
100
101 #define TABSIZE         8
102
103 #define SCALE_SPLIT_VIEW(height)        ((height) * 2 / 3)
104
105 #ifndef GIT_CONFIG
106 #define GIT_CONFIG "git config"
107 #endif
108
109 #define TIG_LS_REMOTE \
110         "git ls-remote $(git rev-parse --git-dir) 2>/dev/null"
111
112 #define TIG_DIFF_CMD \
113         "git show --pretty=fuller --no-color --root --patch-with-stat --find-copies-harder -C %s 2>/dev/null"
114
115 #define TIG_LOG_CMD     \
116         "git log --no-color --cc --stat -n100 %s 2>/dev/null"
117
118 #define TIG_MAIN_CMD \
119         "git log --no-color --topo-order --boundary --pretty=raw %s 2>/dev/null"
120
121 #define TIG_TREE_CMD    \
122         "git ls-tree %s %s"
123
124 #define TIG_BLOB_CMD    \
125         "git cat-file blob %s"
126
127 /* XXX: Needs to be defined to the empty string. */
128 #define TIG_HELP_CMD    ""
129 #define TIG_PAGER_CMD   ""
130 #define TIG_STATUS_CMD  ""
131 #define TIG_STAGE_CMD   ""
132
133 /* Some ascii-shorthands fitted into the ncurses namespace. */
134 #define KEY_TAB         '\t'
135 #define KEY_RETURN      '\r'
136 #define KEY_ESC         27
137
138
139 struct ref {
140         char *name;             /* Ref name; tag or head names are shortened. */
141         char id[SIZEOF_REV];    /* Commit SHA1 ID */
142         unsigned int tag:1;     /* Is it a tag? */
143         unsigned int ltag:1;    /* If so, is the tag local? */
144         unsigned int remote:1;  /* Is it a remote ref? */
145         unsigned int next:1;    /* For ref lists: are there more refs? */
146 };
147
148 static struct ref **get_refs(char *id);
149
150 struct int_map {
151         const char *name;
152         int namelen;
153         int value;
154 };
155
156 static int
157 set_from_int_map(struct int_map *map, size_t map_size,
158                  int *value, const char *name, int namelen)
159 {
160
161         int i;
162
163         for (i = 0; i < map_size; i++)
164                 if (namelen == map[i].namelen &&
165                     !strncasecmp(name, map[i].name, namelen)) {
166                         *value = map[i].value;
167                         return OK;
168                 }
169
170         return ERR;
171 }
172
173
174 /*
175  * String helpers
176  */
177
178 static inline void
179 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
180 {
181         if (srclen > dstlen - 1)
182                 srclen = dstlen - 1;
183
184         strncpy(dst, src, srclen);
185         dst[srclen] = 0;
186 }
187
188 /* Shorthands for safely copying into a fixed buffer. */
189
190 #define string_copy(dst, src) \
191         string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
192
193 #define string_ncopy(dst, src, srclen) \
194         string_ncopy_do(dst, sizeof(dst), src, srclen)
195
196 #define string_copy_rev(dst, src) \
197         string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
198
199 #define string_add(dst, from, src) \
200         string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
201
202 static char *
203 chomp_string(char *name)
204 {
205         int namelen;
206
207         while (isspace(*name))
208                 name++;
209
210         namelen = strlen(name) - 1;
211         while (namelen > 0 && isspace(name[namelen]))
212                 name[namelen--] = 0;
213
214         return name;
215 }
216
217 static bool
218 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
219 {
220         va_list args;
221         size_t pos = bufpos ? *bufpos : 0;
222
223         va_start(args, fmt);
224         pos += vsnprintf(buf + pos, bufsize - pos, fmt, args);
225         va_end(args);
226
227         if (bufpos)
228                 *bufpos = pos;
229
230         return pos >= bufsize ? FALSE : TRUE;
231 }
232
233 #define string_format(buf, fmt, args...) \
234         string_nformat(buf, sizeof(buf), NULL, fmt, args)
235
236 #define string_format_from(buf, from, fmt, args...) \
237         string_nformat(buf, sizeof(buf), from, fmt, args)
238
239 static int
240 string_enum_compare(const char *str1, const char *str2, int len)
241 {
242         size_t i;
243
244 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
245
246         /* Diff-Header == DIFF_HEADER */
247         for (i = 0; i < len; i++) {
248                 if (toupper(str1[i]) == toupper(str2[i]))
249                         continue;
250
251                 if (string_enum_sep(str1[i]) &&
252                     string_enum_sep(str2[i]))
253                         continue;
254
255                 return str1[i] - str2[i];
256         }
257
258         return 0;
259 }
260
261 /* Shell quoting
262  *
263  * NOTE: The following is a slightly modified copy of the git project's shell
264  * quoting routines found in the quote.c file.
265  *
266  * Help to copy the thing properly quoted for the shell safety.  any single
267  * quote is replaced with '\'', any exclamation point is replaced with '\!',
268  * and the whole thing is enclosed in a
269  *
270  * E.g.
271  *  original     sq_quote     result
272  *  name     ==> name      ==> 'name'
273  *  a b      ==> a b       ==> 'a b'
274  *  a'b      ==> a'\''b    ==> 'a'\''b'
275  *  a!b      ==> a'\!'b    ==> 'a'\!'b'
276  */
277
278 static size_t
279 sq_quote(char buf[SIZEOF_STR], size_t bufsize, const char *src)
280 {
281         char c;
282
283 #define BUFPUT(x) do { if (bufsize < SIZEOF_STR) buf[bufsize++] = (x); } while (0)
284
285         BUFPUT('\'');
286         while ((c = *src++)) {
287                 if (c == '\'' || c == '!') {
288                         BUFPUT('\'');
289                         BUFPUT('\\');
290                         BUFPUT(c);
291                         BUFPUT('\'');
292                 } else {
293                         BUFPUT(c);
294                 }
295         }
296         BUFPUT('\'');
297
298         if (bufsize < SIZEOF_STR)
299                 buf[bufsize] = 0;
300
301         return bufsize;
302 }
303
304
305 /*
306  * User requests
307  */
308
309 #define REQ_INFO \
310         /* XXX: Keep the view request first and in sync with views[]. */ \
311         REQ_GROUP("View switching") \
312         REQ_(VIEW_MAIN,         "Show main view"), \
313         REQ_(VIEW_DIFF,         "Show diff view"), \
314         REQ_(VIEW_LOG,          "Show log view"), \
315         REQ_(VIEW_TREE,         "Show tree view"), \
316         REQ_(VIEW_BLOB,         "Show blob view"), \
317         REQ_(VIEW_HELP,         "Show help page"), \
318         REQ_(VIEW_PAGER,        "Show pager view"), \
319         REQ_(VIEW_STATUS,       "Show status view"), \
320         REQ_(VIEW_STAGE,        "Show stage view"), \
321         \
322         REQ_GROUP("View manipulation") \
323         REQ_(ENTER,             "Enter current line and scroll"), \
324         REQ_(NEXT,              "Move to next"), \
325         REQ_(PREVIOUS,          "Move to previous"), \
326         REQ_(VIEW_NEXT,         "Move focus to next view"), \
327         REQ_(REFRESH,           "Reload and refresh"), \
328         REQ_(VIEW_CLOSE,        "Close the current view"), \
329         REQ_(QUIT,              "Close all views and quit"), \
330         \
331         REQ_GROUP("Cursor navigation") \
332         REQ_(MOVE_UP,           "Move cursor one line up"), \
333         REQ_(MOVE_DOWN,         "Move cursor one line down"), \
334         REQ_(MOVE_PAGE_DOWN,    "Move cursor one page down"), \
335         REQ_(MOVE_PAGE_UP,      "Move cursor one page up"), \
336         REQ_(MOVE_FIRST_LINE,   "Move cursor to first line"), \
337         REQ_(MOVE_LAST_LINE,    "Move cursor to last line"), \
338         \
339         REQ_GROUP("Scrolling") \
340         REQ_(SCROLL_LINE_UP,    "Scroll one line up"), \
341         REQ_(SCROLL_LINE_DOWN,  "Scroll one line down"), \
342         REQ_(SCROLL_PAGE_UP,    "Scroll one page up"), \
343         REQ_(SCROLL_PAGE_DOWN,  "Scroll one page down"), \
344         \
345         REQ_GROUP("Searching") \
346         REQ_(SEARCH,            "Search the view"), \
347         REQ_(SEARCH_BACK,       "Search backwards in the view"), \
348         REQ_(FIND_NEXT,         "Find next search match"), \
349         REQ_(FIND_PREV,         "Find previous search match"), \
350         \
351         REQ_GROUP("Misc") \
352         REQ_(PROMPT,            "Bring up the prompt"), \
353         REQ_(SCREEN_REDRAW,     "Redraw the screen"), \
354         REQ_(SCREEN_RESIZE,     "Resize the screen"), \
355         REQ_(SHOW_VERSION,      "Show version information"), \
356         REQ_(STOP_LOADING,      "Stop all loading views"), \
357         REQ_(TOGGLE_LINENO,     "Toggle line numbers"), \
358         REQ_(TOGGLE_DATE,       "Toggle date display"), \
359         REQ_(TOGGLE_AUTHOR,     "Toggle author display"), \
360         REQ_(TOGGLE_REV_GRAPH,  "Toggle revision graph visualization"), \
361         REQ_(TOGGLE_REFS,       "Toggle reference display (tags/branches)"), \
362         REQ_(STATUS_UPDATE,     "Update file status"), \
363         REQ_(STATUS_MERGE,      "Merge file using external tool"), \
364         REQ_(TREE_PARENT,       "Switch to parent directory in tree view"), \
365         REQ_(EDIT,              "Open in editor"), \
366         REQ_(NONE,              "Do nothing")
367
368
369 /* User action requests. */
370 enum request {
371 #define REQ_GROUP(help)
372 #define REQ_(req, help) REQ_##req
373
374         /* Offset all requests to avoid conflicts with ncurses getch values. */
375         REQ_OFFSET = KEY_MAX + 1,
376         REQ_INFO
377
378 #undef  REQ_GROUP
379 #undef  REQ_
380 };
381
382 struct request_info {
383         enum request request;
384         char *name;
385         int namelen;
386         char *help;
387 };
388
389 static struct request_info req_info[] = {
390 #define REQ_GROUP(help) { 0, NULL, 0, (help) },
391 #define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) }
392         REQ_INFO
393 #undef  REQ_GROUP
394 #undef  REQ_
395 };
396
397 static enum request
398 get_request(const char *name)
399 {
400         int namelen = strlen(name);
401         int i;
402
403         for (i = 0; i < ARRAY_SIZE(req_info); i++)
404                 if (req_info[i].namelen == namelen &&
405                     !string_enum_compare(req_info[i].name, name, namelen))
406                         return req_info[i].request;
407
408         return REQ_NONE;
409 }
410
411
412 /*
413  * Options
414  */
415
416 static const char usage[] =
417 "tig " TIG_VERSION " (" __DATE__ ")\n"
418 "\n"
419 "Usage: tig        [options] [revs] [--] [paths]\n"
420 "   or: tig show   [options] [revs] [--] [paths]\n"
421 "   or: tig status\n"
422 "   or: tig <      [git command output]\n"
423 "\n"
424 "Options:\n"
425 "  -v, --version   Show version and exit\n"
426 "  -h, --help      Show help message and exit\n";
427
428 /* Option and state variables. */
429 static bool opt_date                    = TRUE;
430 static bool opt_author                  = TRUE;
431 static bool opt_line_number             = FALSE;
432 static bool opt_rev_graph               = FALSE;
433 static bool opt_show_refs               = TRUE;
434 static int opt_num_interval             = NUMBER_INTERVAL;
435 static int opt_tab_size                 = TABSIZE;
436 static enum request opt_request         = REQ_VIEW_MAIN;
437 static char opt_cmd[SIZEOF_STR]         = "";
438 static char opt_path[SIZEOF_STR]        = "";
439 static FILE *opt_pipe                   = NULL;
440 static char opt_encoding[20]            = "UTF-8";
441 static bool opt_utf8                    = TRUE;
442 static char opt_codeset[20]             = "UTF-8";
443 static iconv_t opt_iconv                = ICONV_NONE;
444 static char opt_search[SIZEOF_STR]      = "";
445 static char opt_cdup[SIZEOF_STR]        = "";
446 static char opt_git_dir[SIZEOF_STR]     = "";
447 static signed char opt_is_inside_work_tree      = -1; /* set to TRUE or FALSE */
448 static char opt_editor[SIZEOF_STR]      = "";
449
450 enum option_type {
451         OPT_NONE,
452         OPT_INT,
453 };
454
455 static bool
456 check_option(char *opt, char short_name, char *name, enum option_type type, ...)
457 {
458         va_list args;
459         char *value = "";
460         int *number;
461
462         if (opt[0] != '-')
463                 return FALSE;
464
465         if (opt[1] == '-') {
466                 int namelen = strlen(name);
467
468                 opt += 2;
469
470                 if (strncmp(opt, name, namelen))
471                         return FALSE;
472
473                 if (opt[namelen] == '=')
474                         value = opt + namelen + 1;
475
476         } else {
477                 if (!short_name || opt[1] != short_name)
478                         return FALSE;
479                 value = opt + 2;
480         }
481
482         va_start(args, type);
483         if (type == OPT_INT) {
484                 number = va_arg(args, int *);
485                 if (isdigit(*value))
486                         *number = atoi(value);
487         }
488         va_end(args);
489
490         return TRUE;
491 }
492
493 /* Returns the index of log or diff command or -1 to exit. */
494 static bool
495 parse_options(int argc, char *argv[])
496 {
497         char *altargv[1024];
498         int altargc = 0;
499         char *subcommand = NULL;
500         int i;
501
502         for (i = 1; i < argc; i++) {
503                 char *opt = argv[i];
504
505                 if (!strcmp(opt, "log") ||
506                     !strcmp(opt, "diff")) {
507                         subcommand = opt;
508                         opt_request = opt[0] == 'l'
509                                     ? REQ_VIEW_LOG : REQ_VIEW_DIFF;
510                         warn("`tig %s' has been deprecated", opt);
511                         break;
512                 }
513
514                 if (!strcmp(opt, "show")) {
515                         subcommand = opt;
516                         opt_request = REQ_VIEW_DIFF;
517                         break;
518                 }
519
520                 if (!strcmp(opt, "status")) {
521                         subcommand = opt;
522                         opt_request = REQ_VIEW_STATUS;
523                         break;
524                 }
525
526                 if (opt[0] && opt[0] != '-')
527                         break;
528
529                 if (!strcmp(opt, "--")) {
530                         i++;
531                         break;
532                 }
533
534                 if (check_option(opt, 'v', "version", OPT_NONE)) {
535                         printf("tig version %s\n", TIG_VERSION);
536                         return FALSE;
537                 }
538
539                 if (check_option(opt, 'h', "help", OPT_NONE)) {
540                         printf(usage);
541                         return FALSE;
542                 }
543
544                 if (!strcmp(opt, "-S")) {
545                         warn("`%s' has been deprecated; use `tig status' instead", opt);
546                         opt_request = REQ_VIEW_STATUS;
547                         continue;
548                 }
549
550                 if (!strcmp(opt, "-l")) {
551                         opt_request = REQ_VIEW_LOG;
552                 } else if (!strcmp(opt, "-d")) {
553                         opt_request = REQ_VIEW_DIFF;
554                 } else if (check_option(opt, 'n', "line-number", OPT_INT, &opt_num_interval)) {
555                         opt_line_number = TRUE;
556                 } else if (check_option(opt, 'b', "tab-size", OPT_INT, &opt_tab_size)) {
557                         opt_tab_size = MIN(opt_tab_size, TABSIZE);
558                 } else {
559                         if (altargc >= ARRAY_SIZE(altargv))
560                                 die("maximum number of arguments exceeded");
561                         altargv[altargc++] = opt;
562                         continue;
563                 }
564
565                 warn("`%s' has been deprecated", opt);
566         }
567
568         /* Check that no 'alt' arguments occured before a subcommand. */
569         if (subcommand && i < argc && altargc > 0)
570                 die("unknown arguments before `%s'", argv[i]);
571
572         if (!isatty(STDIN_FILENO)) {
573                 opt_request = REQ_VIEW_PAGER;
574                 opt_pipe = stdin;
575
576         } else if (opt_request == REQ_VIEW_STATUS) {
577                 if (argc - i > 1)
578                         warn("ignoring arguments after `%s'", argv[i]);
579
580         } else if (i < argc || altargc > 0) {
581                 int alti = 0;
582                 size_t buf_size;
583
584                 if (opt_request == REQ_VIEW_MAIN)
585                         /* XXX: This is vulnerable to the user overriding
586                          * options required for the main view parser. */
587                         string_copy(opt_cmd, "git log --no-color --pretty=raw --boundary");
588                 else
589                         string_copy(opt_cmd, "git");
590                 buf_size = strlen(opt_cmd);
591
592                 while (buf_size < sizeof(opt_cmd) && alti < altargc) {
593                         opt_cmd[buf_size++] = ' ';
594                         buf_size = sq_quote(opt_cmd, buf_size, altargv[alti++]);
595                 }
596
597                 while (buf_size < sizeof(opt_cmd) && i < argc) {
598                         opt_cmd[buf_size++] = ' ';
599                         buf_size = sq_quote(opt_cmd, buf_size, argv[i++]);
600                 }
601
602                 if (buf_size >= sizeof(opt_cmd))
603                         die("command too long");
604
605                 opt_cmd[buf_size] = 0;
606         }
607
608         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
609                 opt_utf8 = FALSE;
610
611         return TRUE;
612 }
613
614
615 /*
616  * Line-oriented content detection.
617  */
618
619 #define LINE_INFO \
620 LINE(DIFF_HEADER,  "diff --git ",       COLOR_YELLOW,   COLOR_DEFAULT,  0), \
621 LINE(DIFF_CHUNK,   "@@",                COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
622 LINE(DIFF_ADD,     "+",                 COLOR_GREEN,    COLOR_DEFAULT,  0), \
623 LINE(DIFF_DEL,     "-",                 COLOR_RED,      COLOR_DEFAULT,  0), \
624 LINE(DIFF_INDEX,        "index ",         COLOR_BLUE,   COLOR_DEFAULT,  0), \
625 LINE(DIFF_OLDMODE,      "old file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
626 LINE(DIFF_NEWMODE,      "new file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
627 LINE(DIFF_COPY_FROM,    "copy from",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
628 LINE(DIFF_COPY_TO,      "copy to",        COLOR_YELLOW, COLOR_DEFAULT,  0), \
629 LINE(DIFF_RENAME_FROM,  "rename from",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
630 LINE(DIFF_RENAME_TO,    "rename to",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
631 LINE(DIFF_SIMILARITY,   "similarity ",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
632 LINE(DIFF_DISSIMILARITY,"dissimilarity ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
633 LINE(DIFF_TREE,         "diff-tree ",     COLOR_BLUE,   COLOR_DEFAULT,  0), \
634 LINE(PP_AUTHOR,    "Author: ",          COLOR_CYAN,     COLOR_DEFAULT,  0), \
635 LINE(PP_COMMIT,    "Commit: ",          COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
636 LINE(PP_MERGE,     "Merge: ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
637 LINE(PP_DATE,      "Date:   ",          COLOR_YELLOW,   COLOR_DEFAULT,  0), \
638 LINE(PP_ADATE,     "AuthorDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
639 LINE(PP_CDATE,     "CommitDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
640 LINE(PP_REFS,      "Refs: ",            COLOR_RED,      COLOR_DEFAULT,  0), \
641 LINE(COMMIT,       "commit ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
642 LINE(PARENT,       "parent ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
643 LINE(TREE,         "tree ",             COLOR_BLUE,     COLOR_DEFAULT,  0), \
644 LINE(AUTHOR,       "author ",           COLOR_CYAN,     COLOR_DEFAULT,  0), \
645 LINE(COMMITTER,    "committer ",        COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
646 LINE(SIGNOFF,      "    Signed-off-by", COLOR_YELLOW,   COLOR_DEFAULT,  0), \
647 LINE(ACKED,        "    Acked-by",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
648 LINE(DEFAULT,      "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
649 LINE(CURSOR,       "",                  COLOR_WHITE,    COLOR_GREEN,    A_BOLD), \
650 LINE(STATUS,       "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
651 LINE(TITLE_BLUR,   "",                  COLOR_WHITE,    COLOR_BLUE,     0), \
652 LINE(TITLE_FOCUS,  "",                  COLOR_WHITE,    COLOR_BLUE,     A_BOLD), \
653 LINE(MAIN_DATE,    "",                  COLOR_BLUE,     COLOR_DEFAULT,  0), \
654 LINE(MAIN_AUTHOR,  "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
655 LINE(MAIN_COMMIT,  "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
656 LINE(MAIN_DELIM,   "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
657 LINE(MAIN_TAG,     "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
658 LINE(MAIN_LOCAL_TAG,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
659 LINE(MAIN_REMOTE,  "",                  COLOR_YELLOW,   COLOR_DEFAULT,  A_BOLD), \
660 LINE(MAIN_REF,     "",                  COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD), \
661 LINE(MAIN_REVGRAPH,"",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
662 LINE(TREE_DIR,     "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
663 LINE(TREE_FILE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
664 LINE(STAT_SECTION, "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
665 LINE(STAT_NONE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
666 LINE(STAT_STAGED,  "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
667 LINE(STAT_UNSTAGED,"",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
668 LINE(STAT_UNTRACKED,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0)
669
670 enum line_type {
671 #define LINE(type, line, fg, bg, attr) \
672         LINE_##type
673         LINE_INFO
674 #undef  LINE
675 };
676
677 struct line_info {
678         const char *name;       /* Option name. */
679         int namelen;            /* Size of option name. */
680         const char *line;       /* The start of line to match. */
681         int linelen;            /* Size of string to match. */
682         int fg, bg, attr;       /* Color and text attributes for the lines. */
683 };
684
685 static struct line_info line_info[] = {
686 #define LINE(type, line, fg, bg, attr) \
687         { #type, STRING_SIZE(#type), (line), STRING_SIZE(line), (fg), (bg), (attr) }
688         LINE_INFO
689 #undef  LINE
690 };
691
692 static enum line_type
693 get_line_type(char *line)
694 {
695         int linelen = strlen(line);
696         enum line_type type;
697
698         for (type = 0; type < ARRAY_SIZE(line_info); type++)
699                 /* Case insensitive search matches Signed-off-by lines better. */
700                 if (linelen >= line_info[type].linelen &&
701                     !strncasecmp(line_info[type].line, line, line_info[type].linelen))
702                         return type;
703
704         return LINE_DEFAULT;
705 }
706
707 static inline int
708 get_line_attr(enum line_type type)
709 {
710         assert(type < ARRAY_SIZE(line_info));
711         return COLOR_PAIR(type) | line_info[type].attr;
712 }
713
714 static struct line_info *
715 get_line_info(char *name, int namelen)
716 {
717         enum line_type type;
718
719         for (type = 0; type < ARRAY_SIZE(line_info); type++)
720                 if (namelen == line_info[type].namelen &&
721                     !string_enum_compare(line_info[type].name, name, namelen))
722                         return &line_info[type];
723
724         return NULL;
725 }
726
727 static void
728 init_colors(void)
729 {
730         int default_bg = line_info[LINE_DEFAULT].bg;
731         int default_fg = line_info[LINE_DEFAULT].fg;
732         enum line_type type;
733
734         start_color();
735
736         if (assume_default_colors(default_fg, default_bg) == ERR) {
737                 default_bg = COLOR_BLACK;
738                 default_fg = COLOR_WHITE;
739         }
740
741         for (type = 0; type < ARRAY_SIZE(line_info); type++) {
742                 struct line_info *info = &line_info[type];
743                 int bg = info->bg == COLOR_DEFAULT ? default_bg : info->bg;
744                 int fg = info->fg == COLOR_DEFAULT ? default_fg : info->fg;
745
746                 init_pair(type, fg, bg);
747         }
748 }
749
750 struct line {
751         enum line_type type;
752
753         /* State flags */
754         unsigned int selected:1;
755
756         void *data;             /* User data */
757 };
758
759
760 /*
761  * Keys
762  */
763
764 struct keybinding {
765         int alias;
766         enum request request;
767         struct keybinding *next;
768 };
769
770 static struct keybinding default_keybindings[] = {
771         /* View switching */
772         { 'm',          REQ_VIEW_MAIN },
773         { 'd',          REQ_VIEW_DIFF },
774         { 'l',          REQ_VIEW_LOG },
775         { 't',          REQ_VIEW_TREE },
776         { 'f',          REQ_VIEW_BLOB },
777         { 'p',          REQ_VIEW_PAGER },
778         { 'h',          REQ_VIEW_HELP },
779         { 'S',          REQ_VIEW_STATUS },
780         { 'c',          REQ_VIEW_STAGE },
781
782         /* View manipulation */
783         { 'q',          REQ_VIEW_CLOSE },
784         { KEY_TAB,      REQ_VIEW_NEXT },
785         { KEY_RETURN,   REQ_ENTER },
786         { KEY_UP,       REQ_PREVIOUS },
787         { KEY_DOWN,     REQ_NEXT },
788         { 'R',          REQ_REFRESH },
789
790         /* Cursor navigation */
791         { 'k',          REQ_MOVE_UP },
792         { 'j',          REQ_MOVE_DOWN },
793         { KEY_HOME,     REQ_MOVE_FIRST_LINE },
794         { KEY_END,      REQ_MOVE_LAST_LINE },
795         { KEY_NPAGE,    REQ_MOVE_PAGE_DOWN },
796         { ' ',          REQ_MOVE_PAGE_DOWN },
797         { KEY_PPAGE,    REQ_MOVE_PAGE_UP },
798         { 'b',          REQ_MOVE_PAGE_UP },
799         { '-',          REQ_MOVE_PAGE_UP },
800
801         /* Scrolling */
802         { KEY_IC,       REQ_SCROLL_LINE_UP },
803         { KEY_DC,       REQ_SCROLL_LINE_DOWN },
804         { 'w',          REQ_SCROLL_PAGE_UP },
805         { 's',          REQ_SCROLL_PAGE_DOWN },
806
807         /* Searching */
808         { '/',          REQ_SEARCH },
809         { '?',          REQ_SEARCH_BACK },
810         { 'n',          REQ_FIND_NEXT },
811         { 'N',          REQ_FIND_PREV },
812
813         /* Misc */
814         { 'Q',          REQ_QUIT },
815         { 'z',          REQ_STOP_LOADING },
816         { 'v',          REQ_SHOW_VERSION },
817         { 'r',          REQ_SCREEN_REDRAW },
818         { '.',          REQ_TOGGLE_LINENO },
819         { 'D',          REQ_TOGGLE_DATE },
820         { 'A',          REQ_TOGGLE_AUTHOR },
821         { 'g',          REQ_TOGGLE_REV_GRAPH },
822         { 'F',          REQ_TOGGLE_REFS },
823         { ':',          REQ_PROMPT },
824         { 'u',          REQ_STATUS_UPDATE },
825         { 'M',          REQ_STATUS_MERGE },
826         { ',',          REQ_TREE_PARENT },
827         { 'e',          REQ_EDIT },
828
829         /* Using the ncurses SIGWINCH handler. */
830         { KEY_RESIZE,   REQ_SCREEN_RESIZE },
831 };
832
833 #define KEYMAP_INFO \
834         KEYMAP_(GENERIC), \
835         KEYMAP_(MAIN), \
836         KEYMAP_(DIFF), \
837         KEYMAP_(LOG), \
838         KEYMAP_(TREE), \
839         KEYMAP_(BLOB), \
840         KEYMAP_(PAGER), \
841         KEYMAP_(HELP), \
842         KEYMAP_(STATUS), \
843         KEYMAP_(STAGE)
844
845 enum keymap {
846 #define KEYMAP_(name) KEYMAP_##name
847         KEYMAP_INFO
848 #undef  KEYMAP_
849 };
850
851 static struct int_map keymap_table[] = {
852 #define KEYMAP_(name) { #name, STRING_SIZE(#name), KEYMAP_##name }
853         KEYMAP_INFO
854 #undef  KEYMAP_
855 };
856
857 #define set_keymap(map, name) \
858         set_from_int_map(keymap_table, ARRAY_SIZE(keymap_table), map, name, strlen(name))
859
860 static struct keybinding *keybindings[ARRAY_SIZE(keymap_table)];
861
862 static void
863 add_keybinding(enum keymap keymap, enum request request, int key)
864 {
865         struct keybinding *keybinding;
866
867         keybinding = calloc(1, sizeof(*keybinding));
868         if (!keybinding)
869                 die("Failed to allocate keybinding");
870
871         keybinding->alias = key;
872         keybinding->request = request;
873         keybinding->next = keybindings[keymap];
874         keybindings[keymap] = keybinding;
875 }
876
877 /* Looks for a key binding first in the given map, then in the generic map, and
878  * lastly in the default keybindings. */
879 static enum request
880 get_keybinding(enum keymap keymap, int key)
881 {
882         struct keybinding *kbd;
883         int i;
884
885         for (kbd = keybindings[keymap]; kbd; kbd = kbd->next)
886                 if (kbd->alias == key)
887                         return kbd->request;
888
889         for (kbd = keybindings[KEYMAP_GENERIC]; kbd; kbd = kbd->next)
890                 if (kbd->alias == key)
891                         return kbd->request;
892
893         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++)
894                 if (default_keybindings[i].alias == key)
895                         return default_keybindings[i].request;
896
897         return (enum request) key;
898 }
899
900
901 struct key {
902         char *name;
903         int value;
904 };
905
906 static struct key key_table[] = {
907         { "Enter",      KEY_RETURN },
908         { "Space",      ' ' },
909         { "Backspace",  KEY_BACKSPACE },
910         { "Tab",        KEY_TAB },
911         { "Escape",     KEY_ESC },
912         { "Left",       KEY_LEFT },
913         { "Right",      KEY_RIGHT },
914         { "Up",         KEY_UP },
915         { "Down",       KEY_DOWN },
916         { "Insert",     KEY_IC },
917         { "Delete",     KEY_DC },
918         { "Hash",       '#' },
919         { "Home",       KEY_HOME },
920         { "End",        KEY_END },
921         { "PageUp",     KEY_PPAGE },
922         { "PageDown",   KEY_NPAGE },
923         { "F1",         KEY_F(1) },
924         { "F2",         KEY_F(2) },
925         { "F3",         KEY_F(3) },
926         { "F4",         KEY_F(4) },
927         { "F5",         KEY_F(5) },
928         { "F6",         KEY_F(6) },
929         { "F7",         KEY_F(7) },
930         { "F8",         KEY_F(8) },
931         { "F9",         KEY_F(9) },
932         { "F10",        KEY_F(10) },
933         { "F11",        KEY_F(11) },
934         { "F12",        KEY_F(12) },
935 };
936
937 static int
938 get_key_value(const char *name)
939 {
940         int i;
941
942         for (i = 0; i < ARRAY_SIZE(key_table); i++)
943                 if (!strcasecmp(key_table[i].name, name))
944                         return key_table[i].value;
945
946         if (strlen(name) == 1 && isprint(*name))
947                 return (int) *name;
948
949         return ERR;
950 }
951
952 static char *
953 get_key_name(int key_value)
954 {
955         static char key_char[] = "'X'";
956         char *seq = NULL;
957         int key;
958
959         for (key = 0; key < ARRAY_SIZE(key_table); key++)
960                 if (key_table[key].value == key_value)
961                         seq = key_table[key].name;
962
963         if (seq == NULL &&
964             key_value < 127 &&
965             isprint(key_value)) {
966                 key_char[1] = (char) key_value;
967                 seq = key_char;
968         }
969
970         return seq ? seq : "'?'";
971 }
972
973 static char *
974 get_key(enum request request)
975 {
976         static char buf[BUFSIZ];
977         size_t pos = 0;
978         char *sep = "";
979         int i;
980
981         buf[pos] = 0;
982
983         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
984                 struct keybinding *keybinding = &default_keybindings[i];
985
986                 if (keybinding->request != request)
987                         continue;
988
989                 if (!string_format_from(buf, &pos, "%s%s", sep,
990                                         get_key_name(keybinding->alias)))
991                         return "Too many keybindings!";
992                 sep = ", ";
993         }
994
995         return buf;
996 }
997
998 struct run_request {
999         enum keymap keymap;
1000         int key;
1001         char cmd[SIZEOF_STR];
1002 };
1003
1004 static struct run_request *run_request;
1005 static size_t run_requests;
1006
1007 static enum request
1008 add_run_request(enum keymap keymap, int key, int argc, char **argv)
1009 {
1010         struct run_request *tmp;
1011         struct run_request req = { keymap, key };
1012         size_t bufpos;
1013
1014         for (bufpos = 0; argc > 0; argc--, argv++)
1015                 if (!string_format_from(req.cmd, &bufpos, "%s ", *argv))
1016                         return REQ_NONE;
1017
1018         req.cmd[bufpos - 1] = 0;
1019
1020         tmp = realloc(run_request, (run_requests + 1) * sizeof(*run_request));
1021         if (!tmp)
1022                 return REQ_NONE;
1023
1024         run_request = tmp;
1025         run_request[run_requests++] = req;
1026
1027         return REQ_NONE + run_requests;
1028 }
1029
1030 static struct run_request *
1031 get_run_request(enum request request)
1032 {
1033         if (request <= REQ_NONE)
1034                 return NULL;
1035         return &run_request[request - REQ_NONE - 1];
1036 }
1037
1038 static void
1039 add_builtin_run_requests(void)
1040 {
1041         struct {
1042                 enum keymap keymap;
1043                 int key;
1044                 char *argv[1];
1045         } reqs[] = {
1046                 { KEYMAP_MAIN,    'C', { "git cherry-pick %(commit)" } },
1047                 { KEYMAP_GENERIC, 'G', { "git gc" } },
1048         };
1049         int i;
1050
1051         for (i = 0; i < ARRAY_SIZE(reqs); i++) {
1052                 enum request req;
1053
1054                 req = add_run_request(reqs[i].keymap, reqs[i].key, 1, reqs[i].argv);
1055                 if (req != REQ_NONE)
1056                         add_keybinding(reqs[i].keymap, req, reqs[i].key);
1057         }
1058 }
1059
1060 /*
1061  * User config file handling.
1062  */
1063
1064 static struct int_map color_map[] = {
1065 #define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name }
1066         COLOR_MAP(DEFAULT),
1067         COLOR_MAP(BLACK),
1068         COLOR_MAP(BLUE),
1069         COLOR_MAP(CYAN),
1070         COLOR_MAP(GREEN),
1071         COLOR_MAP(MAGENTA),
1072         COLOR_MAP(RED),
1073         COLOR_MAP(WHITE),
1074         COLOR_MAP(YELLOW),
1075 };
1076
1077 #define set_color(color, name) \
1078         set_from_int_map(color_map, ARRAY_SIZE(color_map), color, name, strlen(name))
1079
1080 static struct int_map attr_map[] = {
1081 #define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name }
1082         ATTR_MAP(NORMAL),
1083         ATTR_MAP(BLINK),
1084         ATTR_MAP(BOLD),
1085         ATTR_MAP(DIM),
1086         ATTR_MAP(REVERSE),
1087         ATTR_MAP(STANDOUT),
1088         ATTR_MAP(UNDERLINE),
1089 };
1090
1091 #define set_attribute(attr, name) \
1092         set_from_int_map(attr_map, ARRAY_SIZE(attr_map), attr, name, strlen(name))
1093
1094 static int   config_lineno;
1095 static bool  config_errors;
1096 static char *config_msg;
1097
1098 /* Wants: object fgcolor bgcolor [attr] */
1099 static int
1100 option_color_command(int argc, char *argv[])
1101 {
1102         struct line_info *info;
1103
1104         if (argc != 3 && argc != 4) {
1105                 config_msg = "Wrong number of arguments given to color command";
1106                 return ERR;
1107         }
1108
1109         info = get_line_info(argv[0], strlen(argv[0]));
1110         if (!info) {
1111                 config_msg = "Unknown color name";
1112                 return ERR;
1113         }
1114
1115         if (set_color(&info->fg, argv[1]) == ERR ||
1116             set_color(&info->bg, argv[2]) == ERR) {
1117                 config_msg = "Unknown color";
1118                 return ERR;
1119         }
1120
1121         if (argc == 4 && set_attribute(&info->attr, argv[3]) == ERR) {
1122                 config_msg = "Unknown attribute";
1123                 return ERR;
1124         }
1125
1126         return OK;
1127 }
1128
1129 static bool parse_bool(const char *s)
1130 {
1131         return (!strcmp(s, "1") || !strcmp(s, "true") ||
1132                 !strcmp(s, "yes")) ? TRUE : FALSE;
1133 }
1134
1135 /* Wants: name = value */
1136 static int
1137 option_set_command(int argc, char *argv[])
1138 {
1139         if (argc != 3) {
1140                 config_msg = "Wrong number of arguments given to set command";
1141                 return ERR;
1142         }
1143
1144         if (strcmp(argv[1], "=")) {
1145                 config_msg = "No value assigned";
1146                 return ERR;
1147         }
1148
1149         if (!strcmp(argv[0], "show-author")) {
1150                 opt_author = parse_bool(argv[2]);
1151                 return OK;
1152         }
1153
1154         if (!strcmp(argv[0], "show-date")) {
1155                 opt_date = parse_bool(argv[2]);
1156                 return OK;
1157         }
1158
1159         if (!strcmp(argv[0], "show-rev-graph")) {
1160                 opt_rev_graph = parse_bool(argv[2]);
1161                 return OK;
1162         }
1163
1164         if (!strcmp(argv[0], "show-refs")) {
1165                 opt_show_refs = parse_bool(argv[2]);
1166                 return OK;
1167         }
1168
1169         if (!strcmp(argv[0], "show-line-numbers")) {
1170                 opt_line_number = parse_bool(argv[2]);
1171                 return OK;
1172         }
1173
1174         if (!strcmp(argv[0], "line-number-interval")) {
1175                 opt_num_interval = atoi(argv[2]);
1176                 return OK;
1177         }
1178
1179         if (!strcmp(argv[0], "tab-size")) {
1180                 opt_tab_size = atoi(argv[2]);
1181                 return OK;
1182         }
1183
1184         if (!strcmp(argv[0], "commit-encoding")) {
1185                 char *arg = argv[2];
1186                 int delimiter = *arg;
1187                 int i;
1188
1189                 switch (delimiter) {
1190                 case '"':
1191                 case '\'':
1192                         for (arg++, i = 0; arg[i]; i++)
1193                                 if (arg[i] == delimiter) {
1194                                         arg[i] = 0;
1195                                         break;
1196                                 }
1197                 default:
1198                         string_ncopy(opt_encoding, arg, strlen(arg));
1199                         return OK;
1200                 }
1201         }
1202
1203         config_msg = "Unknown variable name";
1204         return ERR;
1205 }
1206
1207 /* Wants: mode request key */
1208 static int
1209 option_bind_command(int argc, char *argv[])
1210 {
1211         enum request request;
1212         int keymap;
1213         int key;
1214
1215         if (argc < 3) {
1216                 config_msg = "Wrong number of arguments given to bind command";
1217                 return ERR;
1218         }
1219
1220         if (set_keymap(&keymap, argv[0]) == ERR) {
1221                 config_msg = "Unknown key map";
1222                 return ERR;
1223         }
1224
1225         key = get_key_value(argv[1]);
1226         if (key == ERR) {
1227                 config_msg = "Unknown key";
1228                 return ERR;
1229         }
1230
1231         request = get_request(argv[2]);
1232         if (request == REQ_NONE) {
1233                 const char *obsolete[] = { "cherry-pick" };
1234                 size_t namelen = strlen(argv[2]);
1235                 int i;
1236
1237                 for (i = 0; i < ARRAY_SIZE(obsolete); i++) {
1238                         if (namelen == strlen(obsolete[i]) &&
1239                             !string_enum_compare(obsolete[i], argv[2], namelen)) {
1240                                 config_msg = "Obsolete request name";
1241                                 return ERR;
1242                         }
1243                 }
1244         }
1245         if (request == REQ_NONE && *argv[2]++ == '!')
1246                 request = add_run_request(keymap, key, argc - 2, argv + 2);
1247         if (request == REQ_NONE) {
1248                 config_msg = "Unknown request name";
1249                 return ERR;
1250         }
1251
1252         add_keybinding(keymap, request, key);
1253
1254         return OK;
1255 }
1256
1257 static int
1258 set_option(char *opt, char *value)
1259 {
1260         char *argv[16];
1261         int valuelen;
1262         int argc = 0;
1263
1264         /* Tokenize */
1265         while (argc < ARRAY_SIZE(argv) && (valuelen = strcspn(value, " \t"))) {
1266                 argv[argc++] = value;
1267                 value += valuelen;
1268
1269                 /* Nothing more to tokenize or last available token. */
1270                 if (!*value || argc >= ARRAY_SIZE(argv))
1271                         break;
1272
1273                 *value++ = 0;
1274                 while (isspace(*value))
1275                         value++;
1276         }
1277
1278         if (!strcmp(opt, "color"))
1279                 return option_color_command(argc, argv);
1280
1281         if (!strcmp(opt, "set"))
1282                 return option_set_command(argc, argv);
1283
1284         if (!strcmp(opt, "bind"))
1285                 return option_bind_command(argc, argv);
1286
1287         config_msg = "Unknown option command";
1288         return ERR;
1289 }
1290
1291 static int
1292 read_option(char *opt, size_t optlen, char *value, size_t valuelen)
1293 {
1294         int status = OK;
1295
1296         config_lineno++;
1297         config_msg = "Internal error";
1298
1299         /* Check for comment markers, since read_properties() will
1300          * only ensure opt and value are split at first " \t". */
1301         optlen = strcspn(opt, "#");
1302         if (optlen == 0)
1303                 return OK;
1304
1305         if (opt[optlen] != 0) {
1306                 config_msg = "No option value";
1307                 status = ERR;
1308
1309         }  else {
1310                 /* Look for comment endings in the value. */
1311                 size_t len = strcspn(value, "#");
1312
1313                 if (len < valuelen) {
1314                         valuelen = len;
1315                         value[valuelen] = 0;
1316                 }
1317
1318                 status = set_option(opt, value);
1319         }
1320
1321         if (status == ERR) {
1322                 fprintf(stderr, "Error on line %d, near '%.*s': %s\n",
1323                         config_lineno, (int) optlen, opt, config_msg);
1324                 config_errors = TRUE;
1325         }
1326
1327         /* Always keep going if errors are encountered. */
1328         return OK;
1329 }
1330
1331 static void
1332 load_option_file(const char *path)
1333 {
1334         FILE *file;
1335
1336         /* It's ok that the file doesn't exist. */
1337         file = fopen(path, "r");
1338         if (!file)
1339                 return;
1340
1341         config_lineno = 0;
1342         config_errors = FALSE;
1343
1344         if (read_properties(file, " \t", read_option) == ERR ||
1345             config_errors == TRUE)
1346                 fprintf(stderr, "Errors while loading %s.\n", path);
1347 }
1348
1349 static int
1350 load_options(void)
1351 {
1352         char *home = getenv("HOME");
1353         char *tigrc_user = getenv("TIGRC_USER");
1354         char *tigrc_system = getenv("TIGRC_SYSTEM");
1355         char buf[SIZEOF_STR];
1356
1357         add_builtin_run_requests();
1358
1359         if (!tigrc_system) {
1360                 if (!string_format(buf, "%s/tigrc", SYSCONFDIR))
1361                         return ERR;
1362                 tigrc_system = buf;
1363         }
1364         load_option_file(tigrc_system);
1365
1366         if (!tigrc_user) {
1367                 if (!home || !string_format(buf, "%s/.tigrc", home))
1368                         return ERR;
1369                 tigrc_user = buf;
1370         }
1371         load_option_file(tigrc_user);
1372
1373         return OK;
1374 }
1375
1376
1377 /*
1378  * The viewer
1379  */
1380
1381 struct view;
1382 struct view_ops;
1383
1384 /* The display array of active views and the index of the current view. */
1385 static struct view *display[2];
1386 static unsigned int current_view;
1387
1388 /* Reading from the prompt? */
1389 static bool input_mode = FALSE;
1390
1391 #define foreach_displayed_view(view, i) \
1392         for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
1393
1394 #define displayed_views()       (display[1] != NULL ? 2 : 1)
1395
1396 /* Current head and commit ID */
1397 static char ref_blob[SIZEOF_REF]        = "";
1398 static char ref_commit[SIZEOF_REF]      = "HEAD";
1399 static char ref_head[SIZEOF_REF]        = "HEAD";
1400
1401 struct view {
1402         const char *name;       /* View name */
1403         const char *cmd_fmt;    /* Default command line format */
1404         const char *cmd_env;    /* Command line set via environment */
1405         const char *id;         /* Points to either of ref_{head,commit,blob} */
1406
1407         struct view_ops *ops;   /* View operations */
1408
1409         enum keymap keymap;     /* What keymap does this view have */
1410
1411         char cmd[SIZEOF_STR];   /* Command buffer */
1412         char ref[SIZEOF_REF];   /* Hovered commit reference */
1413         char vid[SIZEOF_REF];   /* View ID. Set to id member when updating. */
1414
1415         int height, width;      /* The width and height of the main window */
1416         WINDOW *win;            /* The main window */
1417         WINDOW *title;          /* The title window living below the main window */
1418
1419         /* Navigation */
1420         unsigned long offset;   /* Offset of the window top */
1421         unsigned long lineno;   /* Current line number */
1422
1423         /* Searching */
1424         char grep[SIZEOF_STR];  /* Search string */
1425         regex_t *regex;         /* Pre-compiled regex */
1426
1427         /* If non-NULL, points to the view that opened this view. If this view
1428          * is closed tig will switch back to the parent view. */
1429         struct view *parent;
1430
1431         /* Buffering */
1432         unsigned long lines;    /* Total number of lines */
1433         struct line *line;      /* Line index */
1434         unsigned long line_size;/* Total number of allocated lines */
1435         unsigned int digits;    /* Number of digits in the lines member. */
1436
1437         /* Loading */
1438         FILE *pipe;
1439         time_t start_time;
1440 };
1441
1442 struct view_ops {
1443         /* What type of content being displayed. Used in the title bar. */
1444         const char *type;
1445         /* Open and reads in all view content. */
1446         bool (*open)(struct view *view);
1447         /* Read one line; updates view->line. */
1448         bool (*read)(struct view *view, char *data);
1449         /* Draw one line; @lineno must be < view->height. */
1450         bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected);
1451         /* Depending on view handle a special requests. */
1452         enum request (*request)(struct view *view, enum request request, struct line *line);
1453         /* Search for regex in a line. */
1454         bool (*grep)(struct view *view, struct line *line);
1455         /* Select line */
1456         void (*select)(struct view *view, struct line *line);
1457 };
1458
1459 static struct view_ops pager_ops;
1460 static struct view_ops main_ops;
1461 static struct view_ops tree_ops;
1462 static struct view_ops blob_ops;
1463 static struct view_ops help_ops;
1464 static struct view_ops status_ops;
1465 static struct view_ops stage_ops;
1466
1467 #define VIEW_STR(name, cmd, env, ref, ops, map) \
1468         { name, cmd, #env, ref, ops, map}
1469
1470 #define VIEW_(id, name, ops, ref) \
1471         VIEW_STR(name, TIG_##id##_CMD,  TIG_##id##_CMD, ref, ops, KEYMAP_##id)
1472
1473
1474 static struct view views[] = {
1475         VIEW_(MAIN,   "main",   &main_ops,   ref_head),
1476         VIEW_(DIFF,   "diff",   &pager_ops,  ref_commit),
1477         VIEW_(LOG,    "log",    &pager_ops,  ref_head),
1478         VIEW_(TREE,   "tree",   &tree_ops,   ref_commit),
1479         VIEW_(BLOB,   "blob",   &blob_ops,   ref_blob),
1480         VIEW_(HELP,   "help",   &help_ops,   ""),
1481         VIEW_(PAGER,  "pager",  &pager_ops,  "stdin"),
1482         VIEW_(STATUS, "status", &status_ops, ""),
1483         VIEW_(STAGE,  "stage",  &stage_ops,  ""),
1484 };
1485
1486 #define VIEW(req) (&views[(req) - REQ_OFFSET - 1])
1487
1488 #define foreach_view(view, i) \
1489         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
1490
1491 #define view_is_displayed(view) \
1492         (view == display[0] || view == display[1])
1493
1494 static int
1495 draw_text(struct view *view, const char *string, int max_len, int col,
1496           bool use_tilde, int tilde_attr)
1497 {
1498         int len = 0;
1499         int trimmed = FALSE;
1500
1501         if (max_len <= 0)
1502                 return 0;
1503
1504         if (opt_utf8) {
1505                 len = utf8_length(string, max_len, &trimmed, use_tilde);
1506         } else {
1507                 len = strlen(string);
1508                 if (len > max_len) {
1509                         if (use_tilde) {
1510                                 max_len -= 1;
1511                         }
1512                         len = max_len;
1513                         trimmed = TRUE;
1514                 }
1515         }
1516
1517         waddnstr(view->win, string, len);
1518         if (trimmed && use_tilde) {
1519                 if (tilde_attr != -1)
1520                         wattrset(view->win, tilde_attr);
1521                 waddch(view->win, '~');
1522                 len++;
1523         }
1524
1525         return len;
1526 }
1527
1528 static bool
1529 draw_view_line(struct view *view, unsigned int lineno)
1530 {
1531         struct line *line;
1532         bool selected = (view->offset + lineno == view->lineno);
1533         bool draw_ok;
1534
1535         assert(view_is_displayed(view));
1536
1537         if (view->offset + lineno >= view->lines)
1538                 return FALSE;
1539
1540         line = &view->line[view->offset + lineno];
1541
1542         if (selected) {
1543                 line->selected = TRUE;
1544                 view->ops->select(view, line);
1545         } else if (line->selected) {
1546                 line->selected = FALSE;
1547                 wmove(view->win, lineno, 0);
1548                 wclrtoeol(view->win);
1549         }
1550
1551         scrollok(view->win, FALSE);
1552         draw_ok = view->ops->draw(view, line, lineno, selected);
1553         scrollok(view->win, TRUE);
1554
1555         return draw_ok;
1556 }
1557
1558 static void
1559 redraw_view_from(struct view *view, int lineno)
1560 {
1561         assert(0 <= lineno && lineno < view->height);
1562
1563         for (; lineno < view->height; lineno++) {
1564                 if (!draw_view_line(view, lineno))
1565                         break;
1566         }
1567
1568         redrawwin(view->win);
1569         if (input_mode)
1570                 wnoutrefresh(view->win);
1571         else
1572                 wrefresh(view->win);
1573 }
1574
1575 static void
1576 redraw_view(struct view *view)
1577 {
1578         wclear(view->win);
1579         redraw_view_from(view, 0);
1580 }
1581
1582
1583 static void
1584 update_view_title(struct view *view)
1585 {
1586         char buf[SIZEOF_STR];
1587         char state[SIZEOF_STR];
1588         size_t bufpos = 0, statelen = 0;
1589
1590         assert(view_is_displayed(view));
1591
1592         if (view != VIEW(REQ_VIEW_STATUS) && (view->lines || view->pipe)) {
1593                 unsigned int view_lines = view->offset + view->height;
1594                 unsigned int lines = view->lines
1595                                    ? MIN(view_lines, view->lines) * 100 / view->lines
1596                                    : 0;
1597
1598                 string_format_from(state, &statelen, "- %s %d of %d (%d%%)",
1599                                    view->ops->type,
1600                                    view->lineno + 1,
1601                                    view->lines,
1602                                    lines);
1603
1604                 if (view->pipe) {
1605                         time_t secs = time(NULL) - view->start_time;
1606
1607                         /* Three git seconds are a long time ... */
1608                         if (secs > 2)
1609                                 string_format_from(state, &statelen, " %lds", secs);
1610                 }
1611         }
1612
1613         string_format_from(buf, &bufpos, "[%s]", view->name);
1614         if (*view->ref && bufpos < view->width) {
1615                 size_t refsize = strlen(view->ref);
1616                 size_t minsize = bufpos + 1 + /* abbrev= */ 7 + 1 + statelen;
1617
1618                 if (minsize < view->width)
1619                         refsize = view->width - minsize + 7;
1620                 string_format_from(buf, &bufpos, " %.*s", (int) refsize, view->ref);
1621         }
1622
1623         if (statelen && bufpos < view->width) {
1624                 string_format_from(buf, &bufpos, " %s", state);
1625         }
1626
1627         if (view == display[current_view])
1628                 wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
1629         else
1630                 wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
1631
1632         mvwaddnstr(view->title, 0, 0, buf, bufpos);
1633         wclrtoeol(view->title);
1634         wmove(view->title, 0, view->width - 1);
1635
1636         if (input_mode)
1637                 wnoutrefresh(view->title);
1638         else
1639                 wrefresh(view->title);
1640 }
1641
1642 static void
1643 resize_display(void)
1644 {
1645         int offset, i;
1646         struct view *base = display[0];
1647         struct view *view = display[1] ? display[1] : display[0];
1648
1649         /* Setup window dimensions */
1650
1651         getmaxyx(stdscr, base->height, base->width);
1652
1653         /* Make room for the status window. */
1654         base->height -= 1;
1655
1656         if (view != base) {
1657                 /* Horizontal split. */
1658                 view->width   = base->width;
1659                 view->height  = SCALE_SPLIT_VIEW(base->height);
1660                 base->height -= view->height;
1661
1662                 /* Make room for the title bar. */
1663                 view->height -= 1;
1664         }
1665
1666         /* Make room for the title bar. */
1667         base->height -= 1;
1668
1669         offset = 0;
1670
1671         foreach_displayed_view (view, i) {
1672                 if (!view->win) {
1673                         view->win = newwin(view->height, 0, offset, 0);
1674                         if (!view->win)
1675                                 die("Failed to create %s view", view->name);
1676
1677                         scrollok(view->win, TRUE);
1678
1679                         view->title = newwin(1, 0, offset + view->height, 0);
1680                         if (!view->title)
1681                                 die("Failed to create title window");
1682
1683                 } else {
1684                         wresize(view->win, view->height, view->width);
1685                         mvwin(view->win,   offset, 0);
1686                         mvwin(view->title, offset + view->height, 0);
1687                 }
1688
1689                 offset += view->height + 1;
1690         }
1691 }
1692
1693 static void
1694 redraw_display(void)
1695 {
1696         struct view *view;
1697         int i;
1698
1699         foreach_displayed_view (view, i) {
1700                 redraw_view(view);
1701                 update_view_title(view);
1702         }
1703 }
1704
1705 static void
1706 update_display_cursor(struct view *view)
1707 {
1708         /* Move the cursor to the right-most column of the cursor line.
1709          *
1710          * XXX: This could turn out to be a bit expensive, but it ensures that
1711          * the cursor does not jump around. */
1712         if (view->lines) {
1713                 wmove(view->win, view->lineno - view->offset, view->width - 1);
1714                 wrefresh(view->win);
1715         }
1716 }
1717
1718 /*
1719  * Navigation
1720  */
1721
1722 /* Scrolling backend */
1723 static void
1724 do_scroll_view(struct view *view, int lines)
1725 {
1726         bool redraw_current_line = FALSE;
1727
1728         /* The rendering expects the new offset. */
1729         view->offset += lines;
1730
1731         assert(0 <= view->offset && view->offset < view->lines);
1732         assert(lines);
1733
1734         /* Move current line into the view. */
1735         if (view->lineno < view->offset) {
1736                 view->lineno = view->offset;
1737                 redraw_current_line = TRUE;
1738         } else if (view->lineno >= view->offset + view->height) {
1739                 view->lineno = view->offset + view->height - 1;
1740                 redraw_current_line = TRUE;
1741         }
1742
1743         assert(view->offset <= view->lineno && view->lineno < view->lines);
1744
1745         /* Redraw the whole screen if scrolling is pointless. */
1746         if (view->height < ABS(lines)) {
1747                 redraw_view(view);
1748
1749         } else {
1750                 int line = lines > 0 ? view->height - lines : 0;
1751                 int end = line + ABS(lines);
1752
1753                 wscrl(view->win, lines);
1754
1755                 for (; line < end; line++) {
1756                         if (!draw_view_line(view, line))
1757                                 break;
1758                 }
1759
1760                 if (redraw_current_line)
1761                         draw_view_line(view, view->lineno - view->offset);
1762         }
1763
1764         redrawwin(view->win);
1765         wrefresh(view->win);
1766         report("");
1767 }
1768
1769 /* Scroll frontend */
1770 static void
1771 scroll_view(struct view *view, enum request request)
1772 {
1773         int lines = 1;
1774
1775         assert(view_is_displayed(view));
1776
1777         switch (request) {
1778         case REQ_SCROLL_PAGE_DOWN:
1779                 lines = view->height;
1780         case REQ_SCROLL_LINE_DOWN:
1781                 if (view->offset + lines > view->lines)
1782                         lines = view->lines - view->offset;
1783
1784                 if (lines == 0 || view->offset + view->height >= view->lines) {
1785                         report("Cannot scroll beyond the last line");
1786                         return;
1787                 }
1788                 break;
1789
1790         case REQ_SCROLL_PAGE_UP:
1791                 lines = view->height;
1792         case REQ_SCROLL_LINE_UP:
1793                 if (lines > view->offset)
1794                         lines = view->offset;
1795
1796                 if (lines == 0) {
1797                         report("Cannot scroll beyond the first line");
1798                         return;
1799                 }
1800
1801                 lines = -lines;
1802                 break;
1803
1804         default:
1805                 die("request %d not handled in switch", request);
1806         }
1807
1808         do_scroll_view(view, lines);
1809 }
1810
1811 /* Cursor moving */
1812 static void
1813 move_view(struct view *view, enum request request)
1814 {
1815         int scroll_steps = 0;
1816         int steps;
1817
1818         switch (request) {
1819         case REQ_MOVE_FIRST_LINE:
1820                 steps = -view->lineno;
1821                 break;
1822
1823         case REQ_MOVE_LAST_LINE:
1824                 steps = view->lines - view->lineno - 1;
1825                 break;
1826
1827         case REQ_MOVE_PAGE_UP:
1828                 steps = view->height > view->lineno
1829                       ? -view->lineno : -view->height;
1830                 break;
1831
1832         case REQ_MOVE_PAGE_DOWN:
1833                 steps = view->lineno + view->height >= view->lines
1834                       ? view->lines - view->lineno - 1 : view->height;
1835                 break;
1836
1837         case REQ_MOVE_UP:
1838                 steps = -1;
1839                 break;
1840
1841         case REQ_MOVE_DOWN:
1842                 steps = 1;
1843                 break;
1844
1845         default:
1846                 die("request %d not handled in switch", request);
1847         }
1848
1849         if (steps <= 0 && view->lineno == 0) {
1850                 report("Cannot move beyond the first line");
1851                 return;
1852
1853         } else if (steps >= 0 && view->lineno + 1 >= view->lines) {
1854                 report("Cannot move beyond the last line");
1855                 return;
1856         }
1857
1858         /* Move the current line */
1859         view->lineno += steps;
1860         assert(0 <= view->lineno && view->lineno < view->lines);
1861
1862         /* Check whether the view needs to be scrolled */
1863         if (view->lineno < view->offset ||
1864             view->lineno >= view->offset + view->height) {
1865                 scroll_steps = steps;
1866                 if (steps < 0 && -steps > view->offset) {
1867                         scroll_steps = -view->offset;
1868
1869                 } else if (steps > 0) {
1870                         if (view->lineno == view->lines - 1 &&
1871                             view->lines > view->height) {
1872                                 scroll_steps = view->lines - view->offset - 1;
1873                                 if (scroll_steps >= view->height)
1874                                         scroll_steps -= view->height - 1;
1875                         }
1876                 }
1877         }
1878
1879         if (!view_is_displayed(view)) {
1880                 view->offset += scroll_steps;
1881                 assert(0 <= view->offset && view->offset < view->lines);
1882                 view->ops->select(view, &view->line[view->lineno]);
1883                 return;
1884         }
1885
1886         /* Repaint the old "current" line if we be scrolling */
1887         if (ABS(steps) < view->height)
1888                 draw_view_line(view, view->lineno - steps - view->offset);
1889
1890         if (scroll_steps) {
1891                 do_scroll_view(view, scroll_steps);
1892                 return;
1893         }
1894
1895         /* Draw the current line */
1896         draw_view_line(view, view->lineno - view->offset);
1897
1898         redrawwin(view->win);
1899         wrefresh(view->win);
1900         report("");
1901 }
1902
1903
1904 /*
1905  * Searching
1906  */
1907
1908 static void search_view(struct view *view, enum request request);
1909
1910 static bool
1911 find_next_line(struct view *view, unsigned long lineno, struct line *line)
1912 {
1913         assert(view_is_displayed(view));
1914
1915         if (!view->ops->grep(view, line))
1916                 return FALSE;
1917
1918         if (lineno - view->offset >= view->height) {
1919                 view->offset = lineno;
1920                 view->lineno = lineno;
1921                 redraw_view(view);
1922
1923         } else {
1924                 unsigned long old_lineno = view->lineno - view->offset;
1925
1926                 view->lineno = lineno;
1927                 draw_view_line(view, old_lineno);
1928
1929                 draw_view_line(view, view->lineno - view->offset);
1930                 redrawwin(view->win);
1931                 wrefresh(view->win);
1932         }
1933
1934         report("Line %ld matches '%s'", lineno + 1, view->grep);
1935         return TRUE;
1936 }
1937
1938 static void
1939 find_next(struct view *view, enum request request)
1940 {
1941         unsigned long lineno = view->lineno;
1942         int direction;
1943
1944         if (!*view->grep) {
1945                 if (!*opt_search)
1946                         report("No previous search");
1947                 else
1948                         search_view(view, request);
1949                 return;
1950         }
1951
1952         switch (request) {
1953         case REQ_SEARCH:
1954         case REQ_FIND_NEXT:
1955                 direction = 1;
1956                 break;
1957
1958         case REQ_SEARCH_BACK:
1959         case REQ_FIND_PREV:
1960                 direction = -1;
1961                 break;
1962
1963         default:
1964                 return;
1965         }
1966
1967         if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
1968                 lineno += direction;
1969
1970         /* Note, lineno is unsigned long so will wrap around in which case it
1971          * will become bigger than view->lines. */
1972         for (; lineno < view->lines; lineno += direction) {
1973                 struct line *line = &view->line[lineno];
1974
1975                 if (find_next_line(view, lineno, line))
1976                         return;
1977         }
1978
1979         report("No match found for '%s'", view->grep);
1980 }
1981
1982 static void
1983 search_view(struct view *view, enum request request)
1984 {
1985         int regex_err;
1986
1987         if (view->regex) {
1988                 regfree(view->regex);
1989                 *view->grep = 0;
1990         } else {
1991                 view->regex = calloc(1, sizeof(*view->regex));
1992                 if (!view->regex)
1993                         return;
1994         }
1995
1996         regex_err = regcomp(view->regex, opt_search, REG_EXTENDED);
1997         if (regex_err != 0) {
1998                 char buf[SIZEOF_STR] = "unknown error";
1999
2000                 regerror(regex_err, view->regex, buf, sizeof(buf));
2001                 report("Search failed: %s", buf);
2002                 return;
2003         }
2004
2005         string_copy(view->grep, opt_search);
2006
2007         find_next(view, request);
2008 }
2009
2010 /*
2011  * Incremental updating
2012  */
2013
2014 static void
2015 end_update(struct view *view)
2016 {
2017         if (!view->pipe)
2018                 return;
2019         set_nonblocking_input(FALSE);
2020         if (view->pipe == stdin)
2021                 fclose(view->pipe);
2022         else
2023                 pclose(view->pipe);
2024         view->pipe = NULL;
2025 }
2026
2027 static bool
2028 begin_update(struct view *view)
2029 {
2030         if (view->pipe)
2031                 end_update(view);
2032
2033         if (opt_cmd[0]) {
2034                 string_copy(view->cmd, opt_cmd);
2035                 opt_cmd[0] = 0;
2036                 /* When running random commands, initially show the
2037                  * command in the title. However, it maybe later be
2038                  * overwritten if a commit line is selected. */
2039                 if (view == VIEW(REQ_VIEW_PAGER))
2040                         string_copy(view->ref, view->cmd);
2041                 else
2042                         view->ref[0] = 0;
2043
2044         } else if (view == VIEW(REQ_VIEW_TREE)) {
2045                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
2046                 char path[SIZEOF_STR];
2047
2048                 if (strcmp(view->vid, view->id))
2049                         opt_path[0] = path[0] = 0;
2050                 else if (sq_quote(path, 0, opt_path) >= sizeof(path))
2051                         return FALSE;
2052
2053                 if (!string_format(view->cmd, format, view->id, path))
2054                         return FALSE;
2055
2056         } else {
2057                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
2058                 const char *id = view->id;
2059
2060                 if (!string_format(view->cmd, format, id, id, id, id, id))
2061                         return FALSE;
2062
2063                 /* Put the current ref_* value to the view title ref
2064                  * member. This is needed by the blob view. Most other
2065                  * views sets it automatically after loading because the
2066                  * first line is a commit line. */
2067                 string_copy_rev(view->ref, view->id);
2068         }
2069
2070         /* Special case for the pager view. */
2071         if (opt_pipe) {
2072                 view->pipe = opt_pipe;
2073                 opt_pipe = NULL;
2074         } else {
2075                 view->pipe = popen(view->cmd, "r");
2076         }
2077
2078         if (!view->pipe)
2079                 return FALSE;
2080
2081         set_nonblocking_input(TRUE);
2082
2083         view->offset = 0;
2084         view->lines  = 0;
2085         view->lineno = 0;
2086         string_copy_rev(view->vid, view->id);
2087
2088         if (view->line) {
2089                 int i;
2090
2091                 for (i = 0; i < view->lines; i++)
2092                         if (view->line[i].data)
2093                                 free(view->line[i].data);
2094
2095                 free(view->line);
2096                 view->line = NULL;
2097         }
2098
2099         view->start_time = time(NULL);
2100
2101         return TRUE;
2102 }
2103
2104 static struct line *
2105 realloc_lines(struct view *view, size_t line_size)
2106 {
2107         struct line *tmp = realloc(view->line, sizeof(*view->line) * line_size);
2108
2109         if (!tmp)
2110                 return NULL;
2111
2112         view->line = tmp;
2113         view->line_size = line_size;
2114         return view->line;
2115 }
2116
2117 static bool
2118 update_view(struct view *view)
2119 {
2120         char in_buffer[BUFSIZ];
2121         char out_buffer[BUFSIZ * 2];
2122         char *line;
2123         /* The number of lines to read. If too low it will cause too much
2124          * redrawing (and possible flickering), if too high responsiveness
2125          * will suffer. */
2126         unsigned long lines = view->height;
2127         int redraw_from = -1;
2128
2129         if (!view->pipe)
2130                 return TRUE;
2131
2132         /* Only redraw if lines are visible. */
2133         if (view->offset + view->height >= view->lines)
2134                 redraw_from = view->lines - view->offset;
2135
2136         /* FIXME: This is probably not perfect for backgrounded views. */
2137         if (!realloc_lines(view, view->lines + lines))
2138                 goto alloc_error;
2139
2140         while ((line = fgets(in_buffer, sizeof(in_buffer), view->pipe))) {
2141                 size_t linelen = strlen(line);
2142
2143                 if (linelen)
2144                         line[linelen - 1] = 0;
2145
2146                 if (opt_iconv != ICONV_NONE) {
2147                         ICONV_CONST char *inbuf = line;
2148                         size_t inlen = linelen;
2149
2150                         char *outbuf = out_buffer;
2151                         size_t outlen = sizeof(out_buffer);
2152
2153                         size_t ret;
2154
2155                         ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
2156                         if (ret != (size_t) -1) {
2157                                 line = out_buffer;
2158                                 linelen = strlen(out_buffer);
2159                         }
2160                 }
2161
2162                 if (!view->ops->read(view, line))
2163                         goto alloc_error;
2164
2165                 if (lines-- == 1)
2166                         break;
2167         }
2168
2169         {
2170                 int digits;
2171
2172                 lines = view->lines;
2173                 for (digits = 0; lines; digits++)
2174                         lines /= 10;
2175
2176                 /* Keep the displayed view in sync with line number scaling. */
2177                 if (digits != view->digits) {
2178                         view->digits = digits;
2179                         redraw_from = 0;
2180                 }
2181         }
2182
2183         if (!view_is_displayed(view))
2184                 goto check_pipe;
2185
2186         if (view == VIEW(REQ_VIEW_TREE)) {
2187                 /* Clear the view and redraw everything since the tree sorting
2188                  * might have rearranged things. */
2189                 redraw_view(view);
2190
2191         } else if (redraw_from >= 0) {
2192                 /* If this is an incremental update, redraw the previous line
2193                  * since for commits some members could have changed when
2194                  * loading the main view. */
2195                 if (redraw_from > 0)
2196                         redraw_from--;
2197
2198                 /* Since revision graph visualization requires knowledge
2199                  * about the parent commit, it causes a further one-off
2200                  * needed to be redrawn for incremental updates. */
2201                 if (redraw_from > 0 && opt_rev_graph)
2202                         redraw_from--;
2203
2204                 /* Incrementally draw avoids flickering. */
2205                 redraw_view_from(view, redraw_from);
2206         }
2207
2208         /* Update the title _after_ the redraw so that if the redraw picks up a
2209          * commit reference in view->ref it'll be available here. */
2210         update_view_title(view);
2211
2212 check_pipe:
2213         if (ferror(view->pipe)) {
2214                 report("Failed to read: %s", strerror(errno));
2215                 goto end;
2216
2217         } else if (feof(view->pipe)) {
2218                 report("");
2219                 goto end;
2220         }
2221
2222         return TRUE;
2223
2224 alloc_error:
2225         report("Allocation failure");
2226
2227 end:
2228         view->ops->read(view, NULL);
2229         end_update(view);
2230         return FALSE;
2231 }
2232
2233 static struct line *
2234 add_line_data(struct view *view, void *data, enum line_type type)
2235 {
2236         struct line *line = &view->line[view->lines++];
2237
2238         memset(line, 0, sizeof(*line));
2239         line->type = type;
2240         line->data = data;
2241
2242         return line;
2243 }
2244
2245 static struct line *
2246 add_line_text(struct view *view, char *data, enum line_type type)
2247 {
2248         if (data)
2249                 data = strdup(data);
2250
2251         return data ? add_line_data(view, data, type) : NULL;
2252 }
2253
2254
2255 /*
2256  * View opening
2257  */
2258
2259 enum open_flags {
2260         OPEN_DEFAULT = 0,       /* Use default view switching. */
2261         OPEN_SPLIT = 1,         /* Split current view. */
2262         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
2263         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2264 };
2265
2266 static void
2267 open_view(struct view *prev, enum request request, enum open_flags flags)
2268 {
2269         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
2270         bool split = !!(flags & OPEN_SPLIT);
2271         bool reload = !!(flags & OPEN_RELOAD);
2272         struct view *view = VIEW(request);
2273         int nviews = displayed_views();
2274         struct view *base_view = display[0];
2275
2276         if (view == prev && nviews == 1 && !reload) {
2277                 report("Already in %s view", view->name);
2278                 return;
2279         }
2280
2281         if (view->ops->open) {
2282                 if (!view->ops->open(view)) {
2283                         report("Failed to load %s view", view->name);
2284                         return;
2285                 }
2286
2287         } else if ((reload || strcmp(view->vid, view->id)) &&
2288                    !begin_update(view)) {
2289                 report("Failed to load %s view", view->name);
2290                 return;
2291         }
2292
2293         if (split) {
2294                 display[1] = view;
2295                 if (!backgrounded)
2296                         current_view = 1;
2297         } else {
2298                 /* Maximize the current view. */
2299                 memset(display, 0, sizeof(display));
2300                 current_view = 0;
2301                 display[current_view] = view;
2302         }
2303
2304         /* Resize the view when switching between split- and full-screen,
2305          * or when switching between two different full-screen views. */
2306         if (nviews != displayed_views() ||
2307             (nviews == 1 && base_view != display[0]))
2308                 resize_display();
2309
2310         if (split && prev->lineno - prev->offset >= prev->height) {
2311                 /* Take the title line into account. */
2312                 int lines = prev->lineno - prev->offset - prev->height + 1;
2313
2314                 /* Scroll the view that was split if the current line is
2315                  * outside the new limited view. */
2316                 do_scroll_view(prev, lines);
2317         }
2318
2319         if (prev && view != prev) {
2320                 if (split && !backgrounded) {
2321                         /* "Blur" the previous view. */
2322                         update_view_title(prev);
2323                 }
2324
2325                 view->parent = prev;
2326         }
2327
2328         if (view->pipe && view->lines == 0) {
2329                 /* Clear the old view and let the incremental updating refill
2330                  * the screen. */
2331                 wclear(view->win);
2332                 report("");
2333         } else {
2334                 redraw_view(view);
2335                 report("");
2336         }
2337
2338         /* If the view is backgrounded the above calls to report()
2339          * won't redraw the view title. */
2340         if (backgrounded)
2341                 update_view_title(view);
2342 }
2343
2344 static void
2345 open_external_viewer(const char *cmd)
2346 {
2347         def_prog_mode();           /* save current tty modes */
2348         endwin();                  /* restore original tty modes */
2349         system(cmd);
2350         fprintf(stderr, "Press Enter to continue");
2351         getc(stdin);
2352         reset_prog_mode();
2353         redraw_display();
2354 }
2355
2356 static void
2357 open_mergetool(const char *file)
2358 {
2359         char cmd[SIZEOF_STR];
2360         char file_sq[SIZEOF_STR];
2361
2362         if (sq_quote(file_sq, 0, file) < sizeof(file_sq) &&
2363             string_format(cmd, "git mergetool %s", file_sq)) {
2364                 open_external_viewer(cmd);
2365         }
2366 }
2367
2368 static void
2369 open_editor(bool from_root, const char *file)
2370 {
2371         char cmd[SIZEOF_STR];
2372         char file_sq[SIZEOF_STR];
2373         char *editor;
2374         char *prefix = from_root ? opt_cdup : "";
2375
2376         editor = getenv("GIT_EDITOR");
2377         if (!editor && *opt_editor)
2378                 editor = opt_editor;
2379         if (!editor)
2380                 editor = getenv("VISUAL");
2381         if (!editor)
2382                 editor = getenv("EDITOR");
2383         if (!editor)
2384                 editor = "vi";
2385
2386         if (sq_quote(file_sq, 0, file) < sizeof(file_sq) &&
2387             string_format(cmd, "%s %s%s", editor, prefix, file_sq)) {
2388                 open_external_viewer(cmd);
2389         }
2390 }
2391
2392 static void
2393 open_run_request(enum request request)
2394 {
2395         struct run_request *req = get_run_request(request);
2396         char buf[SIZEOF_STR * 2];
2397         size_t bufpos;
2398         char *cmd;
2399
2400         if (!req) {
2401                 report("Unknown run request");
2402                 return;
2403         }
2404
2405         bufpos = 0;
2406         cmd = req->cmd;
2407
2408         while (cmd) {
2409                 char *next = strstr(cmd, "%(");
2410                 int len = next - cmd;
2411                 char *value;
2412
2413                 if (!next) {
2414                         len = strlen(cmd);
2415                         value = "";
2416
2417                 } else if (!strncmp(next, "%(head)", 7)) {
2418                         value = ref_head;
2419
2420                 } else if (!strncmp(next, "%(commit)", 9)) {
2421                         value = ref_commit;
2422
2423                 } else if (!strncmp(next, "%(blob)", 7)) {
2424                         value = ref_blob;
2425
2426                 } else {
2427                         report("Unknown replacement in run request: `%s`", req->cmd);
2428                         return;
2429                 }
2430
2431                 if (!string_format_from(buf, &bufpos, "%.*s%s", len, cmd, value))
2432                         return;
2433
2434                 if (next)
2435                         next = strchr(next, ')') + 1;
2436                 cmd = next;
2437         }
2438
2439         open_external_viewer(buf);
2440 }
2441
2442 /*
2443  * User request switch noodle
2444  */
2445
2446 static int
2447 view_driver(struct view *view, enum request request)
2448 {
2449         int i;
2450
2451         if (request == REQ_NONE) {
2452                 doupdate();
2453                 return TRUE;
2454         }
2455
2456         if (request > REQ_NONE) {
2457                 open_run_request(request);
2458                 return TRUE;
2459         }
2460
2461         if (view && view->lines) {
2462                 request = view->ops->request(view, request, &view->line[view->lineno]);
2463                 if (request == REQ_NONE)
2464                         return TRUE;
2465         }
2466
2467         switch (request) {
2468         case REQ_MOVE_UP:
2469         case REQ_MOVE_DOWN:
2470         case REQ_MOVE_PAGE_UP:
2471         case REQ_MOVE_PAGE_DOWN:
2472         case REQ_MOVE_FIRST_LINE:
2473         case REQ_MOVE_LAST_LINE:
2474                 move_view(view, request);
2475                 break;
2476
2477         case REQ_SCROLL_LINE_DOWN:
2478         case REQ_SCROLL_LINE_UP:
2479         case REQ_SCROLL_PAGE_DOWN:
2480         case REQ_SCROLL_PAGE_UP:
2481                 scroll_view(view, request);
2482                 break;
2483
2484         case REQ_VIEW_BLOB:
2485                 if (!ref_blob[0]) {
2486                         report("No file chosen, press %s to open tree view",
2487                                get_key(REQ_VIEW_TREE));
2488                         break;
2489                 }
2490                 open_view(view, request, OPEN_DEFAULT);
2491                 break;
2492
2493         case REQ_VIEW_PAGER:
2494                 if (!opt_pipe && !VIEW(REQ_VIEW_PAGER)->lines) {
2495                         report("No pager content, press %s to run command from prompt",
2496                                get_key(REQ_PROMPT));
2497                         break;
2498                 }
2499                 open_view(view, request, OPEN_DEFAULT);
2500                 break;
2501
2502         case REQ_VIEW_STAGE:
2503                 if (!VIEW(REQ_VIEW_STAGE)->lines) {
2504                         report("No stage content, press %s to open the status view and choose file",
2505                                get_key(REQ_VIEW_STATUS));
2506                         break;
2507                 }
2508                 open_view(view, request, OPEN_DEFAULT);
2509                 break;
2510
2511         case REQ_VIEW_STATUS:
2512                 if (opt_is_inside_work_tree == FALSE) {
2513                         report("The status view requires a working tree");
2514                         break;
2515                 }
2516                 open_view(view, request, OPEN_DEFAULT);
2517                 break;
2518
2519         case REQ_VIEW_MAIN:
2520         case REQ_VIEW_DIFF:
2521         case REQ_VIEW_LOG:
2522         case REQ_VIEW_TREE:
2523         case REQ_VIEW_HELP:
2524                 open_view(view, request, OPEN_DEFAULT);
2525                 break;
2526
2527         case REQ_NEXT:
2528         case REQ_PREVIOUS:
2529                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
2530
2531                 if ((view == VIEW(REQ_VIEW_DIFF) &&
2532                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
2533                    (view == VIEW(REQ_VIEW_STAGE) &&
2534                      view->parent == VIEW(REQ_VIEW_STATUS)) ||
2535                    (view == VIEW(REQ_VIEW_BLOB) &&
2536                      view->parent == VIEW(REQ_VIEW_TREE))) {
2537                         int line;
2538
2539                         view = view->parent;
2540                         line = view->lineno;
2541                         move_view(view, request);
2542                         if (view_is_displayed(view))
2543                                 update_view_title(view);
2544                         if (line != view->lineno)
2545                                 view->ops->request(view, REQ_ENTER,
2546                                                    &view->line[view->lineno]);
2547
2548                 } else {
2549                         move_view(view, request);
2550                 }
2551                 break;
2552
2553         case REQ_VIEW_NEXT:
2554         {
2555                 int nviews = displayed_views();
2556                 int next_view = (current_view + 1) % nviews;
2557
2558                 if (next_view == current_view) {
2559                         report("Only one view is displayed");
2560                         break;
2561                 }
2562
2563                 current_view = next_view;
2564                 /* Blur out the title of the previous view. */
2565                 update_view_title(view);
2566                 report("");
2567                 break;
2568         }
2569         case REQ_REFRESH:
2570                 report("Refreshing is not yet supported for the %s view", view->name);
2571                 break;
2572
2573         case REQ_TOGGLE_LINENO:
2574                 opt_line_number = !opt_line_number;
2575                 redraw_display();
2576                 break;
2577
2578         case REQ_TOGGLE_DATE:
2579                 opt_date = !opt_date;
2580                 redraw_display();
2581                 break;
2582
2583         case REQ_TOGGLE_AUTHOR:
2584                 opt_author = !opt_author;
2585                 redraw_display();
2586                 break;
2587
2588         case REQ_TOGGLE_REV_GRAPH:
2589                 opt_rev_graph = !opt_rev_graph;
2590                 redraw_display();
2591                 break;
2592
2593         case REQ_TOGGLE_REFS:
2594                 opt_show_refs = !opt_show_refs;
2595                 redraw_display();
2596                 break;
2597
2598         case REQ_PROMPT:
2599                 /* Always reload^Wrerun commands from the prompt. */
2600                 open_view(view, opt_request, OPEN_RELOAD);
2601                 break;
2602
2603         case REQ_SEARCH:
2604         case REQ_SEARCH_BACK:
2605                 search_view(view, request);
2606                 break;
2607
2608         case REQ_FIND_NEXT:
2609         case REQ_FIND_PREV:
2610                 find_next(view, request);
2611                 break;
2612
2613         case REQ_STOP_LOADING:
2614                 for (i = 0; i < ARRAY_SIZE(views); i++) {
2615                         view = &views[i];
2616                         if (view->pipe)
2617                                 report("Stopped loading the %s view", view->name),
2618                         end_update(view);
2619                 }
2620                 break;
2621
2622         case REQ_SHOW_VERSION:
2623                 report("tig-%s (built %s)", TIG_VERSION, __DATE__);
2624                 return TRUE;
2625
2626         case REQ_SCREEN_RESIZE:
2627                 resize_display();
2628                 /* Fall-through */
2629         case REQ_SCREEN_REDRAW:
2630                 redraw_display();
2631                 break;
2632
2633         case REQ_EDIT:
2634                 report("Nothing to edit");
2635                 break;
2636
2637
2638         case REQ_ENTER:
2639                 report("Nothing to enter");
2640                 break;
2641
2642
2643         case REQ_VIEW_CLOSE:
2644                 /* XXX: Mark closed views by letting view->parent point to the
2645                  * view itself. Parents to closed view should never be
2646                  * followed. */
2647                 if (view->parent &&
2648                     view->parent->parent != view->parent) {
2649                         memset(display, 0, sizeof(display));
2650                         current_view = 0;
2651                         display[current_view] = view->parent;
2652                         view->parent = view;
2653                         resize_display();
2654                         redraw_display();
2655                         break;
2656                 }
2657                 /* Fall-through */
2658         case REQ_QUIT:
2659                 return FALSE;
2660
2661         default:
2662                 /* An unknown key will show most commonly used commands. */
2663                 report("Unknown key, press 'h' for help");
2664                 return TRUE;
2665         }
2666
2667         return TRUE;
2668 }
2669
2670
2671 /*
2672  * Pager backend
2673  */
2674
2675 static bool
2676 pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2677 {
2678         char *text = line->data;
2679         enum line_type type = line->type;
2680         int attr;
2681
2682         wmove(view->win, lineno, 0);
2683
2684         if (selected) {
2685                 type = LINE_CURSOR;
2686                 wchgat(view->win, -1, 0, type, NULL);
2687         }
2688
2689         attr = get_line_attr(type);
2690         wattrset(view->win, attr);
2691
2692         if (opt_line_number || opt_tab_size < TABSIZE) {
2693                 static char spaces[] = "                    ";
2694                 int col_offset = 0, col = 0;
2695
2696                 if (opt_line_number) {
2697                         unsigned long real_lineno = view->offset + lineno + 1;
2698
2699                         if (real_lineno == 1 ||
2700                             (real_lineno % opt_num_interval) == 0) {
2701                                 wprintw(view->win, "%.*d", view->digits, real_lineno);
2702
2703                         } else {
2704                                 waddnstr(view->win, spaces,
2705                                          MIN(view->digits, STRING_SIZE(spaces)));
2706                         }
2707                         waddstr(view->win, ": ");
2708                         col_offset = view->digits + 2;
2709                 }
2710
2711                 while (text && col_offset + col < view->width) {
2712                         int cols_max = view->width - col_offset - col;
2713                         char *pos = text;
2714                         int cols;
2715
2716                         if (*text == '\t') {
2717                                 text++;
2718                                 assert(sizeof(spaces) > TABSIZE);
2719                                 pos = spaces;
2720                                 cols = opt_tab_size - (col % opt_tab_size);
2721
2722                         } else {
2723                                 text = strchr(text, '\t');
2724                                 cols = line ? text - pos : strlen(pos);
2725                         }
2726
2727                         waddnstr(view->win, pos, MIN(cols, cols_max));
2728                         col += cols;
2729                 }
2730
2731         } else {
2732                 int tilde_attr = get_line_attr(LINE_MAIN_DELIM);
2733
2734                 draw_text(view, text, view->width, 0, TRUE, tilde_attr);
2735         }
2736
2737         return TRUE;
2738 }
2739
2740 static bool
2741 add_describe_ref(char *buf, size_t *bufpos, char *commit_id, const char *sep)
2742 {
2743         char refbuf[SIZEOF_STR];
2744         char *ref = NULL;
2745         FILE *pipe;
2746
2747         if (!string_format(refbuf, "git describe %s 2>/dev/null", commit_id))
2748                 return TRUE;
2749
2750         pipe = popen(refbuf, "r");
2751         if (!pipe)
2752                 return TRUE;
2753
2754         if ((ref = fgets(refbuf, sizeof(refbuf), pipe)))
2755                 ref = chomp_string(ref);
2756         pclose(pipe);
2757
2758         if (!ref || !*ref)
2759                 return TRUE;
2760
2761         /* This is the only fatal call, since it can "corrupt" the buffer. */
2762         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
2763                 return FALSE;
2764
2765         return TRUE;
2766 }
2767
2768 static void
2769 add_pager_refs(struct view *view, struct line *line)
2770 {
2771         char buf[SIZEOF_STR];
2772         char *commit_id = (char *)line->data + STRING_SIZE("commit ");
2773         struct ref **refs;
2774         size_t bufpos = 0, refpos = 0;
2775         const char *sep = "Refs: ";
2776         bool is_tag = FALSE;
2777
2778         assert(line->type == LINE_COMMIT);
2779
2780         refs = get_refs(commit_id);
2781         if (!refs) {
2782                 if (view == VIEW(REQ_VIEW_DIFF))
2783                         goto try_add_describe_ref;
2784                 return;
2785         }
2786
2787         do {
2788                 struct ref *ref = refs[refpos];
2789                 char *fmt = ref->tag    ? "%s[%s]" :
2790                             ref->remote ? "%s<%s>" : "%s%s";
2791
2792                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
2793                         return;
2794                 sep = ", ";
2795                 if (ref->tag)
2796                         is_tag = TRUE;
2797         } while (refs[refpos++]->next);
2798
2799         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
2800 try_add_describe_ref:
2801                 /* Add <tag>-g<commit_id> "fake" reference. */
2802                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
2803                         return;
2804         }
2805
2806         if (bufpos == 0)
2807                 return;
2808
2809         if (!realloc_lines(view, view->line_size + 1))
2810                 return;
2811
2812         add_line_text(view, buf, LINE_PP_REFS);
2813 }
2814
2815 static bool
2816 pager_read(struct view *view, char *data)
2817 {
2818         struct line *line;
2819
2820         if (!data)
2821                 return TRUE;
2822
2823         line = add_line_text(view, data, get_line_type(data));
2824         if (!line)
2825                 return FALSE;
2826
2827         if (line->type == LINE_COMMIT &&
2828             (view == VIEW(REQ_VIEW_DIFF) ||
2829              view == VIEW(REQ_VIEW_LOG)))
2830                 add_pager_refs(view, line);
2831
2832         return TRUE;
2833 }
2834
2835 static enum request
2836 pager_request(struct view *view, enum request request, struct line *line)
2837 {
2838         int split = 0;
2839
2840         if (request != REQ_ENTER)
2841                 return request;
2842
2843         if (line->type == LINE_COMMIT &&
2844            (view == VIEW(REQ_VIEW_LOG) ||
2845             view == VIEW(REQ_VIEW_PAGER))) {
2846                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
2847                 split = 1;
2848         }
2849
2850         /* Always scroll the view even if it was split. That way
2851          * you can use Enter to scroll through the log view and
2852          * split open each commit diff. */
2853         scroll_view(view, REQ_SCROLL_LINE_DOWN);
2854
2855         /* FIXME: A minor workaround. Scrolling the view will call report("")
2856          * but if we are scrolling a non-current view this won't properly
2857          * update the view title. */
2858         if (split)
2859                 update_view_title(view);
2860
2861         return REQ_NONE;
2862 }
2863
2864 static bool
2865 pager_grep(struct view *view, struct line *line)
2866 {
2867         regmatch_t pmatch;
2868         char *text = line->data;
2869
2870         if (!*text)
2871                 return FALSE;
2872
2873         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
2874                 return FALSE;
2875
2876         return TRUE;
2877 }
2878
2879 static void
2880 pager_select(struct view *view, struct line *line)
2881 {
2882         if (line->type == LINE_COMMIT) {
2883                 char *text = (char *)line->data + STRING_SIZE("commit ");
2884
2885                 if (view != VIEW(REQ_VIEW_PAGER))
2886                         string_copy_rev(view->ref, text);
2887                 string_copy_rev(ref_commit, text);
2888         }
2889 }
2890
2891 static struct view_ops pager_ops = {
2892         "line",
2893         NULL,
2894         pager_read,
2895         pager_draw,
2896         pager_request,
2897         pager_grep,
2898         pager_select,
2899 };
2900
2901
2902 /*
2903  * Help backend
2904  */
2905
2906 static bool
2907 help_open(struct view *view)
2908 {
2909         char buf[BUFSIZ];
2910         int lines = ARRAY_SIZE(req_info) + 2;
2911         int i;
2912
2913         if (view->lines > 0)
2914                 return TRUE;
2915
2916         for (i = 0; i < ARRAY_SIZE(req_info); i++)
2917                 if (!req_info[i].request)
2918                         lines++;
2919
2920         lines += run_requests + 1;
2921
2922         view->line = calloc(lines, sizeof(*view->line));
2923         if (!view->line)
2924                 return FALSE;
2925
2926         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
2927
2928         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
2929                 char *key;
2930
2931                 if (req_info[i].request == REQ_NONE)
2932                         continue;
2933
2934                 if (!req_info[i].request) {
2935                         add_line_text(view, "", LINE_DEFAULT);
2936                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
2937                         continue;
2938                 }
2939
2940                 key = get_key(req_info[i].request);
2941                 if (!*key)
2942                         key = "(no key defined)";
2943
2944                 if (!string_format(buf, "    %-25s %s", key, req_info[i].help))
2945                         continue;
2946
2947                 add_line_text(view, buf, LINE_DEFAULT);
2948         }
2949
2950         if (run_requests) {
2951                 add_line_text(view, "", LINE_DEFAULT);
2952                 add_line_text(view, "External commands:", LINE_DEFAULT);
2953         }
2954
2955         for (i = 0; i < run_requests; i++) {
2956                 struct run_request *req = get_run_request(REQ_NONE + i + 1);
2957                 char *key;
2958
2959                 if (!req)
2960                         continue;
2961
2962                 key = get_key_name(req->key);
2963                 if (!*key)
2964                         key = "(no key defined)";
2965
2966                 if (!string_format(buf, "    %-10s %-14s `%s`",
2967                                    keymap_table[req->keymap].name,
2968                                    key, req->cmd))
2969                         continue;
2970
2971                 add_line_text(view, buf, LINE_DEFAULT);
2972         }
2973
2974         return TRUE;
2975 }
2976
2977 static struct view_ops help_ops = {
2978         "line",
2979         help_open,
2980         NULL,
2981         pager_draw,
2982         pager_request,
2983         pager_grep,
2984         pager_select,
2985 };
2986
2987
2988 /*
2989  * Tree backend
2990  */
2991
2992 struct tree_stack_entry {
2993         struct tree_stack_entry *prev;  /* Entry below this in the stack */
2994         unsigned long lineno;           /* Line number to restore */
2995         char *name;                     /* Position of name in opt_path */
2996 };
2997
2998 /* The top of the path stack. */
2999 static struct tree_stack_entry *tree_stack = NULL;
3000 unsigned long tree_lineno = 0;
3001
3002 static void
3003 pop_tree_stack_entry(void)
3004 {
3005         struct tree_stack_entry *entry = tree_stack;
3006
3007         tree_lineno = entry->lineno;
3008         entry->name[0] = 0;
3009         tree_stack = entry->prev;
3010         free(entry);
3011 }
3012
3013 static void
3014 push_tree_stack_entry(char *name, unsigned long lineno)
3015 {
3016         struct tree_stack_entry *entry = calloc(1, sizeof(*entry));
3017         size_t pathlen = strlen(opt_path);
3018
3019         if (!entry)
3020                 return;
3021
3022         entry->prev = tree_stack;
3023         entry->name = opt_path + pathlen;
3024         tree_stack = entry;
3025
3026         if (!string_format_from(opt_path, &pathlen, "%s/", name)) {
3027                 pop_tree_stack_entry();
3028                 return;
3029         }
3030
3031         /* Move the current line to the first tree entry. */
3032         tree_lineno = 1;
3033         entry->lineno = lineno;
3034 }
3035
3036 /* Parse output from git-ls-tree(1):
3037  *
3038  * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile
3039  * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README
3040  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
3041  * 100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38 web.conf
3042  */
3043
3044 #define SIZEOF_TREE_ATTR \
3045         STRING_SIZE("100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38\t")
3046
3047 #define TREE_UP_FORMAT "040000 tree %s\t.."
3048
3049 static int
3050 tree_compare_entry(enum line_type type1, char *name1,
3051                    enum line_type type2, char *name2)
3052 {
3053         if (type1 != type2) {
3054                 if (type1 == LINE_TREE_DIR)
3055                         return -1;
3056                 return 1;
3057         }
3058
3059         return strcmp(name1, name2);
3060 }
3061
3062 static bool
3063 tree_read(struct view *view, char *text)
3064 {
3065         size_t textlen = text ? strlen(text) : 0;
3066         char buf[SIZEOF_STR];
3067         unsigned long pos;
3068         enum line_type type;
3069         bool first_read = view->lines == 0;
3070
3071         if (textlen <= SIZEOF_TREE_ATTR)
3072                 return FALSE;
3073
3074         type = text[STRING_SIZE("100644 ")] == 't'
3075              ? LINE_TREE_DIR : LINE_TREE_FILE;
3076
3077         if (first_read) {
3078                 /* Add path info line */
3079                 if (!string_format(buf, "Directory path /%s", opt_path) ||
3080                     !realloc_lines(view, view->line_size + 1) ||
3081                     !add_line_text(view, buf, LINE_DEFAULT))
3082                         return FALSE;
3083
3084                 /* Insert "link" to parent directory. */
3085                 if (*opt_path) {
3086                         if (!string_format(buf, TREE_UP_FORMAT, view->ref) ||
3087                             !realloc_lines(view, view->line_size + 1) ||
3088                             !add_line_text(view, buf, LINE_TREE_DIR))
3089                                 return FALSE;
3090                 }
3091         }
3092
3093         /* Strip the path part ... */
3094         if (*opt_path) {
3095                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
3096                 size_t striplen = strlen(opt_path);
3097                 char *path = text + SIZEOF_TREE_ATTR;
3098
3099                 if (pathlen > striplen)
3100                         memmove(path, path + striplen,
3101                                 pathlen - striplen + 1);
3102         }
3103
3104         /* Skip "Directory ..." and ".." line. */
3105         for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
3106                 struct line *line = &view->line[pos];
3107                 char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR;
3108                 char *path2 = text + SIZEOF_TREE_ATTR;
3109                 int cmp = tree_compare_entry(line->type, path1, type, path2);
3110
3111                 if (cmp <= 0)
3112                         continue;
3113
3114                 text = strdup(text);
3115                 if (!text)
3116                         return FALSE;
3117
3118                 if (view->lines > pos)
3119                         memmove(&view->line[pos + 1], &view->line[pos],
3120                                 (view->lines - pos) * sizeof(*line));
3121
3122                 line = &view->line[pos];
3123                 line->data = text;
3124                 line->type = type;
3125                 view->lines++;
3126                 return TRUE;
3127         }
3128
3129         if (!add_line_text(view, text, type))
3130                 return FALSE;
3131
3132         if (tree_lineno > view->lineno) {
3133                 view->lineno = tree_lineno;
3134                 tree_lineno = 0;
3135         }
3136
3137         return TRUE;
3138 }
3139
3140 static enum request
3141 tree_request(struct view *view, enum request request, struct line *line)
3142 {
3143         enum open_flags flags;
3144
3145         if (request == REQ_TREE_PARENT) {
3146                 if (*opt_path) {
3147                         /* fake 'cd  ..' */
3148                         request = REQ_ENTER;
3149                         line = &view->line[1];
3150                 } else {
3151                         /* quit view if at top of tree */
3152                         return REQ_VIEW_CLOSE;
3153                 }
3154         }
3155         if (request != REQ_ENTER)
3156                 return request;
3157
3158         /* Cleanup the stack if the tree view is at a different tree. */
3159         while (!*opt_path && tree_stack)
3160                 pop_tree_stack_entry();
3161
3162         switch (line->type) {
3163         case LINE_TREE_DIR:
3164                 /* Depending on whether it is a subdir or parent (updir?) link
3165                  * mangle the path buffer. */
3166                 if (line == &view->line[1] && *opt_path) {
3167                         pop_tree_stack_entry();
3168
3169                 } else {
3170                         char *data = line->data;
3171                         char *basename = data + SIZEOF_TREE_ATTR;
3172
3173                         push_tree_stack_entry(basename, view->lineno);
3174                 }
3175
3176                 /* Trees and subtrees share the same ID, so they are not not
3177                  * unique like blobs. */
3178                 flags = OPEN_RELOAD;
3179                 request = REQ_VIEW_TREE;
3180                 break;
3181
3182         case LINE_TREE_FILE:
3183                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3184                 request = REQ_VIEW_BLOB;
3185                 break;
3186
3187         default:
3188                 return TRUE;
3189         }
3190
3191         open_view(view, request, flags);
3192         if (request == REQ_VIEW_TREE) {
3193                 view->lineno = tree_lineno;
3194         }
3195
3196         return REQ_NONE;
3197 }
3198
3199 static void
3200 tree_select(struct view *view, struct line *line)
3201 {
3202         char *text = (char *)line->data + STRING_SIZE("100644 blob ");
3203
3204         if (line->type == LINE_TREE_FILE) {
3205                 string_copy_rev(ref_blob, text);
3206
3207         } else if (line->type != LINE_TREE_DIR) {
3208                 return;
3209         }
3210
3211         string_copy_rev(view->ref, text);
3212 }
3213
3214 static struct view_ops tree_ops = {
3215         "file",
3216         NULL,
3217         tree_read,
3218         pager_draw,
3219         tree_request,
3220         pager_grep,
3221         tree_select,
3222 };
3223
3224 static bool
3225 blob_read(struct view *view, char *line)
3226 {
3227         return add_line_text(view, line, LINE_DEFAULT) != NULL;
3228 }
3229
3230 static struct view_ops blob_ops = {
3231         "line",
3232         NULL,
3233         blob_read,
3234         pager_draw,
3235         pager_request,
3236         pager_grep,
3237         pager_select,
3238 };
3239
3240
3241 /*
3242  * Status backend
3243  */
3244
3245 struct status {
3246         char status;
3247         struct {
3248                 mode_t mode;
3249                 char rev[SIZEOF_REV];
3250                 char name[SIZEOF_STR];
3251         } old;
3252         struct {
3253                 mode_t mode;
3254                 char rev[SIZEOF_REV];
3255                 char name[SIZEOF_STR];
3256         } new;
3257 };
3258
3259 static struct status stage_status;
3260 static enum line_type stage_line_type;
3261
3262 /* Get fields from the diff line:
3263  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
3264  */
3265 static inline bool
3266 status_get_diff(struct status *file, char *buf, size_t bufsize)
3267 {
3268         char *old_mode = buf +  1;
3269         char *new_mode = buf +  8;
3270         char *old_rev  = buf + 15;
3271         char *new_rev  = buf + 56;
3272         char *status   = buf + 97;
3273
3274         if (bufsize < 99 ||
3275             old_mode[-1] != ':' ||
3276             new_mode[-1] != ' ' ||
3277             old_rev[-1]  != ' ' ||
3278             new_rev[-1]  != ' ' ||
3279             status[-1]   != ' ')
3280                 return FALSE;
3281
3282         file->status = *status;
3283
3284         string_copy_rev(file->old.rev, old_rev);
3285         string_copy_rev(file->new.rev, new_rev);
3286
3287         file->old.mode = strtoul(old_mode, NULL, 8);
3288         file->new.mode = strtoul(new_mode, NULL, 8);
3289
3290         file->old.name[0] = file->new.name[0] = 0;
3291
3292         return TRUE;
3293 }
3294
3295 static bool
3296 status_run(struct view *view, const char cmd[], bool diff, enum line_type type)
3297 {
3298         struct status *file = NULL;
3299         struct status *unmerged = NULL;
3300         char buf[SIZEOF_STR * 4];
3301         size_t bufsize = 0;
3302         FILE *pipe;
3303
3304         pipe = popen(cmd, "r");
3305         if (!pipe)
3306                 return FALSE;
3307
3308         add_line_data(view, NULL, type);
3309
3310         while (!feof(pipe) && !ferror(pipe)) {
3311                 char *sep;
3312                 size_t readsize;
3313
3314                 readsize = fread(buf + bufsize, 1, sizeof(buf) - bufsize, pipe);
3315                 if (!readsize)
3316                         break;
3317                 bufsize += readsize;
3318
3319                 /* Process while we have NUL chars. */
3320                 while ((sep = memchr(buf, 0, bufsize))) {
3321                         size_t sepsize = sep - buf + 1;
3322
3323                         if (!file) {
3324                                 if (!realloc_lines(view, view->line_size + 1))
3325                                         goto error_out;
3326
3327                                 file = calloc(1, sizeof(*file));
3328                                 if (!file)
3329                                         goto error_out;
3330
3331                                 add_line_data(view, file, type);
3332                         }
3333
3334                         /* Parse diff info part. */
3335                         if (!diff) {
3336                                 file->status = '?';
3337
3338                         } else if (!file->status) {
3339                                 if (!status_get_diff(file, buf, sepsize))
3340                                         goto error_out;
3341
3342                                 bufsize -= sepsize;
3343                                 memmove(buf, sep + 1, bufsize);
3344
3345                                 sep = memchr(buf, 0, bufsize);
3346                                 if (!sep)
3347                                         break;
3348                                 sepsize = sep - buf + 1;
3349
3350                                 /* Collapse all 'M'odified entries that
3351                                  * follow a associated 'U'nmerged entry.
3352                                  */
3353                                 if (file->status == 'U') {
3354                                         unmerged = file;
3355
3356                                 } else if (unmerged) {
3357                                         int collapse = !strcmp(buf, unmerged->new.name);
3358
3359                                         unmerged = NULL;
3360                                         if (collapse) {
3361                                                 free(file);
3362                                                 view->lines--;
3363                                                 continue;
3364                                         }
3365                                 }
3366                         }
3367
3368                         /* Grab the old name for rename/copy. */
3369                         if (!*file->old.name &&
3370                             (file->status == 'R' || file->status == 'C')) {
3371                                 sepsize = sep - buf + 1;
3372                                 string_ncopy(file->old.name, buf, sepsize);
3373                                 bufsize -= sepsize;
3374                                 memmove(buf, sep + 1, bufsize);
3375
3376                                 sep = memchr(buf, 0, bufsize);
3377                                 if (!sep)
3378                                         break;
3379                                 sepsize = sep - buf + 1;
3380                         }
3381
3382                         /* git-ls-files just delivers a NUL separated
3383                          * list of file names similar to the second half
3384                          * of the git-diff-* output. */
3385                         string_ncopy(file->new.name, buf, sepsize);
3386                         if (!*file->old.name)
3387                                 string_copy(file->old.name, file->new.name);
3388                         bufsize -= sepsize;
3389                         memmove(buf, sep + 1, bufsize);
3390                         file = NULL;
3391                 }
3392         }
3393
3394         if (ferror(pipe)) {
3395 error_out:
3396                 pclose(pipe);
3397                 return FALSE;
3398         }
3399
3400         if (!view->line[view->lines - 1].data)
3401                 add_line_data(view, NULL, LINE_STAT_NONE);
3402
3403         pclose(pipe);
3404         return TRUE;
3405 }
3406
3407 /* Don't show unmerged entries in the staged section. */
3408 #define STATUS_DIFF_INDEX_CMD "git diff-index -z --diff-filter=ACDMRTXB --cached -M HEAD"
3409 #define STATUS_DIFF_FILES_CMD "git diff-files -z"
3410 #define STATUS_LIST_OTHER_CMD \
3411         "git ls-files -z --others --exclude-per-directory=.gitignore"
3412
3413 #define STATUS_DIFF_INDEX_SHOW_CMD \
3414         "git diff-index --root --patch-with-stat -C -M --cached HEAD -- %s %s 2>/dev/null"
3415
3416 #define STATUS_DIFF_FILES_SHOW_CMD \
3417         "git diff-files --root --patch-with-stat -C -M -- %s %s 2>/dev/null"
3418
3419 /* First parse staged info using git-diff-index(1), then parse unstaged
3420  * info using git-diff-files(1), and finally untracked files using
3421  * git-ls-files(1). */
3422 static bool
3423 status_open(struct view *view)
3424 {
3425         struct stat statbuf;
3426         char exclude[SIZEOF_STR];
3427         char cmd[SIZEOF_STR];
3428         unsigned long prev_lineno = view->lineno;
3429         size_t i;
3430
3431         for (i = 0; i < view->lines; i++)
3432                 free(view->line[i].data);
3433         free(view->line);
3434         view->lines = view->line_size = view->lineno = 0;
3435         view->line = NULL;
3436
3437         if (!realloc_lines(view, view->line_size + 6))
3438                 return FALSE;
3439
3440         if (!string_format(exclude, "%s/info/exclude", opt_git_dir))
3441                 return FALSE;
3442
3443         string_copy(cmd, STATUS_LIST_OTHER_CMD);
3444
3445         if (stat(exclude, &statbuf) >= 0) {
3446                 size_t cmdsize = strlen(cmd);
3447
3448                 if (!string_format_from(cmd, &cmdsize, " %s", "--exclude-from=") ||
3449                     sq_quote(cmd, cmdsize, exclude) >= sizeof(cmd))
3450                         return FALSE;
3451         }
3452
3453         system("git update-index -q --refresh");
3454
3455         if (!status_run(view, STATUS_DIFF_INDEX_CMD, TRUE, LINE_STAT_STAGED) ||
3456             !status_run(view, STATUS_DIFF_FILES_CMD, TRUE, LINE_STAT_UNSTAGED) ||
3457             !status_run(view, cmd, FALSE, LINE_STAT_UNTRACKED))
3458                 return FALSE;
3459
3460         /* If all went well restore the previous line number to stay in
3461          * the context. */
3462         if (prev_lineno < view->lines)
3463                 view->lineno = prev_lineno;
3464         else
3465                 view->lineno = view->lines - 1;
3466
3467         return TRUE;
3468 }
3469
3470 static bool
3471 status_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
3472 {
3473         struct status *status = line->data;
3474         int tilde_attr = get_line_attr(LINE_MAIN_DELIM);
3475
3476         wmove(view->win, lineno, 0);
3477
3478         if (selected) {
3479                 wattrset(view->win, get_line_attr(LINE_CURSOR));
3480                 wchgat(view->win, -1, 0, LINE_CURSOR, NULL);
3481                 tilde_attr = -1;
3482
3483         } else if (!status && line->type != LINE_STAT_NONE) {
3484                 wattrset(view->win, get_line_attr(LINE_STAT_SECTION));
3485                 wchgat(view->win, -1, 0, LINE_STAT_SECTION, NULL);
3486
3487         } else {
3488                 wattrset(view->win, get_line_attr(line->type));
3489         }
3490
3491         if (!status) {
3492                 char *text;
3493
3494                 switch (line->type) {
3495                 case LINE_STAT_STAGED:
3496                         text = "Changes to be committed:";
3497                         break;
3498
3499                 case LINE_STAT_UNSTAGED:
3500                         text = "Changed but not updated:";
3501                         break;
3502
3503                 case LINE_STAT_UNTRACKED:
3504                         text = "Untracked files:";
3505                         break;
3506
3507                 case LINE_STAT_NONE:
3508                         text = "    (no files)";
3509                         break;
3510
3511                 default:
3512                         return FALSE;
3513                 }
3514
3515                 draw_text(view, text, view->width, 0, TRUE, tilde_attr);
3516                 return TRUE;
3517         }
3518
3519         waddch(view->win, status->status);
3520         if (!selected)
3521                 wattrset(view->win, A_NORMAL);
3522         wmove(view->win, lineno, 4);
3523         if (view->width < 5)
3524                 return TRUE;
3525
3526         draw_text(view, status->new.name, view->width - 5, 5, TRUE, tilde_attr);
3527         return TRUE;
3528 }
3529
3530 static enum request
3531 status_enter(struct view *view, struct line *line)
3532 {
3533         struct status *status = line->data;
3534         char oldpath[SIZEOF_STR] = "";
3535         char newpath[SIZEOF_STR] = "";
3536         char *info;
3537         size_t cmdsize = 0;
3538
3539         if (line->type == LINE_STAT_NONE ||
3540             (!status && line[1].type == LINE_STAT_NONE)) {
3541                 report("No file to diff");
3542                 return REQ_NONE;
3543         }
3544
3545         if (status) {
3546                 if (sq_quote(oldpath, 0, status->old.name) >= sizeof(oldpath))
3547                         return REQ_QUIT;
3548                 /* Diffs for unmerged entries are empty when pasing the
3549                  * new path, so leave it empty. */
3550                 if (status->status != 'U' &&
3551                     sq_quote(newpath, 0, status->new.name) >= sizeof(newpath))
3552                         return REQ_QUIT;
3553         }
3554
3555         if (opt_cdup[0] &&
3556             line->type != LINE_STAT_UNTRACKED &&
3557             !string_format_from(opt_cmd, &cmdsize, "cd %s;", opt_cdup))
3558                 return REQ_QUIT;
3559
3560         switch (line->type) {
3561         case LINE_STAT_STAGED:
3562                 if (!string_format_from(opt_cmd, &cmdsize,
3563                                         STATUS_DIFF_INDEX_SHOW_CMD, oldpath, newpath))
3564                         return REQ_QUIT;
3565                 if (status)
3566                         info = "Staged changes to %s";
3567                 else
3568                         info = "Staged changes";
3569                 break;
3570
3571         case LINE_STAT_UNSTAGED:
3572                 if (!string_format_from(opt_cmd, &cmdsize,
3573                                         STATUS_DIFF_FILES_SHOW_CMD, oldpath, newpath))
3574                         return REQ_QUIT;
3575                 if (status)
3576                         info = "Unstaged changes to %s";
3577                 else
3578                         info = "Unstaged changes";
3579                 break;
3580
3581         case LINE_STAT_UNTRACKED:
3582                 if (opt_pipe)
3583                         return REQ_QUIT;
3584
3585
3586                 if (!status) {
3587                         report("No file to show");
3588                         return REQ_NONE;
3589                 }
3590
3591                 opt_pipe = fopen(status->new.name, "r");
3592                 info = "Untracked file %s";
3593                 break;
3594
3595         default:
3596                 die("line type %d not handled in switch", line->type);
3597         }
3598
3599         open_view(view, REQ_VIEW_STAGE, OPEN_RELOAD | OPEN_SPLIT);
3600         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
3601                 if (status) {
3602                         stage_status = *status;
3603                 } else {
3604                         memset(&stage_status, 0, sizeof(stage_status));
3605                 }
3606
3607                 stage_line_type = line->type;
3608                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
3609         }
3610
3611         return REQ_NONE;
3612 }
3613
3614
3615 static bool
3616 status_update_file(struct view *view, struct status *status, enum line_type type)
3617 {
3618         char cmd[SIZEOF_STR];
3619         char buf[SIZEOF_STR];
3620         size_t cmdsize = 0;
3621         size_t bufsize = 0;
3622         size_t written = 0;
3623         FILE *pipe;
3624
3625         if (opt_cdup[0] &&
3626             type != LINE_STAT_UNTRACKED &&
3627             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
3628                 return FALSE;
3629
3630         switch (type) {
3631         case LINE_STAT_STAGED:
3632                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
3633                                         status->old.mode,
3634                                         status->old.rev,
3635                                         status->old.name, 0))
3636                         return FALSE;
3637
3638                 string_add(cmd, cmdsize, "git update-index -z --index-info");
3639                 break;
3640
3641         case LINE_STAT_UNSTAGED:
3642         case LINE_STAT_UNTRACKED:
3643                 if (!string_format_from(buf, &bufsize, "%s%c", status->new.name, 0))
3644                         return FALSE;
3645
3646                 string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin");
3647                 break;
3648
3649         default:
3650                 die("line type %d not handled in switch", type);
3651         }
3652
3653         pipe = popen(cmd, "w");
3654         if (!pipe)
3655                 return FALSE;
3656
3657         while (!ferror(pipe) && written < bufsize) {
3658                 written += fwrite(buf + written, 1, bufsize - written, pipe);
3659         }
3660
3661         pclose(pipe);
3662
3663         if (written != bufsize)
3664                 return FALSE;
3665
3666         return TRUE;
3667 }
3668
3669 static void
3670 status_update(struct view *view)
3671 {
3672         struct line *line = &view->line[view->lineno];
3673
3674         assert(view->lines);
3675
3676         if (!line->data) {
3677                 while (++line < view->line + view->lines && line->data) {
3678                         if (!status_update_file(view, line->data, line->type))
3679                                 report("Failed to update file status");
3680                 }
3681
3682                 if (!line[-1].data) {
3683                         report("Nothing to update");
3684                         return;
3685                 }
3686
3687         } else if (!status_update_file(view, line->data, line->type)) {
3688                 report("Failed to update file status");
3689         }
3690 }
3691
3692 static enum request
3693 status_request(struct view *view, enum request request, struct line *line)
3694 {
3695         struct status *status = line->data;
3696
3697         switch (request) {
3698         case REQ_STATUS_UPDATE:
3699                 status_update(view);
3700                 break;
3701
3702         case REQ_STATUS_MERGE:
3703                 if (!status || status->status != 'U') {
3704                         report("Merging only possible for files with unmerged status ('U').");
3705                         return REQ_NONE;
3706                 }
3707                 open_mergetool(status->new.name);
3708                 break;
3709
3710         case REQ_EDIT:
3711                 if (!status)
3712                         return request;
3713
3714                 open_editor(status->status != '?', status->new.name);
3715                 break;
3716
3717         case REQ_ENTER:
3718                 /* After returning the status view has been split to
3719                  * show the stage view. No further reloading is
3720                  * necessary. */
3721                 status_enter(view, line);
3722                 return REQ_NONE;
3723
3724         case REQ_REFRESH:
3725                 /* Simply reload the view. */
3726                 break;
3727
3728         default:
3729                 return request;
3730         }
3731
3732         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
3733
3734         return REQ_NONE;
3735 }
3736
3737 static void
3738 status_select(struct view *view, struct line *line)
3739 {
3740         struct status *status = line->data;
3741         char file[SIZEOF_STR] = "all files";
3742         char *text;
3743         char *key;
3744
3745         if (status && !string_format(file, "'%s'", status->new.name))
3746                 return;
3747
3748         if (!status && line[1].type == LINE_STAT_NONE)
3749                 line++;
3750
3751         switch (line->type) {
3752         case LINE_STAT_STAGED:
3753                 text = "Press %s to unstage %s for commit";
3754                 break;
3755
3756         case LINE_STAT_UNSTAGED:
3757                 text = "Press %s to stage %s for commit";
3758                 break;
3759
3760         case LINE_STAT_UNTRACKED:
3761                 text = "Press %s to stage %s for addition";
3762                 break;
3763
3764         case LINE_STAT_NONE:
3765                 text = "Nothing to update";
3766                 break;
3767
3768         default:
3769                 die("line type %d not handled in switch", line->type);
3770         }
3771
3772         if (status && status->status == 'U') {
3773                 text = "Press %s to resolve conflict in %s";
3774                 key = get_key(REQ_STATUS_MERGE);
3775
3776         } else {
3777                 key = get_key(REQ_STATUS_UPDATE);
3778         }
3779
3780         string_format(view->ref, text, key, file);
3781 }
3782
3783 static bool
3784 status_grep(struct view *view, struct line *line)
3785 {
3786         struct status *status = line->data;
3787         enum { S_STATUS, S_NAME, S_END } state;
3788         char buf[2] = "?";
3789         regmatch_t pmatch;
3790
3791         if (!status)
3792                 return FALSE;
3793
3794         for (state = S_STATUS; state < S_END; state++) {
3795                 char *text;
3796
3797                 switch (state) {
3798                 case S_NAME:    text = status->new.name;        break;
3799                 case S_STATUS:
3800                         buf[0] = status->status;
3801                         text = buf;
3802                         break;
3803
3804                 default:
3805                         return FALSE;
3806                 }
3807
3808                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
3809                         return TRUE;
3810         }
3811
3812         return FALSE;
3813 }
3814
3815 static struct view_ops status_ops = {
3816         "file",
3817         status_open,
3818         NULL,
3819         status_draw,
3820         status_request,
3821         status_grep,
3822         status_select,
3823 };
3824
3825
3826 static bool
3827 stage_diff_line(FILE *pipe, struct line *line)
3828 {
3829         char *buf = line->data;
3830         size_t bufsize = strlen(buf);
3831         size_t written = 0;
3832
3833         while (!ferror(pipe) && written < bufsize) {
3834                 written += fwrite(buf + written, 1, bufsize - written, pipe);
3835         }
3836
3837         fputc('\n', pipe);
3838
3839         return written == bufsize;
3840 }
3841
3842 static struct line *
3843 stage_diff_hdr(struct view *view, struct line *line)
3844 {
3845         int diff_hdr_dir = line->type == LINE_DIFF_CHUNK ? -1 : 1;
3846         struct line *diff_hdr;
3847
3848         if (line->type == LINE_DIFF_CHUNK)
3849                 diff_hdr = line - 1;
3850         else
3851                 diff_hdr = view->line + 1;
3852
3853         while (diff_hdr > view->line && diff_hdr < view->line + view->lines) {
3854                 if (diff_hdr->type == LINE_DIFF_HEADER)
3855                         return diff_hdr;
3856
3857                 diff_hdr += diff_hdr_dir;
3858         }
3859
3860         return NULL;
3861 }
3862
3863 static bool
3864 stage_update_chunk(struct view *view, struct line *line)
3865 {
3866         char cmd[SIZEOF_STR];
3867         size_t cmdsize = 0;
3868         struct line *diff_hdr, *diff_chunk, *diff_end;
3869         FILE *pipe;
3870
3871         diff_hdr = stage_diff_hdr(view, line);
3872         if (!diff_hdr)
3873                 return FALSE;
3874
3875         if (opt_cdup[0] &&
3876             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
3877                 return FALSE;
3878
3879         if (!string_format_from(cmd, &cmdsize,
3880                                 "git apply --cached %s - && "
3881                                 "git update-index -q --unmerged --refresh 2>/dev/null",
3882                                 stage_line_type == LINE_STAT_STAGED ? "-R" : ""))
3883                 return FALSE;
3884
3885         pipe = popen(cmd, "w");
3886         if (!pipe)
3887                 return FALSE;
3888
3889         diff_end = view->line + view->lines;
3890         if (line->type != LINE_DIFF_CHUNK) {
3891                 diff_chunk = diff_hdr;
3892
3893         } else {
3894                 for (diff_chunk = line + 1; diff_chunk < diff_end; diff_chunk++)
3895                         if (diff_chunk->type == LINE_DIFF_CHUNK ||
3896                             diff_chunk->type == LINE_DIFF_HEADER)
3897                                 diff_end = diff_chunk;
3898
3899                 diff_chunk = line;
3900
3901                 while (diff_hdr->type != LINE_DIFF_CHUNK) {
3902                         switch (diff_hdr->type) {
3903                         case LINE_DIFF_HEADER:
3904                         case LINE_DIFF_INDEX:
3905                         case LINE_DIFF_ADD:
3906                         case LINE_DIFF_DEL:
3907                                 break;
3908
3909                         default:
3910                                 diff_hdr++;
3911                                 continue;
3912                         }
3913
3914                         if (!stage_diff_line(pipe, diff_hdr++)) {
3915                                 pclose(pipe);
3916                                 return FALSE;
3917                         }
3918                 }
3919         }
3920
3921         while (diff_chunk < diff_end && stage_diff_line(pipe, diff_chunk))
3922                 diff_chunk++;
3923
3924         pclose(pipe);
3925
3926         if (diff_chunk != diff_end)
3927                 return FALSE;
3928
3929         return TRUE;
3930 }
3931
3932 static void
3933 stage_update(struct view *view, struct line *line)
3934 {
3935         if (stage_line_type != LINE_STAT_UNTRACKED &&
3936             (line->type == LINE_DIFF_CHUNK || !stage_status.status)) {
3937                 if (!stage_update_chunk(view, line)) {
3938                         report("Failed to apply chunk");
3939                         return;
3940                 }
3941
3942         } else if (!status_update_file(view, &stage_status, stage_line_type)) {
3943                 report("Failed to update file");
3944                 return;
3945         }
3946
3947         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
3948
3949         view = VIEW(REQ_VIEW_STATUS);
3950         if (view_is_displayed(view))
3951                 status_enter(view, &view->line[view->lineno]);
3952 }
3953
3954 static enum request
3955 stage_request(struct view *view, enum request request, struct line *line)
3956 {
3957         switch (request) {
3958         case REQ_STATUS_UPDATE:
3959                 stage_update(view, line);
3960                 break;
3961
3962         case REQ_EDIT:
3963                 if (!stage_status.new.name[0])
3964                         return request;
3965
3966                 open_editor(stage_status.status != '?', stage_status.new.name);
3967                 break;
3968
3969         case REQ_ENTER:
3970                 pager_request(view, request, line);
3971                 break;
3972
3973         default:
3974                 return request;
3975         }
3976
3977         return REQ_NONE;
3978 }
3979
3980 static struct view_ops stage_ops = {
3981         "line",
3982         NULL,
3983         pager_read,
3984         pager_draw,
3985         stage_request,
3986         pager_grep,
3987         pager_select,
3988 };
3989
3990
3991 /*
3992  * Revision graph
3993  */
3994
3995 struct commit {
3996         char id[SIZEOF_REV];            /* SHA1 ID. */
3997         char title[128];                /* First line of the commit message. */
3998         char author[75];                /* Author of the commit. */
3999         struct tm time;                 /* Date from the author ident. */
4000         struct ref **refs;              /* Repository references. */
4001         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
4002         size_t graph_size;              /* The width of the graph array. */
4003 };
4004
4005 /* Size of rev graph with no  "padding" columns */
4006 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
4007
4008 struct rev_graph {
4009         struct rev_graph *prev, *next, *parents;
4010         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
4011         size_t size;
4012         struct commit *commit;
4013         size_t pos;
4014         unsigned int boundary:1;
4015 };
4016
4017 /* Parents of the commit being visualized. */
4018 static struct rev_graph graph_parents[4];
4019
4020 /* The current stack of revisions on the graph. */
4021 static struct rev_graph graph_stacks[4] = {
4022         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
4023         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
4024         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
4025         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
4026 };
4027
4028 static inline bool
4029 graph_parent_is_merge(struct rev_graph *graph)
4030 {
4031         return graph->parents->size > 1;
4032 }
4033
4034 static inline void
4035 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
4036 {
4037         struct commit *commit = graph->commit;
4038
4039         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
4040                 commit->graph[commit->graph_size++] = symbol;
4041 }
4042
4043 static void
4044 done_rev_graph(struct rev_graph *graph)
4045 {
4046         if (graph_parent_is_merge(graph) &&
4047             graph->pos < graph->size - 1 &&
4048             graph->next->size == graph->size + graph->parents->size - 1) {
4049                 size_t i = graph->pos + graph->parents->size - 1;
4050
4051                 graph->commit->graph_size = i * 2;
4052                 while (i < graph->next->size - 1) {
4053                         append_to_rev_graph(graph, ' ');
4054                         append_to_rev_graph(graph, '\\');
4055                         i++;
4056                 }
4057         }
4058
4059         graph->size = graph->pos = 0;
4060         graph->commit = NULL;
4061         memset(graph->parents, 0, sizeof(*graph->parents));
4062 }
4063
4064 static void
4065 push_rev_graph(struct rev_graph *graph, char *parent)
4066 {
4067         int i;
4068
4069         /* "Collapse" duplicate parents lines.
4070          *
4071          * FIXME: This needs to also update update the drawn graph but
4072          * for now it just serves as a method for pruning graph lines. */
4073         for (i = 0; i < graph->size; i++)
4074                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
4075                         return;
4076
4077         if (graph->size < SIZEOF_REVITEMS) {
4078                 string_copy_rev(graph->rev[graph->size++], parent);
4079         }
4080 }
4081
4082 static chtype
4083 get_rev_graph_symbol(struct rev_graph *graph)
4084 {
4085         chtype symbol;
4086
4087         if (graph->boundary)
4088                 symbol = REVGRAPH_BOUND;
4089         else if (graph->parents->size == 0)
4090                 symbol = REVGRAPH_INIT;
4091         else if (graph_parent_is_merge(graph))
4092                 symbol = REVGRAPH_MERGE;
4093         else if (graph->pos >= graph->size)
4094                 symbol = REVGRAPH_BRANCH;
4095         else
4096                 symbol = REVGRAPH_COMMIT;
4097
4098         return symbol;
4099 }
4100
4101 static void
4102 draw_rev_graph(struct rev_graph *graph)
4103 {
4104         struct rev_filler {
4105                 chtype separator, line;
4106         };
4107         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
4108         static struct rev_filler fillers[] = {
4109                 { ' ',  REVGRAPH_LINE },
4110                 { '`',  '.' },
4111                 { '\'', ' ' },
4112                 { '/',  ' ' },
4113         };
4114         chtype symbol = get_rev_graph_symbol(graph);
4115         struct rev_filler *filler;
4116         size_t i;
4117
4118         filler = &fillers[DEFAULT];
4119
4120         for (i = 0; i < graph->pos; i++) {
4121                 append_to_rev_graph(graph, filler->line);
4122                 if (graph_parent_is_merge(graph->prev) &&
4123                     graph->prev->pos == i)
4124                         filler = &fillers[RSHARP];
4125
4126                 append_to_rev_graph(graph, filler->separator);
4127         }
4128
4129         /* Place the symbol for this revision. */
4130         append_to_rev_graph(graph, symbol);
4131
4132         if (graph->prev->size > graph->size)
4133                 filler = &fillers[RDIAG];
4134         else
4135                 filler = &fillers[DEFAULT];
4136
4137         i++;
4138
4139         for (; i < graph->size; i++) {
4140                 append_to_rev_graph(graph, filler->separator);
4141                 append_to_rev_graph(graph, filler->line);
4142                 if (graph_parent_is_merge(graph->prev) &&
4143                     i < graph->prev->pos + graph->parents->size)
4144                         filler = &fillers[RSHARP];
4145                 if (graph->prev->size > graph->size)
4146                         filler = &fillers[LDIAG];
4147         }
4148
4149         if (graph->prev->size > graph->size) {
4150                 append_to_rev_graph(graph, filler->separator);
4151                 if (filler->line != ' ')
4152                         append_to_rev_graph(graph, filler->line);
4153         }
4154 }
4155
4156 /* Prepare the next rev graph */
4157 static void
4158 prepare_rev_graph(struct rev_graph *graph)
4159 {
4160         size_t i;
4161
4162         /* First, traverse all lines of revisions up to the active one. */
4163         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
4164                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
4165                         break;
4166
4167                 push_rev_graph(graph->next, graph->rev[graph->pos]);
4168         }
4169
4170         /* Interleave the new revision parent(s). */
4171         for (i = 0; !graph->boundary && i < graph->parents->size; i++)
4172                 push_rev_graph(graph->next, graph->parents->rev[i]);
4173
4174         /* Lastly, put any remaining revisions. */
4175         for (i = graph->pos + 1; i < graph->size; i++)
4176                 push_rev_graph(graph->next, graph->rev[i]);
4177 }
4178
4179 static void
4180 update_rev_graph(struct rev_graph *graph)
4181 {
4182         /* If this is the finalizing update ... */
4183         if (graph->commit)
4184                 prepare_rev_graph(graph);
4185
4186         /* Graph visualization needs a one rev look-ahead,
4187          * so the first update doesn't visualize anything. */
4188         if (!graph->prev->commit)
4189                 return;
4190
4191         draw_rev_graph(graph->prev);
4192         done_rev_graph(graph->prev->prev);
4193 }
4194
4195
4196 /*
4197  * Main view backend
4198  */
4199
4200 static bool
4201 main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
4202 {
4203         char buf[DATE_COLS + 1];
4204         struct commit *commit = line->data;
4205         enum line_type type;
4206         int col = 0;
4207         size_t timelen;
4208         int tilde_attr;
4209         int space;
4210
4211         if (!*commit->author)
4212                 return FALSE;
4213
4214         space = view->width;
4215         wmove(view->win, lineno, col);
4216
4217         if (selected) {
4218                 type = LINE_CURSOR;
4219                 wattrset(view->win, get_line_attr(type));
4220                 wchgat(view->win, -1, 0, type, NULL);
4221                 tilde_attr = -1;
4222         } else {
4223                 type = LINE_MAIN_COMMIT;
4224                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
4225                 tilde_attr = get_line_attr(LINE_MAIN_DELIM);
4226         }
4227
4228         if (opt_date) {
4229                 int n;
4230
4231                 timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
4232                 n = draw_text(
4233                         view, buf, view->width - col, col, FALSE, tilde_attr);
4234                 draw_text(
4235                         view, " ", view->width - col - n, col + n, FALSE,
4236                         tilde_attr);
4237
4238                 col += DATE_COLS;
4239                 wmove(view->win, lineno, col);
4240                 if (col >= view->width)
4241                         return TRUE;
4242         }
4243         if (type != LINE_CURSOR)
4244                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
4245
4246         if (opt_author) {
4247                 int max_len;
4248
4249                 max_len = view->width - col;
4250                 if (max_len > AUTHOR_COLS - 1)
4251                         max_len = AUTHOR_COLS - 1;
4252                 draw_text(
4253                         view, commit->author, max_len, col, TRUE, tilde_attr);
4254                 col += AUTHOR_COLS;
4255                 if (col >= view->width)
4256                         return TRUE;
4257         }
4258
4259         if (opt_rev_graph && commit->graph_size) {
4260                 size_t graph_size = view->width - col;
4261                 size_t i;
4262
4263                 if (type != LINE_CURSOR)
4264                         wattrset(view->win, get_line_attr(LINE_MAIN_REVGRAPH));
4265                 wmove(view->win, lineno, col);
4266                 if (graph_size > commit->graph_size)
4267                         graph_size = commit->graph_size;
4268                 /* Using waddch() instead of waddnstr() ensures that
4269                  * they'll be rendered correctly for the cursor line. */
4270                 for (i = 0; i < graph_size; i++)
4271                         waddch(view->win, commit->graph[i]);
4272
4273                 col += commit->graph_size + 1;
4274                 if (col >= view->width)
4275                         return TRUE;
4276                 waddch(view->win, ' ');
4277         }
4278         if (type != LINE_CURSOR)
4279                 wattrset(view->win, A_NORMAL);
4280
4281         wmove(view->win, lineno, col);
4282
4283         if (opt_show_refs && commit->refs) {
4284                 size_t i = 0;
4285
4286                 do {
4287                         if (type == LINE_CURSOR)
4288                                 ;
4289                         else if (commit->refs[i]->ltag)
4290                                 wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG));
4291                         else if (commit->refs[i]->tag)
4292                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
4293                         else if (commit->refs[i]->remote)
4294                                 wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
4295                         else
4296                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
4297
4298                         col += draw_text(
4299                                 view, "[", view->width - col, col, TRUE,
4300                                 tilde_attr);
4301                         col += draw_text(
4302                                 view, commit->refs[i]->name, view->width - col,
4303                                 col, TRUE, tilde_attr);
4304                         col += draw_text(
4305                                 view, "]", view->width - col, col, TRUE,
4306                                 tilde_attr);
4307                         if (type != LINE_CURSOR)
4308                                 wattrset(view->win, A_NORMAL);
4309                         col += draw_text(
4310                                 view, " ", view->width - col, col, TRUE,
4311                                 tilde_attr);
4312                         if (col >= view->width)
4313                                 return TRUE;
4314                 } while (commit->refs[i++]->next);
4315         }
4316
4317         if (type != LINE_CURSOR)
4318                 wattrset(view->win, get_line_attr(type));
4319
4320         col += draw_text(
4321                 view, commit->title, view->width - col, col, TRUE, tilde_attr);
4322
4323         return TRUE;
4324 }
4325
4326 /* Reads git log --pretty=raw output and parses it into the commit struct. */
4327 static bool
4328 main_read(struct view *view, char *line)
4329 {
4330         static struct rev_graph *graph = graph_stacks;
4331         enum line_type type;
4332         struct commit *commit;
4333
4334         if (!line) {
4335                 update_rev_graph(graph);
4336                 return TRUE;
4337         }
4338
4339         type = get_line_type(line);
4340         if (type == LINE_COMMIT) {
4341                 commit = calloc(1, sizeof(struct commit));
4342                 if (!commit)
4343                         return FALSE;
4344
4345                 line += STRING_SIZE("commit ");
4346                 if (*line == '-') {
4347                         graph->boundary = 1;
4348                         line++;
4349                 }
4350
4351                 string_copy_rev(commit->id, line);
4352                 commit->refs = get_refs(commit->id);
4353                 graph->commit = commit;
4354                 add_line_data(view, commit, LINE_MAIN_COMMIT);
4355                 return TRUE;
4356         }
4357
4358         if (!view->lines)
4359                 return TRUE;
4360         commit = view->line[view->lines - 1].data;
4361
4362         switch (type) {
4363         case LINE_PARENT:
4364                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
4365                 break;
4366
4367         case LINE_AUTHOR:
4368         {
4369                 /* Parse author lines where the name may be empty:
4370                  *      author  <email@address.tld> 1138474660 +0100
4371                  */
4372                 char *ident = line + STRING_SIZE("author ");
4373                 char *nameend = strchr(ident, '<');
4374                 char *emailend = strchr(ident, '>');
4375
4376                 if (!nameend || !emailend)
4377                         break;
4378
4379                 update_rev_graph(graph);
4380                 graph = graph->next;
4381
4382                 *nameend = *emailend = 0;
4383                 ident = chomp_string(ident);
4384                 if (!*ident) {
4385                         ident = chomp_string(nameend + 1);
4386                         if (!*ident)
4387                                 ident = "Unknown";
4388                 }
4389
4390                 string_ncopy(commit->author, ident, strlen(ident));
4391
4392                 /* Parse epoch and timezone */
4393                 if (emailend[1] == ' ') {
4394                         char *secs = emailend + 2;
4395                         char *zone = strchr(secs, ' ');
4396                         time_t time = (time_t) atol(secs);
4397
4398                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
4399                                 long tz;
4400
4401                                 zone++;
4402                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
4403                                 tz += ('0' - zone[2]) * 60 * 60;
4404                                 tz += ('0' - zone[3]) * 60;
4405                                 tz += ('0' - zone[4]) * 60;
4406
4407                                 if (zone[0] == '-')
4408                                         tz = -tz;
4409
4410                                 time -= tz;
4411                         }
4412
4413                         gmtime_r(&time, &commit->time);
4414                 }
4415                 break;
4416         }
4417         default:
4418                 /* Fill in the commit title if it has not already been set. */
4419                 if (commit->title[0])
4420                         break;
4421
4422                 /* Require titles to start with a non-space character at the
4423                  * offset used by git log. */
4424                 if (strncmp(line, "    ", 4))
4425                         break;
4426                 line += 4;
4427                 /* Well, if the title starts with a whitespace character,
4428                  * try to be forgiving.  Otherwise we end up with no title. */
4429                 while (isspace(*line))
4430                         line++;
4431                 if (*line == '\0')
4432                         break;
4433                 /* FIXME: More graceful handling of titles; append "..." to
4434                  * shortened titles, etc. */
4435
4436                 string_ncopy(commit->title, line, strlen(line));
4437         }
4438
4439         return TRUE;
4440 }
4441
4442 static enum request
4443 main_request(struct view *view, enum request request, struct line *line)
4444 {
4445         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4446
4447         if (request == REQ_ENTER)
4448                 open_view(view, REQ_VIEW_DIFF, flags);
4449         else
4450                 return request;
4451
4452         return REQ_NONE;
4453 }
4454
4455 static bool
4456 main_grep(struct view *view, struct line *line)
4457 {
4458         struct commit *commit = line->data;
4459         enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
4460         char buf[DATE_COLS + 1];
4461         regmatch_t pmatch;
4462
4463         for (state = S_TITLE; state < S_END; state++) {
4464                 char *text;
4465
4466                 switch (state) {
4467                 case S_TITLE:   text = commit->title;   break;
4468                 case S_AUTHOR:  text = commit->author;  break;
4469                 case S_DATE:
4470                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
4471                                 continue;
4472                         text = buf;
4473                         break;
4474
4475                 default:
4476                         return FALSE;
4477                 }
4478
4479                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4480                         return TRUE;
4481         }
4482
4483         return FALSE;
4484 }
4485
4486 static void
4487 main_select(struct view *view, struct line *line)
4488 {
4489         struct commit *commit = line->data;
4490
4491         string_copy_rev(view->ref, commit->id);
4492         string_copy_rev(ref_commit, view->ref);
4493 }
4494
4495 static struct view_ops main_ops = {
4496         "commit",
4497         NULL,
4498         main_read,
4499         main_draw,
4500         main_request,
4501         main_grep,
4502         main_select,
4503 };
4504
4505
4506 /*
4507  * Unicode / UTF-8 handling
4508  *
4509  * NOTE: Much of the following code for dealing with unicode is derived from
4510  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
4511  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
4512  */
4513
4514 /* I've (over)annotated a lot of code snippets because I am not entirely
4515  * confident that the approach taken by this small UTF-8 interface is correct.
4516  * --jonas */
4517
4518 static inline int
4519 unicode_width(unsigned long c)
4520 {
4521         if (c >= 0x1100 &&
4522            (c <= 0x115f                         /* Hangul Jamo */
4523             || c == 0x2329
4524             || c == 0x232a
4525             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
4526                                                 /* CJK ... Yi */
4527             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
4528             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
4529             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
4530             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
4531             || (c >= 0xffe0  && c <= 0xffe6)
4532             || (c >= 0x20000 && c <= 0x2fffd)
4533             || (c >= 0x30000 && c <= 0x3fffd)))
4534                 return 2;
4535
4536         if (c == '\t')
4537                 return opt_tab_size;
4538
4539         return 1;
4540 }
4541
4542 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
4543  * Illegal bytes are set one. */
4544 static const unsigned char utf8_bytes[256] = {
4545         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,
4546         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,
4547         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,
4548         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,
4549         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,
4550         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,
4551         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,
4552         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,
4553 };
4554
4555 /* Decode UTF-8 multi-byte representation into a unicode character. */
4556 static inline unsigned long
4557 utf8_to_unicode(const char *string, size_t length)
4558 {
4559         unsigned long unicode;
4560
4561         switch (length) {
4562         case 1:
4563                 unicode  =   string[0];
4564                 break;
4565         case 2:
4566                 unicode  =  (string[0] & 0x1f) << 6;
4567                 unicode +=  (string[1] & 0x3f);
4568                 break;
4569         case 3:
4570                 unicode  =  (string[0] & 0x0f) << 12;
4571                 unicode += ((string[1] & 0x3f) << 6);
4572                 unicode +=  (string[2] & 0x3f);
4573                 break;
4574         case 4:
4575                 unicode  =  (string[0] & 0x0f) << 18;
4576                 unicode += ((string[1] & 0x3f) << 12);
4577                 unicode += ((string[2] & 0x3f) << 6);
4578                 unicode +=  (string[3] & 0x3f);
4579                 break;
4580         case 5:
4581                 unicode  =  (string[0] & 0x0f) << 24;
4582                 unicode += ((string[1] & 0x3f) << 18);
4583                 unicode += ((string[2] & 0x3f) << 12);
4584                 unicode += ((string[3] & 0x3f) << 6);
4585                 unicode +=  (string[4] & 0x3f);
4586                 break;
4587         case 6:
4588                 unicode  =  (string[0] & 0x01) << 30;
4589                 unicode += ((string[1] & 0x3f) << 24);
4590                 unicode += ((string[2] & 0x3f) << 18);
4591                 unicode += ((string[3] & 0x3f) << 12);
4592                 unicode += ((string[4] & 0x3f) << 6);
4593                 unicode +=  (string[5] & 0x3f);
4594                 break;
4595         default:
4596                 die("Invalid unicode length");
4597         }
4598
4599         /* Invalid characters could return the special 0xfffd value but NUL
4600          * should be just as good. */
4601         return unicode > 0xffff ? 0 : unicode;
4602 }
4603
4604 /* Calculates how much of string can be shown within the given maximum width
4605  * and sets trimmed parameter to non-zero value if all of string could not be
4606  * shown. If the reserve flag is TRUE, it will reserve at least one
4607  * trailing character, which can be useful when drawing a delimiter.
4608  *
4609  * Returns the number of bytes to output from string to satisfy max_width. */
4610 static size_t
4611 utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve)
4612 {
4613         const char *start = string;
4614         const char *end = strchr(string, '\0');
4615         unsigned char last_bytes = 0;
4616         size_t width = 0;
4617
4618         *trimmed = 0;
4619
4620         while (string < end) {
4621                 int c = *(unsigned char *) string;
4622                 unsigned char bytes = utf8_bytes[c];
4623                 size_t ucwidth;
4624                 unsigned long unicode;
4625
4626                 if (string + bytes > end)
4627                         break;
4628
4629                 /* Change representation to figure out whether
4630                  * it is a single- or double-width character. */
4631
4632                 unicode = utf8_to_unicode(string, bytes);
4633                 /* FIXME: Graceful handling of invalid unicode character. */
4634                 if (!unicode)
4635                         break;
4636
4637                 ucwidth = unicode_width(unicode);
4638                 width  += ucwidth;
4639                 if (width > max_width) {
4640                         *trimmed = 1;
4641                         if (reserve && width - ucwidth == max_width) {
4642                                 string -= last_bytes;
4643                         }
4644                         break;
4645                 }
4646
4647                 string  += bytes;
4648                 last_bytes = bytes;
4649         }
4650
4651         return string - start;
4652 }
4653
4654
4655 /*
4656  * Status management
4657  */
4658
4659 /* Whether or not the curses interface has been initialized. */
4660 static bool cursed = FALSE;
4661
4662 /* The status window is used for polling keystrokes. */
4663 static WINDOW *status_win;
4664
4665 static bool status_empty = TRUE;
4666
4667 /* Update status and title window. */
4668 static void
4669 report(const char *msg, ...)
4670 {
4671         struct view *view = display[current_view];
4672
4673         if (input_mode)
4674                 return;
4675
4676         if (!view) {
4677                 char buf[SIZEOF_STR];
4678                 va_list args;
4679
4680                 va_start(args, msg);
4681                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
4682                         buf[sizeof(buf) - 1] = 0;
4683                         buf[sizeof(buf) - 2] = '.';
4684                         buf[sizeof(buf) - 3] = '.';
4685                         buf[sizeof(buf) - 4] = '.';
4686                 }
4687                 va_end(args);
4688                 die("%s", buf);
4689         }
4690
4691         if (!status_empty || *msg) {
4692                 va_list args;
4693
4694                 va_start(args, msg);
4695
4696                 wmove(status_win, 0, 0);
4697                 if (*msg) {
4698                         vwprintw(status_win, msg, args);
4699                         status_empty = FALSE;
4700                 } else {
4701                         status_empty = TRUE;
4702                 }
4703                 wclrtoeol(status_win);
4704                 wrefresh(status_win);
4705
4706                 va_end(args);
4707         }
4708
4709         update_view_title(view);
4710         update_display_cursor(view);
4711 }
4712
4713 /* Controls when nodelay should be in effect when polling user input. */
4714 static void
4715 set_nonblocking_input(bool loading)
4716 {
4717         static unsigned int loading_views;
4718
4719         if ((loading == FALSE && loading_views-- == 1) ||
4720             (loading == TRUE  && loading_views++ == 0))
4721                 nodelay(status_win, loading);
4722 }
4723
4724 static void
4725 init_display(void)
4726 {
4727         int x, y;
4728
4729         /* Initialize the curses library */
4730         if (isatty(STDIN_FILENO)) {
4731                 cursed = !!initscr();
4732         } else {
4733                 /* Leave stdin and stdout alone when acting as a pager. */
4734                 FILE *io = fopen("/dev/tty", "r+");
4735
4736                 if (!io)
4737                         die("Failed to open /dev/tty");
4738                 cursed = !!newterm(NULL, io, io);
4739         }
4740
4741         if (!cursed)
4742                 die("Failed to initialize curses");
4743
4744         nonl();         /* Tell curses not to do NL->CR/NL on output */
4745         cbreak();       /* Take input chars one at a time, no wait for \n */
4746         noecho();       /* Don't echo input */
4747         leaveok(stdscr, TRUE);
4748
4749         if (has_colors())
4750                 init_colors();
4751
4752         getmaxyx(stdscr, y, x);
4753         status_win = newwin(1, 0, y - 1, 0);
4754         if (!status_win)
4755                 die("Failed to create status window");
4756
4757         /* Enable keyboard mapping */
4758         keypad(status_win, TRUE);
4759         wbkgdset(status_win, get_line_attr(LINE_STATUS));
4760 }
4761
4762 static char *
4763 read_prompt(const char *prompt)
4764 {
4765         enum { READING, STOP, CANCEL } status = READING;
4766         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
4767         int pos = 0;
4768
4769         while (status == READING) {
4770                 struct view *view;
4771                 int i, key;
4772
4773                 input_mode = TRUE;
4774
4775                 foreach_view (view, i)
4776                         update_view(view);
4777
4778                 input_mode = FALSE;
4779
4780                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
4781                 wclrtoeol(status_win);
4782
4783                 /* Refresh, accept single keystroke of input */
4784                 key = wgetch(status_win);
4785                 switch (key) {
4786                 case KEY_RETURN:
4787                 case KEY_ENTER:
4788                 case '\n':
4789                         status = pos ? STOP : CANCEL;
4790                         break;
4791
4792                 case KEY_BACKSPACE:
4793                         if (pos > 0)
4794                                 pos--;
4795                         else
4796                                 status = CANCEL;
4797                         break;
4798
4799                 case KEY_ESC:
4800                         status = CANCEL;
4801                         break;
4802
4803                 case ERR:
4804                         break;
4805
4806                 default:
4807                         if (pos >= sizeof(buf)) {
4808                                 report("Input string too long");
4809                                 return NULL;
4810                         }
4811
4812                         if (isprint(key))
4813                                 buf[pos++] = (char) key;
4814                 }
4815         }
4816
4817         /* Clear the status window */
4818         status_empty = FALSE;
4819         report("");
4820
4821         if (status == CANCEL)
4822                 return NULL;
4823
4824         buf[pos++] = 0;
4825
4826         return buf;
4827 }
4828
4829 /*
4830  * Repository references
4831  */
4832
4833 static struct ref *refs;
4834 static size_t refs_size;
4835
4836 /* Id <-> ref store */
4837 static struct ref ***id_refs;
4838 static size_t id_refs_size;
4839
4840 static struct ref **
4841 get_refs(char *id)
4842 {
4843         struct ref ***tmp_id_refs;
4844         struct ref **ref_list = NULL;
4845         size_t ref_list_size = 0;
4846         size_t i;
4847
4848         for (i = 0; i < id_refs_size; i++)
4849                 if (!strcmp(id, id_refs[i][0]->id))
4850                         return id_refs[i];
4851
4852         tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
4853         if (!tmp_id_refs)
4854                 return NULL;
4855
4856         id_refs = tmp_id_refs;
4857
4858         for (i = 0; i < refs_size; i++) {
4859                 struct ref **tmp;
4860
4861                 if (strcmp(id, refs[i].id))
4862                         continue;
4863
4864                 tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
4865                 if (!tmp) {
4866                         if (ref_list)
4867                                 free(ref_list);
4868                         return NULL;
4869                 }
4870
4871                 ref_list = tmp;
4872                 if (ref_list_size > 0)
4873                         ref_list[ref_list_size - 1]->next = 1;
4874                 ref_list[ref_list_size] = &refs[i];
4875
4876                 /* XXX: The properties of the commit chains ensures that we can
4877                  * safely modify the shared ref. The repo references will
4878                  * always be similar for the same id. */
4879                 ref_list[ref_list_size]->next = 0;
4880                 ref_list_size++;
4881         }
4882
4883         if (ref_list)
4884                 id_refs[id_refs_size++] = ref_list;
4885
4886         return ref_list;
4887 }
4888
4889 static int
4890 read_ref(char *id, size_t idlen, char *name, size_t namelen)
4891 {
4892         struct ref *ref;
4893         bool tag = FALSE;
4894         bool ltag = FALSE;
4895         bool remote = FALSE;
4896         bool check_replace = FALSE;
4897
4898         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
4899                 if (!strcmp(name + namelen - 3, "^{}")) {
4900                         namelen -= 3;
4901                         name[namelen] = 0;
4902                         if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
4903                                 check_replace = TRUE;
4904                 } else {
4905                         ltag = TRUE;
4906                 }
4907
4908                 tag = TRUE;
4909                 namelen -= STRING_SIZE("refs/tags/");
4910                 name    += STRING_SIZE("refs/tags/");
4911
4912         } else if (!strncmp(name, "refs/remotes/", STRING_SIZE("refs/remotes/"))) {
4913                 remote = TRUE;
4914                 namelen -= STRING_SIZE("refs/remotes/");
4915                 name    += STRING_SIZE("refs/remotes/");
4916
4917         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
4918                 namelen -= STRING_SIZE("refs/heads/");
4919                 name    += STRING_SIZE("refs/heads/");
4920
4921         } else if (!strcmp(name, "HEAD")) {
4922                 return OK;
4923         }
4924
4925         if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
4926                 /* it's an annotated tag, replace the previous sha1 with the
4927                  * resolved commit id; relies on the fact git-ls-remote lists
4928                  * the commit id of an annotated tag right beofre the commit id
4929                  * it points to. */
4930                 refs[refs_size - 1].ltag = ltag;
4931                 string_copy_rev(refs[refs_size - 1].id, id);
4932
4933                 return OK;
4934         }
4935         refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
4936         if (!refs)
4937                 return ERR;
4938
4939         ref = &refs[refs_size++];
4940         ref->name = malloc(namelen + 1);
4941         if (!ref->name)
4942                 return ERR;
4943
4944         strncpy(ref->name, name, namelen);
4945         ref->name[namelen] = 0;
4946         ref->tag = tag;
4947         ref->ltag = ltag;
4948         ref->remote = remote;
4949         string_copy_rev(ref->id, id);
4950
4951         return OK;
4952 }
4953
4954 static int
4955 load_refs(void)
4956 {
4957         const char *cmd_env = getenv("TIG_LS_REMOTE");
4958         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
4959
4960         return read_properties(popen(cmd, "r"), "\t", read_ref);
4961 }
4962
4963 static int
4964 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
4965 {
4966         if (!strcmp(name, "i18n.commitencoding"))
4967                 string_ncopy(opt_encoding, value, valuelen);
4968
4969         if (!strcmp(name, "core.editor"))
4970                 string_ncopy(opt_editor, value, valuelen);
4971
4972         return OK;
4973 }
4974
4975 static int
4976 load_repo_config(void)
4977 {
4978         return read_properties(popen(GIT_CONFIG " --list", "r"),
4979                                "=", read_repo_config_option);
4980 }
4981
4982 static int
4983 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
4984 {
4985         if (!opt_git_dir[0]) {
4986                 string_ncopy(opt_git_dir, name, namelen);
4987
4988         } else if (opt_is_inside_work_tree == -1) {
4989                 /* This can be 3 different values depending on the
4990                  * version of git being used. If git-rev-parse does not
4991                  * understand --is-inside-work-tree it will simply echo
4992                  * the option else either "true" or "false" is printed.
4993                  * Default to true for the unknown case. */
4994                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
4995
4996         } else {
4997                 string_ncopy(opt_cdup, name, namelen);
4998         }
4999
5000         return OK;
5001 }
5002
5003 /* XXX: The line outputted by "--show-cdup" can be empty so the option
5004  * must be the last one! */
5005 static int
5006 load_repo_info(void)
5007 {
5008         return read_properties(popen("git rev-parse --git-dir --is-inside-work-tree --show-cdup 2>/dev/null", "r"),
5009                                "=", read_repo_info);
5010 }
5011
5012 static int
5013 read_properties(FILE *pipe, const char *separators,
5014                 int (*read_property)(char *, size_t, char *, size_t))
5015 {
5016         char buffer[BUFSIZ];
5017         char *name;
5018         int state = OK;
5019
5020         if (!pipe)
5021                 return ERR;
5022
5023         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
5024                 char *value;
5025                 size_t namelen;
5026                 size_t valuelen;
5027
5028                 name = chomp_string(name);
5029                 namelen = strcspn(name, separators);
5030
5031                 if (name[namelen]) {
5032                         name[namelen] = 0;
5033                         value = chomp_string(name + namelen + 1);
5034                         valuelen = strlen(value);
5035
5036                 } else {
5037                         value = "";
5038                         valuelen = 0;
5039                 }
5040
5041                 state = read_property(name, namelen, value, valuelen);
5042         }
5043
5044         if (state != ERR && ferror(pipe))
5045                 state = ERR;
5046
5047         pclose(pipe);
5048
5049         return state;
5050 }
5051
5052
5053 /*
5054  * Main
5055  */
5056
5057 static void __NORETURN
5058 quit(int sig)
5059 {
5060         /* XXX: Restore tty modes and let the OS cleanup the rest! */
5061         if (cursed)
5062                 endwin();
5063         exit(0);
5064 }
5065
5066 static void __NORETURN
5067 die(const char *err, ...)
5068 {
5069         va_list args;
5070
5071         endwin();
5072
5073         va_start(args, err);
5074         fputs("tig: ", stderr);
5075         vfprintf(stderr, err, args);
5076         fputs("\n", stderr);
5077         va_end(args);
5078
5079         exit(1);
5080 }
5081
5082 static void
5083 warn(const char *msg, ...)
5084 {
5085         va_list args;
5086
5087         va_start(args, msg);
5088         fputs("tig warning: ", stderr);
5089         vfprintf(stderr, msg, args);
5090         fputs("\n", stderr);
5091         va_end(args);
5092 }
5093
5094 int
5095 main(int argc, char *argv[])
5096 {
5097         struct view *view;
5098         enum request request;
5099         size_t i;
5100
5101         signal(SIGINT, quit);
5102
5103         if (setlocale(LC_ALL, "")) {
5104                 char *codeset = nl_langinfo(CODESET);
5105
5106                 string_ncopy(opt_codeset, codeset, strlen(codeset));
5107         }
5108
5109         if (load_repo_info() == ERR)
5110                 die("Failed to load repo info.");
5111
5112         if (load_options() == ERR)
5113                 die("Failed to load user config.");
5114
5115         /* Load the repo config file so options can be overwritten from
5116          * the command line. */
5117         if (load_repo_config() == ERR)
5118                 die("Failed to load repo config.");
5119
5120         if (!parse_options(argc, argv))
5121                 return 0;
5122
5123         /* Require a git repository unless when running in pager mode. */
5124         if (!opt_git_dir[0])
5125                 die("Not a git repository");
5126
5127         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
5128                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
5129                 if (opt_iconv == ICONV_NONE)
5130                         die("Failed to initialize character set conversion");
5131         }
5132
5133         if (load_refs() == ERR)
5134                 die("Failed to load refs.");
5135
5136         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
5137                 view->cmd_env = getenv(view->cmd_env);
5138
5139         request = opt_request;
5140
5141         init_display();
5142
5143         while (view_driver(display[current_view], request)) {
5144                 int key;
5145                 int i;
5146
5147                 foreach_view (view, i)
5148                         update_view(view);
5149
5150                 /* Refresh, accept single keystroke of input */
5151                 key = wgetch(status_win);
5152
5153                 /* wgetch() with nodelay() enabled returns ERR when there's no
5154                  * input. */
5155                 if (key == ERR) {
5156                         request = REQ_NONE;
5157                         continue;
5158                 }
5159
5160                 request = get_keybinding(display[current_view]->keymap, key);
5161
5162                 /* Some low-level request handling. This keeps access to
5163                  * status_win restricted. */
5164                 switch (request) {
5165                 case REQ_PROMPT:
5166                 {
5167                         char *cmd = read_prompt(":");
5168
5169                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
5170                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
5171                                         opt_request = REQ_VIEW_DIFF;
5172                                 } else {
5173                                         opt_request = REQ_VIEW_PAGER;
5174                                 }
5175                                 break;
5176                         }
5177
5178                         request = REQ_NONE;
5179                         break;
5180                 }
5181                 case REQ_SEARCH:
5182                 case REQ_SEARCH_BACK:
5183                 {
5184                         const char *prompt = request == REQ_SEARCH
5185                                            ? "/" : "?";
5186                         char *search = read_prompt(prompt);
5187
5188                         if (search)
5189                                 string_ncopy(opt_search, search, strlen(search));
5190                         else
5191                                 request = REQ_NONE;
5192                         break;
5193                 }
5194                 case REQ_SCREEN_RESIZE:
5195                 {
5196                         int height, width;
5197
5198                         getmaxyx(stdscr, height, width);
5199
5200                         /* Resize the status view and let the view driver take
5201                          * care of resizing the displayed views. */
5202                         wresize(status_win, 1, width);
5203                         mvwin(status_win, height - 1, 0);
5204                         wrefresh(status_win);
5205                         break;
5206                 }
5207                 default:
5208                         break;
5209                 }
5210         }
5211
5212         quit(0);
5213
5214         return 0;
5215 }