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