Simplify subcommand option parsing by moving it out of the loop
[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         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 (textlen <= SIZEOF_TREE_ATTR)
3081                 return FALSE;
3082
3083         type = text[STRING_SIZE("100644 ")] == 't'
3084              ? LINE_TREE_DIR : LINE_TREE_FILE;
3085
3086         if (first_read) {
3087                 /* Add path info line */
3088                 if (!string_format(buf, "Directory path /%s", opt_path) ||
3089                     !realloc_lines(view, view->line_size + 1) ||
3090                     !add_line_text(view, buf, LINE_DEFAULT))
3091                         return FALSE;
3092
3093                 /* Insert "link" to parent directory. */
3094                 if (*opt_path) {
3095                         if (!string_format(buf, TREE_UP_FORMAT, view->ref) ||
3096                             !realloc_lines(view, view->line_size + 1) ||
3097                             !add_line_text(view, buf, LINE_TREE_DIR))
3098                                 return FALSE;
3099                 }
3100         }
3101
3102         /* Strip the path part ... */
3103         if (*opt_path) {
3104                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
3105                 size_t striplen = strlen(opt_path);
3106                 char *path = text + SIZEOF_TREE_ATTR;
3107
3108                 if (pathlen > striplen)
3109                         memmove(path, path + striplen,
3110                                 pathlen - striplen + 1);
3111         }
3112
3113         /* Skip "Directory ..." and ".." line. */
3114         for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
3115                 struct line *line = &view->line[pos];
3116                 char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR;
3117                 char *path2 = text + SIZEOF_TREE_ATTR;
3118                 int cmp = tree_compare_entry(line->type, path1, type, path2);
3119
3120                 if (cmp <= 0)
3121                         continue;
3122
3123                 text = strdup(text);
3124                 if (!text)
3125                         return FALSE;
3126
3127                 if (view->lines > pos)
3128                         memmove(&view->line[pos + 1], &view->line[pos],
3129                                 (view->lines - pos) * sizeof(*line));
3130
3131                 line = &view->line[pos];
3132                 line->data = text;
3133                 line->type = type;
3134                 view->lines++;
3135                 return TRUE;
3136         }
3137
3138         if (!add_line_text(view, text, type))
3139                 return FALSE;
3140
3141         if (tree_lineno > view->lineno) {
3142                 view->lineno = tree_lineno;
3143                 tree_lineno = 0;
3144         }
3145
3146         return TRUE;
3147 }
3148
3149 static enum request
3150 tree_request(struct view *view, enum request request, struct line *line)
3151 {
3152         enum open_flags flags;
3153
3154         if (request == REQ_TREE_PARENT) {
3155                 if (*opt_path) {
3156                         /* fake 'cd  ..' */
3157                         request = REQ_ENTER;
3158                         line = &view->line[1];
3159                 } else {
3160                         /* quit view if at top of tree */
3161                         return REQ_VIEW_CLOSE;
3162                 }
3163         }
3164         if (request != REQ_ENTER)
3165                 return request;
3166
3167         /* Cleanup the stack if the tree view is at a different tree. */
3168         while (!*opt_path && tree_stack)
3169                 pop_tree_stack_entry();
3170
3171         switch (line->type) {
3172         case LINE_TREE_DIR:
3173                 /* Depending on whether it is a subdir or parent (updir?) link
3174                  * mangle the path buffer. */
3175                 if (line == &view->line[1] && *opt_path) {
3176                         pop_tree_stack_entry();
3177
3178                 } else {
3179                         char *data = line->data;
3180                         char *basename = data + SIZEOF_TREE_ATTR;
3181
3182                         push_tree_stack_entry(basename, view->lineno);
3183                 }
3184
3185                 /* Trees and subtrees share the same ID, so they are not not
3186                  * unique like blobs. */
3187                 flags = OPEN_RELOAD;
3188                 request = REQ_VIEW_TREE;
3189                 break;
3190
3191         case LINE_TREE_FILE:
3192                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3193                 request = REQ_VIEW_BLOB;
3194                 break;
3195
3196         default:
3197                 return TRUE;
3198         }
3199
3200         open_view(view, request, flags);
3201         if (request == REQ_VIEW_TREE) {
3202                 view->lineno = tree_lineno;
3203         }
3204
3205         return REQ_NONE;
3206 }
3207
3208 static void
3209 tree_select(struct view *view, struct line *line)
3210 {
3211         char *text = (char *)line->data + STRING_SIZE("100644 blob ");
3212
3213         if (line->type == LINE_TREE_FILE) {
3214                 string_copy_rev(ref_blob, text);
3215
3216         } else if (line->type != LINE_TREE_DIR) {
3217                 return;
3218         }
3219
3220         string_copy_rev(view->ref, text);
3221 }
3222
3223 static struct view_ops tree_ops = {
3224         "file",
3225         NULL,
3226         tree_read,
3227         pager_draw,
3228         tree_request,
3229         pager_grep,
3230         tree_select,
3231 };
3232
3233 static bool
3234 blob_read(struct view *view, char *line)
3235 {
3236         return add_line_text(view, line, LINE_DEFAULT) != NULL;
3237 }
3238
3239 static struct view_ops blob_ops = {
3240         "line",
3241         NULL,
3242         blob_read,
3243         pager_draw,
3244         pager_request,
3245         pager_grep,
3246         pager_select,
3247 };
3248
3249
3250 /*
3251  * Status backend
3252  */
3253
3254 struct status {
3255         char status;
3256         struct {
3257                 mode_t mode;
3258                 char rev[SIZEOF_REV];
3259                 char name[SIZEOF_STR];
3260         } old;
3261         struct {
3262                 mode_t mode;
3263                 char rev[SIZEOF_REV];
3264                 char name[SIZEOF_STR];
3265         } new;
3266 };
3267
3268 static struct status stage_status;
3269 static enum line_type stage_line_type;
3270
3271 /* Get fields from the diff line:
3272  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
3273  */
3274 static inline bool
3275 status_get_diff(struct status *file, char *buf, size_t bufsize)
3276 {
3277         char *old_mode = buf +  1;
3278         char *new_mode = buf +  8;
3279         char *old_rev  = buf + 15;
3280         char *new_rev  = buf + 56;
3281         char *status   = buf + 97;
3282
3283         if (bufsize < 99 ||
3284             old_mode[-1] != ':' ||
3285             new_mode[-1] != ' ' ||
3286             old_rev[-1]  != ' ' ||
3287             new_rev[-1]  != ' ' ||
3288             status[-1]   != ' ')
3289                 return FALSE;
3290
3291         file->status = *status;
3292
3293         string_copy_rev(file->old.rev, old_rev);
3294         string_copy_rev(file->new.rev, new_rev);
3295
3296         file->old.mode = strtoul(old_mode, NULL, 8);
3297         file->new.mode = strtoul(new_mode, NULL, 8);
3298
3299         file->old.name[0] = file->new.name[0] = 0;
3300
3301         return TRUE;
3302 }
3303
3304 static bool
3305 status_run(struct view *view, const char cmd[], bool diff, enum line_type type)
3306 {
3307         struct status *file = NULL;
3308         struct status *unmerged = NULL;
3309         char buf[SIZEOF_STR * 4];
3310         size_t bufsize = 0;
3311         FILE *pipe;
3312
3313         pipe = popen(cmd, "r");
3314         if (!pipe)
3315                 return FALSE;
3316
3317         add_line_data(view, NULL, type);
3318
3319         while (!feof(pipe) && !ferror(pipe)) {
3320                 char *sep;
3321                 size_t readsize;
3322
3323                 readsize = fread(buf + bufsize, 1, sizeof(buf) - bufsize, pipe);
3324                 if (!readsize)
3325                         break;
3326                 bufsize += readsize;
3327
3328                 /* Process while we have NUL chars. */
3329                 while ((sep = memchr(buf, 0, bufsize))) {
3330                         size_t sepsize = sep - buf + 1;
3331
3332                         if (!file) {
3333                                 if (!realloc_lines(view, view->line_size + 1))
3334                                         goto error_out;
3335
3336                                 file = calloc(1, sizeof(*file));
3337                                 if (!file)
3338                                         goto error_out;
3339
3340                                 add_line_data(view, file, type);
3341                         }
3342
3343                         /* Parse diff info part. */
3344                         if (!diff) {
3345                                 file->status = '?';
3346
3347                         } else if (!file->status) {
3348                                 if (!status_get_diff(file, buf, sepsize))
3349                                         goto error_out;
3350
3351                                 bufsize -= sepsize;
3352                                 memmove(buf, sep + 1, bufsize);
3353
3354                                 sep = memchr(buf, 0, bufsize);
3355                                 if (!sep)
3356                                         break;
3357                                 sepsize = sep - buf + 1;
3358
3359                                 /* Collapse all 'M'odified entries that
3360                                  * follow a associated 'U'nmerged entry.
3361                                  */
3362                                 if (file->status == 'U') {
3363                                         unmerged = file;
3364
3365                                 } else if (unmerged) {
3366                                         int collapse = !strcmp(buf, unmerged->new.name);
3367
3368                                         unmerged = NULL;
3369                                         if (collapse) {
3370                                                 free(file);
3371                                                 view->lines--;
3372                                                 continue;
3373                                         }
3374                                 }
3375                         }
3376
3377                         /* Grab the old name for rename/copy. */
3378                         if (!*file->old.name &&
3379                             (file->status == 'R' || file->status == 'C')) {
3380                                 sepsize = sep - buf + 1;
3381                                 string_ncopy(file->old.name, buf, sepsize);
3382                                 bufsize -= sepsize;
3383                                 memmove(buf, sep + 1, bufsize);
3384
3385                                 sep = memchr(buf, 0, bufsize);
3386                                 if (!sep)
3387                                         break;
3388                                 sepsize = sep - buf + 1;
3389                         }
3390
3391                         /* git-ls-files just delivers a NUL separated
3392                          * list of file names similar to the second half
3393                          * of the git-diff-* output. */
3394                         string_ncopy(file->new.name, buf, sepsize);
3395                         if (!*file->old.name)
3396                                 string_copy(file->old.name, file->new.name);
3397                         bufsize -= sepsize;
3398                         memmove(buf, sep + 1, bufsize);
3399                         file = NULL;
3400                 }
3401         }
3402
3403         if (ferror(pipe)) {
3404 error_out:
3405                 pclose(pipe);
3406                 return FALSE;
3407         }
3408
3409         if (!view->line[view->lines - 1].data)
3410                 add_line_data(view, NULL, LINE_STAT_NONE);
3411
3412         pclose(pipe);
3413         return TRUE;
3414 }
3415
3416 /* Don't show unmerged entries in the staged section. */
3417 #define STATUS_DIFF_INDEX_CMD "git diff-index -z --diff-filter=ACDMRTXB --cached -M HEAD"
3418 #define STATUS_DIFF_FILES_CMD "git diff-files -z"
3419 #define STATUS_LIST_OTHER_CMD \
3420         "git ls-files -z --others --exclude-per-directory=.gitignore"
3421
3422 #define STATUS_DIFF_INDEX_SHOW_CMD \
3423         "git diff-index --root --patch-with-stat -C -M --cached HEAD -- %s %s 2>/dev/null"
3424
3425 #define STATUS_DIFF_FILES_SHOW_CMD \
3426         "git diff-files --root --patch-with-stat -C -M -- %s %s 2>/dev/null"
3427
3428 /* First parse staged info using git-diff-index(1), then parse unstaged
3429  * info using git-diff-files(1), and finally untracked files using
3430  * git-ls-files(1). */
3431 static bool
3432 status_open(struct view *view)
3433 {
3434         struct stat statbuf;
3435         char exclude[SIZEOF_STR];
3436         char cmd[SIZEOF_STR];
3437         unsigned long prev_lineno = view->lineno;
3438         size_t i;
3439
3440         for (i = 0; i < view->lines; i++)
3441                 free(view->line[i].data);
3442         free(view->line);
3443         view->lines = view->line_alloc = view->line_size = view->lineno = 0;
3444         view->line = NULL;
3445
3446         if (!realloc_lines(view, view->line_size + 6))
3447                 return FALSE;
3448
3449         if (!string_format(exclude, "%s/info/exclude", opt_git_dir))
3450                 return FALSE;
3451
3452         string_copy(cmd, STATUS_LIST_OTHER_CMD);
3453
3454         if (stat(exclude, &statbuf) >= 0) {
3455                 size_t cmdsize = strlen(cmd);
3456
3457                 if (!string_format_from(cmd, &cmdsize, " %s", "--exclude-from=") ||
3458                     sq_quote(cmd, cmdsize, exclude) >= sizeof(cmd))
3459                         return FALSE;
3460         }
3461
3462         system("git update-index -q --refresh");
3463
3464         if (!status_run(view, STATUS_DIFF_INDEX_CMD, TRUE, LINE_STAT_STAGED) ||
3465             !status_run(view, STATUS_DIFF_FILES_CMD, TRUE, LINE_STAT_UNSTAGED) ||
3466             !status_run(view, cmd, FALSE, LINE_STAT_UNTRACKED))
3467                 return FALSE;
3468
3469         /* If all went well restore the previous line number to stay in
3470          * the context. */
3471         if (prev_lineno < view->lines)
3472                 view->lineno = prev_lineno;
3473         else
3474                 view->lineno = view->lines - 1;
3475
3476         return TRUE;
3477 }
3478
3479 static bool
3480 status_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
3481 {
3482         struct status *status = line->data;
3483         int tilde_attr = get_line_attr(LINE_MAIN_DELIM);
3484
3485         wmove(view->win, lineno, 0);
3486
3487         if (selected) {
3488                 wattrset(view->win, get_line_attr(LINE_CURSOR));
3489                 wchgat(view->win, -1, 0, LINE_CURSOR, NULL);
3490                 tilde_attr = -1;
3491
3492         } else if (!status && line->type != LINE_STAT_NONE) {
3493                 wattrset(view->win, get_line_attr(LINE_STAT_SECTION));
3494                 wchgat(view->win, -1, 0, LINE_STAT_SECTION, NULL);
3495
3496         } else {
3497                 wattrset(view->win, get_line_attr(line->type));
3498         }
3499
3500         if (!status) {
3501                 char *text;
3502
3503                 switch (line->type) {
3504                 case LINE_STAT_STAGED:
3505                         text = "Changes to be committed:";
3506                         break;
3507
3508                 case LINE_STAT_UNSTAGED:
3509                         text = "Changed but not updated:";
3510                         break;
3511
3512                 case LINE_STAT_UNTRACKED:
3513                         text = "Untracked files:";
3514                         break;
3515
3516                 case LINE_STAT_NONE:
3517                         text = "    (no files)";
3518                         break;
3519
3520                 default:
3521                         return FALSE;
3522                 }
3523
3524                 draw_text(view, text, view->width, TRUE, tilde_attr);
3525                 return TRUE;
3526         }
3527
3528         waddch(view->win, status->status);
3529         if (!selected)
3530                 wattrset(view->win, A_NORMAL);
3531         wmove(view->win, lineno, 4);
3532         if (view->width < 5)
3533                 return TRUE;
3534
3535         draw_text(view, status->new.name, view->width - 5, TRUE, tilde_attr);
3536         return TRUE;
3537 }
3538
3539 static enum request
3540 status_enter(struct view *view, struct line *line)
3541 {
3542         struct status *status = line->data;
3543         char oldpath[SIZEOF_STR] = "";
3544         char newpath[SIZEOF_STR] = "";
3545         char *info;
3546         size_t cmdsize = 0;
3547
3548         if (line->type == LINE_STAT_NONE ||
3549             (!status && line[1].type == LINE_STAT_NONE)) {
3550                 report("No file to diff");
3551                 return REQ_NONE;
3552         }
3553
3554         if (status) {
3555                 if (sq_quote(oldpath, 0, status->old.name) >= sizeof(oldpath))
3556                         return REQ_QUIT;
3557                 /* Diffs for unmerged entries are empty when pasing the
3558                  * new path, so leave it empty. */
3559                 if (status->status != 'U' &&
3560                     sq_quote(newpath, 0, status->new.name) >= sizeof(newpath))
3561                         return REQ_QUIT;
3562         }
3563
3564         if (opt_cdup[0] &&
3565             line->type != LINE_STAT_UNTRACKED &&
3566             !string_format_from(opt_cmd, &cmdsize, "cd %s;", opt_cdup))
3567                 return REQ_QUIT;
3568
3569         switch (line->type) {
3570         case LINE_STAT_STAGED:
3571                 if (!string_format_from(opt_cmd, &cmdsize,
3572                                         STATUS_DIFF_INDEX_SHOW_CMD, oldpath, newpath))
3573                         return REQ_QUIT;
3574                 if (status)
3575                         info = "Staged changes to %s";
3576                 else
3577                         info = "Staged changes";
3578                 break;
3579
3580         case LINE_STAT_UNSTAGED:
3581                 if (!string_format_from(opt_cmd, &cmdsize,
3582                                         STATUS_DIFF_FILES_SHOW_CMD, oldpath, newpath))
3583                         return REQ_QUIT;
3584                 if (status)
3585                         info = "Unstaged changes to %s";
3586                 else
3587                         info = "Unstaged changes";
3588                 break;
3589
3590         case LINE_STAT_UNTRACKED:
3591                 if (opt_pipe)
3592                         return REQ_QUIT;
3593
3594
3595                 if (!status) {
3596                         report("No file to show");
3597                         return REQ_NONE;
3598                 }
3599
3600                 opt_pipe = fopen(status->new.name, "r");
3601                 info = "Untracked file %s";
3602                 break;
3603
3604         default:
3605                 die("line type %d not handled in switch", line->type);
3606         }
3607
3608         open_view(view, REQ_VIEW_STAGE, OPEN_RELOAD | OPEN_SPLIT);
3609         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
3610                 if (status) {
3611                         stage_status = *status;
3612                 } else {
3613                         memset(&stage_status, 0, sizeof(stage_status));
3614                 }
3615
3616                 stage_line_type = line->type;
3617                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
3618         }
3619
3620         return REQ_NONE;
3621 }
3622
3623
3624 static bool
3625 status_update_file(struct view *view, struct status *status, enum line_type type)
3626 {
3627         char cmd[SIZEOF_STR];
3628         char buf[SIZEOF_STR];
3629         size_t cmdsize = 0;
3630         size_t bufsize = 0;
3631         size_t written = 0;
3632         FILE *pipe;
3633
3634         if (opt_cdup[0] &&
3635             type != LINE_STAT_UNTRACKED &&
3636             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
3637                 return FALSE;
3638
3639         switch (type) {
3640         case LINE_STAT_STAGED:
3641                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
3642                                         status->old.mode,
3643                                         status->old.rev,
3644                                         status->old.name, 0))
3645                         return FALSE;
3646
3647                 string_add(cmd, cmdsize, "git update-index -z --index-info");
3648                 break;
3649
3650         case LINE_STAT_UNSTAGED:
3651         case LINE_STAT_UNTRACKED:
3652                 if (!string_format_from(buf, &bufsize, "%s%c", status->new.name, 0))
3653                         return FALSE;
3654
3655                 string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin");
3656                 break;
3657
3658         default:
3659                 die("line type %d not handled in switch", type);
3660         }
3661
3662         pipe = popen(cmd, "w");
3663         if (!pipe)
3664                 return FALSE;
3665
3666         while (!ferror(pipe) && written < bufsize) {
3667                 written += fwrite(buf + written, 1, bufsize - written, pipe);
3668         }
3669
3670         pclose(pipe);
3671
3672         if (written != bufsize)
3673                 return FALSE;
3674
3675         return TRUE;
3676 }
3677
3678 static void
3679 status_update(struct view *view)
3680 {
3681         struct line *line = &view->line[view->lineno];
3682
3683         assert(view->lines);
3684
3685         if (!line->data) {
3686                 while (++line < view->line + view->lines && line->data) {
3687                         if (!status_update_file(view, line->data, line->type))
3688                                 report("Failed to update file status");
3689                 }
3690
3691                 if (!line[-1].data) {
3692                         report("Nothing to update");
3693                         return;
3694                 }
3695
3696         } else if (!status_update_file(view, line->data, line->type)) {
3697                 report("Failed to update file status");
3698         }
3699 }
3700
3701 static enum request
3702 status_request(struct view *view, enum request request, struct line *line)
3703 {
3704         struct status *status = line->data;
3705
3706         switch (request) {
3707         case REQ_STATUS_UPDATE:
3708                 status_update(view);
3709                 break;
3710
3711         case REQ_STATUS_MERGE:
3712                 if (!status || status->status != 'U') {
3713                         report("Merging only possible for files with unmerged status ('U').");
3714                         return REQ_NONE;
3715                 }
3716                 open_mergetool(status->new.name);
3717                 break;
3718
3719         case REQ_EDIT:
3720                 if (!status)
3721                         return request;
3722
3723                 open_editor(status->status != '?', status->new.name);
3724                 break;
3725
3726         case REQ_ENTER:
3727                 /* After returning the status view has been split to
3728                  * show the stage view. No further reloading is
3729                  * necessary. */
3730                 status_enter(view, line);
3731                 return REQ_NONE;
3732
3733         case REQ_REFRESH:
3734                 /* Simply reload the view. */
3735                 break;
3736
3737         default:
3738                 return request;
3739         }
3740
3741         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
3742
3743         return REQ_NONE;
3744 }
3745
3746 static void
3747 status_select(struct view *view, struct line *line)
3748 {
3749         struct status *status = line->data;
3750         char file[SIZEOF_STR] = "all files";
3751         char *text;
3752         char *key;
3753
3754         if (status && !string_format(file, "'%s'", status->new.name))
3755                 return;
3756
3757         if (!status && line[1].type == LINE_STAT_NONE)
3758                 line++;
3759
3760         switch (line->type) {
3761         case LINE_STAT_STAGED:
3762                 text = "Press %s to unstage %s for commit";
3763                 break;
3764
3765         case LINE_STAT_UNSTAGED:
3766                 text = "Press %s to stage %s for commit";
3767                 break;
3768
3769         case LINE_STAT_UNTRACKED:
3770                 text = "Press %s to stage %s for addition";
3771                 break;
3772
3773         case LINE_STAT_NONE:
3774                 text = "Nothing to update";
3775                 break;
3776
3777         default:
3778                 die("line type %d not handled in switch", line->type);
3779         }
3780
3781         if (status && status->status == 'U') {
3782                 text = "Press %s to resolve conflict in %s";
3783                 key = get_key(REQ_STATUS_MERGE);
3784
3785         } else {
3786                 key = get_key(REQ_STATUS_UPDATE);
3787         }
3788
3789         string_format(view->ref, text, key, file);
3790 }
3791
3792 static bool
3793 status_grep(struct view *view, struct line *line)
3794 {
3795         struct status *status = line->data;
3796         enum { S_STATUS, S_NAME, S_END } state;
3797         char buf[2] = "?";
3798         regmatch_t pmatch;
3799
3800         if (!status)
3801                 return FALSE;
3802
3803         for (state = S_STATUS; state < S_END; state++) {
3804                 char *text;
3805
3806                 switch (state) {
3807                 case S_NAME:    text = status->new.name;        break;
3808                 case S_STATUS:
3809                         buf[0] = status->status;
3810                         text = buf;
3811                         break;
3812
3813                 default:
3814                         return FALSE;
3815                 }
3816
3817                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
3818                         return TRUE;
3819         }
3820
3821         return FALSE;
3822 }
3823
3824 static struct view_ops status_ops = {
3825         "file",
3826         status_open,
3827         NULL,
3828         status_draw,
3829         status_request,
3830         status_grep,
3831         status_select,
3832 };
3833
3834
3835 static bool
3836 stage_diff_line(FILE *pipe, struct line *line)
3837 {
3838         char *buf = line->data;
3839         size_t bufsize = strlen(buf);
3840         size_t written = 0;
3841
3842         while (!ferror(pipe) && written < bufsize) {
3843                 written += fwrite(buf + written, 1, bufsize - written, pipe);
3844         }
3845
3846         fputc('\n', pipe);
3847
3848         return written == bufsize;
3849 }
3850
3851 static struct line *
3852 stage_diff_hdr(struct view *view, struct line *line)
3853 {
3854         int diff_hdr_dir = line->type == LINE_DIFF_CHUNK ? -1 : 1;
3855         struct line *diff_hdr;
3856
3857         if (line->type == LINE_DIFF_CHUNK)
3858                 diff_hdr = line - 1;
3859         else
3860                 diff_hdr = view->line + 1;
3861
3862         while (diff_hdr > view->line && diff_hdr < view->line + view->lines) {
3863                 if (diff_hdr->type == LINE_DIFF_HEADER)
3864                         return diff_hdr;
3865
3866                 diff_hdr += diff_hdr_dir;
3867         }
3868
3869         return NULL;
3870 }
3871
3872 static bool
3873 stage_update_chunk(struct view *view, struct line *line)
3874 {
3875         char cmd[SIZEOF_STR];
3876         size_t cmdsize = 0;
3877         struct line *diff_hdr, *diff_chunk, *diff_end;
3878         FILE *pipe;
3879
3880         diff_hdr = stage_diff_hdr(view, line);
3881         if (!diff_hdr)
3882                 return FALSE;
3883
3884         if (opt_cdup[0] &&
3885             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
3886                 return FALSE;
3887
3888         if (!string_format_from(cmd, &cmdsize,
3889                                 "git apply --cached %s - && "
3890                                 "git update-index -q --unmerged --refresh 2>/dev/null",
3891                                 stage_line_type == LINE_STAT_STAGED ? "-R" : ""))
3892                 return FALSE;
3893
3894         pipe = popen(cmd, "w");
3895         if (!pipe)
3896                 return FALSE;
3897
3898         diff_end = view->line + view->lines;
3899         if (line->type != LINE_DIFF_CHUNK) {
3900                 diff_chunk = diff_hdr;
3901
3902         } else {
3903                 for (diff_chunk = line + 1; diff_chunk < diff_end; diff_chunk++)
3904                         if (diff_chunk->type == LINE_DIFF_CHUNK ||
3905                             diff_chunk->type == LINE_DIFF_HEADER)
3906                                 diff_end = diff_chunk;
3907
3908                 diff_chunk = line;
3909
3910                 while (diff_hdr->type != LINE_DIFF_CHUNK) {
3911                         switch (diff_hdr->type) {
3912                         case LINE_DIFF_HEADER:
3913                         case LINE_DIFF_INDEX:
3914                         case LINE_DIFF_ADD:
3915                         case LINE_DIFF_DEL:
3916                                 break;
3917
3918                         default:
3919                                 diff_hdr++;
3920                                 continue;
3921                         }
3922
3923                         if (!stage_diff_line(pipe, diff_hdr++)) {
3924                                 pclose(pipe);
3925                                 return FALSE;
3926                         }
3927                 }
3928         }
3929
3930         while (diff_chunk < diff_end && stage_diff_line(pipe, diff_chunk))
3931                 diff_chunk++;
3932
3933         pclose(pipe);
3934
3935         if (diff_chunk != diff_end)
3936                 return FALSE;
3937
3938         return TRUE;
3939 }
3940
3941 static void
3942 stage_update(struct view *view, struct line *line)
3943 {
3944         if (stage_line_type != LINE_STAT_UNTRACKED &&
3945             (line->type == LINE_DIFF_CHUNK || !stage_status.status)) {
3946                 if (!stage_update_chunk(view, line)) {
3947                         report("Failed to apply chunk");
3948                         return;
3949                 }
3950
3951         } else if (!status_update_file(view, &stage_status, stage_line_type)) {
3952                 report("Failed to update file");
3953                 return;
3954         }
3955
3956         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
3957
3958         view = VIEW(REQ_VIEW_STATUS);
3959         if (view_is_displayed(view))
3960                 status_enter(view, &view->line[view->lineno]);
3961 }
3962
3963 static enum request
3964 stage_request(struct view *view, enum request request, struct line *line)
3965 {
3966         switch (request) {
3967         case REQ_STATUS_UPDATE:
3968                 stage_update(view, line);
3969                 break;
3970
3971         case REQ_EDIT:
3972                 if (!stage_status.new.name[0])
3973                         return request;
3974
3975                 open_editor(stage_status.status != '?', stage_status.new.name);
3976                 break;
3977
3978         case REQ_ENTER:
3979                 pager_request(view, request, line);
3980                 break;
3981
3982         default:
3983                 return request;
3984         }
3985
3986         return REQ_NONE;
3987 }
3988
3989 static struct view_ops stage_ops = {
3990         "line",
3991         NULL,
3992         pager_read,
3993         pager_draw,
3994         stage_request,
3995         pager_grep,
3996         pager_select,
3997 };
3998
3999
4000 /*
4001  * Revision graph
4002  */
4003
4004 struct commit {
4005         char id[SIZEOF_REV];            /* SHA1 ID. */
4006         char title[128];                /* First line of the commit message. */
4007         char author[75];                /* Author of the commit. */
4008         struct tm time;                 /* Date from the author ident. */
4009         struct ref **refs;              /* Repository references. */
4010         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
4011         size_t graph_size;              /* The width of the graph array. */
4012         bool has_parents;               /* Rewritten --parents seen. */
4013 };
4014
4015 /* Size of rev graph with no  "padding" columns */
4016 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
4017
4018 struct rev_graph {
4019         struct rev_graph *prev, *next, *parents;
4020         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
4021         size_t size;
4022         struct commit *commit;
4023         size_t pos;
4024         unsigned int boundary:1;
4025 };
4026
4027 /* Parents of the commit being visualized. */
4028 static struct rev_graph graph_parents[4];
4029
4030 /* The current stack of revisions on the graph. */
4031 static struct rev_graph graph_stacks[4] = {
4032         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
4033         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
4034         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
4035         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
4036 };
4037
4038 static inline bool
4039 graph_parent_is_merge(struct rev_graph *graph)
4040 {
4041         return graph->parents->size > 1;
4042 }
4043
4044 static inline void
4045 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
4046 {
4047         struct commit *commit = graph->commit;
4048
4049         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
4050                 commit->graph[commit->graph_size++] = symbol;
4051 }
4052
4053 static void
4054 done_rev_graph(struct rev_graph *graph)
4055 {
4056         if (graph_parent_is_merge(graph) &&
4057             graph->pos < graph->size - 1 &&
4058             graph->next->size == graph->size + graph->parents->size - 1) {
4059                 size_t i = graph->pos + graph->parents->size - 1;
4060
4061                 graph->commit->graph_size = i * 2;
4062                 while (i < graph->next->size - 1) {
4063                         append_to_rev_graph(graph, ' ');
4064                         append_to_rev_graph(graph, '\\');
4065                         i++;
4066                 }
4067         }
4068
4069         graph->size = graph->pos = 0;
4070         graph->commit = NULL;
4071         memset(graph->parents, 0, sizeof(*graph->parents));
4072 }
4073
4074 static void
4075 push_rev_graph(struct rev_graph *graph, char *parent)
4076 {
4077         int i;
4078
4079         /* "Collapse" duplicate parents lines.
4080          *
4081          * FIXME: This needs to also update update the drawn graph but
4082          * for now it just serves as a method for pruning graph lines. */
4083         for (i = 0; i < graph->size; i++)
4084                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
4085                         return;
4086
4087         if (graph->size < SIZEOF_REVITEMS) {
4088                 string_copy_rev(graph->rev[graph->size++], parent);
4089         }
4090 }
4091
4092 static chtype
4093 get_rev_graph_symbol(struct rev_graph *graph)
4094 {
4095         chtype symbol;
4096
4097         if (graph->boundary)
4098                 symbol = REVGRAPH_BOUND;
4099         else if (graph->parents->size == 0)
4100                 symbol = REVGRAPH_INIT;
4101         else if (graph_parent_is_merge(graph))
4102                 symbol = REVGRAPH_MERGE;
4103         else if (graph->pos >= graph->size)
4104                 symbol = REVGRAPH_BRANCH;
4105         else
4106                 symbol = REVGRAPH_COMMIT;
4107
4108         return symbol;
4109 }
4110
4111 static void
4112 draw_rev_graph(struct rev_graph *graph)
4113 {
4114         struct rev_filler {
4115                 chtype separator, line;
4116         };
4117         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
4118         static struct rev_filler fillers[] = {
4119                 { ' ',  REVGRAPH_LINE },
4120                 { '`',  '.' },
4121                 { '\'', ' ' },
4122                 { '/',  ' ' },
4123         };
4124         chtype symbol = get_rev_graph_symbol(graph);
4125         struct rev_filler *filler;
4126         size_t i;
4127
4128         filler = &fillers[DEFAULT];
4129
4130         for (i = 0; i < graph->pos; i++) {
4131                 append_to_rev_graph(graph, filler->line);
4132                 if (graph_parent_is_merge(graph->prev) &&
4133                     graph->prev->pos == i)
4134                         filler = &fillers[RSHARP];
4135
4136                 append_to_rev_graph(graph, filler->separator);
4137         }
4138
4139         /* Place the symbol for this revision. */
4140         append_to_rev_graph(graph, symbol);
4141
4142         if (graph->prev->size > graph->size)
4143                 filler = &fillers[RDIAG];
4144         else
4145                 filler = &fillers[DEFAULT];
4146
4147         i++;
4148
4149         for (; i < graph->size; i++) {
4150                 append_to_rev_graph(graph, filler->separator);
4151                 append_to_rev_graph(graph, filler->line);
4152                 if (graph_parent_is_merge(graph->prev) &&
4153                     i < graph->prev->pos + graph->parents->size)
4154                         filler = &fillers[RSHARP];
4155                 if (graph->prev->size > graph->size)
4156                         filler = &fillers[LDIAG];
4157         }
4158
4159         if (graph->prev->size > graph->size) {
4160                 append_to_rev_graph(graph, filler->separator);
4161                 if (filler->line != ' ')
4162                         append_to_rev_graph(graph, filler->line);
4163         }
4164 }
4165
4166 /* Prepare the next rev graph */
4167 static void
4168 prepare_rev_graph(struct rev_graph *graph)
4169 {
4170         size_t i;
4171
4172         /* First, traverse all lines of revisions up to the active one. */
4173         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
4174                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
4175                         break;
4176
4177                 push_rev_graph(graph->next, graph->rev[graph->pos]);
4178         }
4179
4180         /* Interleave the new revision parent(s). */
4181         for (i = 0; !graph->boundary && i < graph->parents->size; i++)
4182                 push_rev_graph(graph->next, graph->parents->rev[i]);
4183
4184         /* Lastly, put any remaining revisions. */
4185         for (i = graph->pos + 1; i < graph->size; i++)
4186                 push_rev_graph(graph->next, graph->rev[i]);
4187 }
4188
4189 static void
4190 update_rev_graph(struct rev_graph *graph)
4191 {
4192         /* If this is the finalizing update ... */
4193         if (graph->commit)
4194                 prepare_rev_graph(graph);
4195
4196         /* Graph visualization needs a one rev look-ahead,
4197          * so the first update doesn't visualize anything. */
4198         if (!graph->prev->commit)
4199                 return;
4200
4201         draw_rev_graph(graph->prev);
4202         done_rev_graph(graph->prev->prev);
4203 }
4204
4205
4206 /*
4207  * Main view backend
4208  */
4209
4210 static bool
4211 main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
4212 {
4213         char buf[DATE_COLS + 1];
4214         struct commit *commit = line->data;
4215         enum line_type type;
4216         int col = 0;
4217         size_t timelen;
4218         int tilde_attr;
4219         int space;
4220
4221         if (!*commit->author)
4222                 return FALSE;
4223
4224         space = view->width;
4225         wmove(view->win, lineno, col);
4226
4227         if (selected) {
4228                 type = LINE_CURSOR;
4229                 wattrset(view->win, get_line_attr(type));
4230                 wchgat(view->win, -1, 0, type, NULL);
4231                 tilde_attr = -1;
4232         } else {
4233                 type = LINE_MAIN_COMMIT;
4234                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
4235                 tilde_attr = get_line_attr(LINE_MAIN_DELIM);
4236         }
4237
4238         if (opt_date) {
4239                 int n;
4240
4241                 timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
4242                 n = draw_text(view, buf, view->width - col, FALSE, tilde_attr);
4243                 draw_text(view, " ", view->width - col - n, FALSE, tilde_attr);
4244
4245                 col += DATE_COLS;
4246                 wmove(view->win, lineno, col);
4247                 if (col >= view->width)
4248                         return TRUE;
4249         }
4250         if (type != LINE_CURSOR)
4251                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
4252
4253         if (opt_author) {
4254                 int max_len;
4255
4256                 max_len = view->width - col;
4257                 if (max_len > AUTHOR_COLS - 1)
4258                         max_len = AUTHOR_COLS - 1;
4259                 draw_text(view, commit->author, max_len, TRUE, tilde_attr);
4260                 col += AUTHOR_COLS;
4261                 if (col >= view->width)
4262                         return TRUE;
4263         }
4264
4265         if (opt_rev_graph && commit->graph_size) {
4266                 size_t graph_size = view->width - col;
4267                 size_t i;
4268
4269                 if (type != LINE_CURSOR)
4270                         wattrset(view->win, get_line_attr(LINE_MAIN_REVGRAPH));
4271                 wmove(view->win, lineno, col);
4272                 if (graph_size > commit->graph_size)
4273                         graph_size = commit->graph_size;
4274                 /* Using waddch() instead of waddnstr() ensures that
4275                  * they'll be rendered correctly for the cursor line. */
4276                 for (i = 0; i < graph_size; i++)
4277                         waddch(view->win, commit->graph[i]);
4278
4279                 col += commit->graph_size + 1;
4280                 if (col >= view->width)
4281                         return TRUE;
4282                 waddch(view->win, ' ');
4283         }
4284         if (type != LINE_CURSOR)
4285                 wattrset(view->win, A_NORMAL);
4286
4287         wmove(view->win, lineno, col);
4288
4289         if (opt_show_refs && commit->refs) {
4290                 size_t i = 0;
4291
4292                 do {
4293                         if (type == LINE_CURSOR)
4294                                 ;
4295                         else if (commit->refs[i]->ltag)
4296                                 wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG));
4297                         else if (commit->refs[i]->tag)
4298                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
4299                         else if (commit->refs[i]->remote)
4300                                 wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
4301                         else
4302                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
4303
4304                         col += draw_text(view, "[", view->width - col, TRUE, tilde_attr);
4305                         col += draw_text(view, commit->refs[i]->name, view->width - col,
4306                                          TRUE, tilde_attr);
4307                         col += draw_text(view, "]", view->width - col, TRUE, tilde_attr);
4308                         if (type != LINE_CURSOR)
4309                                 wattrset(view->win, A_NORMAL);
4310                         col += draw_text(view, " ", view->width - col, TRUE, tilde_attr);
4311                         if (col >= view->width)
4312                                 return TRUE;
4313                 } while (commit->refs[i++]->next);
4314         }
4315
4316         if (type != LINE_CURSOR)
4317                 wattrset(view->win, get_line_attr(type));
4318
4319         draw_text(view, commit->title, view->width - col, TRUE, tilde_attr);
4320         return TRUE;
4321 }
4322
4323 /* Reads git log --pretty=raw output and parses it into the commit struct. */
4324 static bool
4325 main_read(struct view *view, char *line)
4326 {
4327         static struct rev_graph *graph = graph_stacks;
4328         enum line_type type;
4329         struct commit *commit;
4330
4331         if (!line) {
4332                 update_rev_graph(graph);
4333                 return TRUE;
4334         }
4335
4336         type = get_line_type(line);
4337         if (type == LINE_COMMIT) {
4338                 commit = calloc(1, sizeof(struct commit));
4339                 if (!commit)
4340                         return FALSE;
4341
4342                 line += STRING_SIZE("commit ");
4343                 if (*line == '-') {
4344                         graph->boundary = 1;
4345                         line++;
4346                 }
4347
4348                 string_copy_rev(commit->id, line);
4349                 commit->refs = get_refs(commit->id);
4350                 graph->commit = commit;
4351                 add_line_data(view, commit, LINE_MAIN_COMMIT);
4352
4353                 while ((line = strchr(line, ' '))) {
4354                         line++;
4355                         push_rev_graph(graph->parents, line);
4356                         commit->has_parents = TRUE;
4357                 }
4358                 return TRUE;
4359         }
4360
4361         if (!view->lines)
4362                 return TRUE;
4363         commit = view->line[view->lines - 1].data;
4364
4365         switch (type) {
4366         case LINE_PARENT:
4367                 if (commit->has_parents)
4368                         break;
4369                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
4370                 break;
4371
4372         case LINE_AUTHOR:
4373         {
4374                 /* Parse author lines where the name may be empty:
4375                  *      author  <email@address.tld> 1138474660 +0100
4376                  */
4377                 char *ident = line + STRING_SIZE("author ");
4378                 char *nameend = strchr(ident, '<');
4379                 char *emailend = strchr(ident, '>');
4380
4381                 if (!nameend || !emailend)
4382                         break;
4383
4384                 update_rev_graph(graph);
4385                 graph = graph->next;
4386
4387                 *nameend = *emailend = 0;
4388                 ident = chomp_string(ident);
4389                 if (!*ident) {
4390                         ident = chomp_string(nameend + 1);
4391                         if (!*ident)
4392                                 ident = "Unknown";
4393                 }
4394
4395                 string_ncopy(commit->author, ident, strlen(ident));
4396
4397                 /* Parse epoch and timezone */
4398                 if (emailend[1] == ' ') {
4399                         char *secs = emailend + 2;
4400                         char *zone = strchr(secs, ' ');
4401                         time_t time = (time_t) atol(secs);
4402
4403                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
4404                                 long tz;
4405
4406                                 zone++;
4407                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
4408                                 tz += ('0' - zone[2]) * 60 * 60;
4409                                 tz += ('0' - zone[3]) * 60;
4410                                 tz += ('0' - zone[4]) * 60;
4411
4412                                 if (zone[0] == '-')
4413                                         tz = -tz;
4414
4415                                 time -= tz;
4416                         }
4417
4418                         gmtime_r(&time, &commit->time);
4419                 }
4420                 break;
4421         }
4422         default:
4423                 /* Fill in the commit title if it has not already been set. */
4424                 if (commit->title[0])
4425                         break;
4426
4427                 /* Require titles to start with a non-space character at the
4428                  * offset used by git log. */
4429                 if (strncmp(line, "    ", 4))
4430                         break;
4431                 line += 4;
4432                 /* Well, if the title starts with a whitespace character,
4433                  * try to be forgiving.  Otherwise we end up with no title. */
4434                 while (isspace(*line))
4435                         line++;
4436                 if (*line == '\0')
4437                         break;
4438                 /* FIXME: More graceful handling of titles; append "..." to
4439                  * shortened titles, etc. */
4440
4441                 string_ncopy(commit->title, line, strlen(line));
4442         }
4443
4444         return TRUE;
4445 }
4446
4447 static enum request
4448 main_request(struct view *view, enum request request, struct line *line)
4449 {
4450         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4451
4452         if (request == REQ_ENTER)
4453                 open_view(view, REQ_VIEW_DIFF, flags);
4454         else
4455                 return request;
4456
4457         return REQ_NONE;
4458 }
4459
4460 static bool
4461 main_grep(struct view *view, struct line *line)
4462 {
4463         struct commit *commit = line->data;
4464         enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
4465         char buf[DATE_COLS + 1];
4466         regmatch_t pmatch;
4467
4468         for (state = S_TITLE; state < S_END; state++) {
4469                 char *text;
4470
4471                 switch (state) {
4472                 case S_TITLE:   text = commit->title;   break;
4473                 case S_AUTHOR:  text = commit->author;  break;
4474                 case S_DATE:
4475                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
4476                                 continue;
4477                         text = buf;
4478                         break;
4479
4480                 default:
4481                         return FALSE;
4482                 }
4483
4484                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4485                         return TRUE;
4486         }
4487
4488         return FALSE;
4489 }
4490
4491 static void
4492 main_select(struct view *view, struct line *line)
4493 {
4494         struct commit *commit = line->data;
4495
4496         string_copy_rev(view->ref, commit->id);
4497         string_copy_rev(ref_commit, view->ref);
4498 }
4499
4500 static struct view_ops main_ops = {
4501         "commit",
4502         NULL,
4503         main_read,
4504         main_draw,
4505         main_request,
4506         main_grep,
4507         main_select,
4508 };
4509
4510
4511 /*
4512  * Unicode / UTF-8 handling
4513  *
4514  * NOTE: Much of the following code for dealing with unicode is derived from
4515  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
4516  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
4517  */
4518
4519 /* I've (over)annotated a lot of code snippets because I am not entirely
4520  * confident that the approach taken by this small UTF-8 interface is correct.
4521  * --jonas */
4522
4523 static inline int
4524 unicode_width(unsigned long c)
4525 {
4526         if (c >= 0x1100 &&
4527            (c <= 0x115f                         /* Hangul Jamo */
4528             || c == 0x2329
4529             || c == 0x232a
4530             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
4531                                                 /* CJK ... Yi */
4532             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
4533             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
4534             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
4535             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
4536             || (c >= 0xffe0  && c <= 0xffe6)
4537             || (c >= 0x20000 && c <= 0x2fffd)
4538             || (c >= 0x30000 && c <= 0x3fffd)))
4539                 return 2;
4540
4541         if (c == '\t')
4542                 return opt_tab_size;
4543
4544         return 1;
4545 }
4546
4547 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
4548  * Illegal bytes are set one. */
4549 static const unsigned char utf8_bytes[256] = {
4550         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
4551         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,
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         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,
4557         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,
4558 };
4559
4560 /* Decode UTF-8 multi-byte representation into a unicode character. */
4561 static inline unsigned long
4562 utf8_to_unicode(const char *string, size_t length)
4563 {
4564         unsigned long unicode;
4565
4566         switch (length) {
4567         case 1:
4568                 unicode  =   string[0];
4569                 break;
4570         case 2:
4571                 unicode  =  (string[0] & 0x1f) << 6;
4572                 unicode +=  (string[1] & 0x3f);
4573                 break;
4574         case 3:
4575                 unicode  =  (string[0] & 0x0f) << 12;
4576                 unicode += ((string[1] & 0x3f) << 6);
4577                 unicode +=  (string[2] & 0x3f);
4578                 break;
4579         case 4:
4580                 unicode  =  (string[0] & 0x0f) << 18;
4581                 unicode += ((string[1] & 0x3f) << 12);
4582                 unicode += ((string[2] & 0x3f) << 6);
4583                 unicode +=  (string[3] & 0x3f);
4584                 break;
4585         case 5:
4586                 unicode  =  (string[0] & 0x0f) << 24;
4587                 unicode += ((string[1] & 0x3f) << 18);
4588                 unicode += ((string[2] & 0x3f) << 12);
4589                 unicode += ((string[3] & 0x3f) << 6);
4590                 unicode +=  (string[4] & 0x3f);
4591                 break;
4592         case 6:
4593                 unicode  =  (string[0] & 0x01) << 30;
4594                 unicode += ((string[1] & 0x3f) << 24);
4595                 unicode += ((string[2] & 0x3f) << 18);
4596                 unicode += ((string[3] & 0x3f) << 12);
4597                 unicode += ((string[4] & 0x3f) << 6);
4598                 unicode +=  (string[5] & 0x3f);
4599                 break;
4600         default:
4601                 die("Invalid unicode length");
4602         }
4603
4604         /* Invalid characters could return the special 0xfffd value but NUL
4605          * should be just as good. */
4606         return unicode > 0xffff ? 0 : unicode;
4607 }
4608
4609 /* Calculates how much of string can be shown within the given maximum width
4610  * and sets trimmed parameter to non-zero value if all of string could not be
4611  * shown. If the reserve flag is TRUE, it will reserve at least one
4612  * trailing character, which can be useful when drawing a delimiter.
4613  *
4614  * Returns the number of bytes to output from string to satisfy max_width. */
4615 static size_t
4616 utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve)
4617 {
4618         const char *start = string;
4619         const char *end = strchr(string, '\0');
4620         unsigned char last_bytes = 0;
4621         size_t width = 0;
4622
4623         *trimmed = 0;
4624
4625         while (string < end) {
4626                 int c = *(unsigned char *) string;
4627                 unsigned char bytes = utf8_bytes[c];
4628                 size_t ucwidth;
4629                 unsigned long unicode;
4630
4631                 if (string + bytes > end)
4632                         break;
4633
4634                 /* Change representation to figure out whether
4635                  * it is a single- or double-width character. */
4636
4637                 unicode = utf8_to_unicode(string, bytes);
4638                 /* FIXME: Graceful handling of invalid unicode character. */
4639                 if (!unicode)
4640                         break;
4641
4642                 ucwidth = unicode_width(unicode);
4643                 width  += ucwidth;
4644                 if (width > max_width) {
4645                         *trimmed = 1;
4646                         if (reserve && width - ucwidth == max_width) {
4647                                 string -= last_bytes;
4648                         }
4649                         break;
4650                 }
4651
4652                 string  += bytes;
4653                 last_bytes = bytes;
4654         }
4655
4656         return string - start;
4657 }
4658
4659
4660 /*
4661  * Status management
4662  */
4663
4664 /* Whether or not the curses interface has been initialized. */
4665 static bool cursed = FALSE;
4666
4667 /* The status window is used for polling keystrokes. */
4668 static WINDOW *status_win;
4669
4670 static bool status_empty = TRUE;
4671
4672 /* Update status and title window. */
4673 static void
4674 report(const char *msg, ...)
4675 {
4676         struct view *view = display[current_view];
4677
4678         if (input_mode)
4679                 return;
4680
4681         if (!view) {
4682                 char buf[SIZEOF_STR];
4683                 va_list args;
4684
4685                 va_start(args, msg);
4686                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
4687                         buf[sizeof(buf) - 1] = 0;
4688                         buf[sizeof(buf) - 2] = '.';
4689                         buf[sizeof(buf) - 3] = '.';
4690                         buf[sizeof(buf) - 4] = '.';
4691                 }
4692                 va_end(args);
4693                 die("%s", buf);
4694         }
4695
4696         if (!status_empty || *msg) {
4697                 va_list args;
4698
4699                 va_start(args, msg);
4700
4701                 wmove(status_win, 0, 0);
4702                 if (*msg) {
4703                         vwprintw(status_win, msg, args);
4704                         status_empty = FALSE;
4705                 } else {
4706                         status_empty = TRUE;
4707                 }
4708                 wclrtoeol(status_win);
4709                 wrefresh(status_win);
4710
4711                 va_end(args);
4712         }
4713
4714         update_view_title(view);
4715         update_display_cursor(view);
4716 }
4717
4718 /* Controls when nodelay should be in effect when polling user input. */
4719 static void
4720 set_nonblocking_input(bool loading)
4721 {
4722         static unsigned int loading_views;
4723
4724         if ((loading == FALSE && loading_views-- == 1) ||
4725             (loading == TRUE  && loading_views++ == 0))
4726                 nodelay(status_win, loading);
4727 }
4728
4729 static void
4730 init_display(void)
4731 {
4732         int x, y;
4733
4734         /* Initialize the curses library */
4735         if (isatty(STDIN_FILENO)) {
4736                 cursed = !!initscr();
4737         } else {
4738                 /* Leave stdin and stdout alone when acting as a pager. */
4739                 FILE *io = fopen("/dev/tty", "r+");
4740
4741                 if (!io)
4742                         die("Failed to open /dev/tty");
4743                 cursed = !!newterm(NULL, io, io);
4744         }
4745
4746         if (!cursed)
4747                 die("Failed to initialize curses");
4748
4749         nonl();         /* Tell curses not to do NL->CR/NL on output */
4750         cbreak();       /* Take input chars one at a time, no wait for \n */
4751         noecho();       /* Don't echo input */
4752         leaveok(stdscr, TRUE);
4753
4754         if (has_colors())
4755                 init_colors();
4756
4757         getmaxyx(stdscr, y, x);
4758         status_win = newwin(1, 0, y - 1, 0);
4759         if (!status_win)
4760                 die("Failed to create status window");
4761
4762         /* Enable keyboard mapping */
4763         keypad(status_win, TRUE);
4764         wbkgdset(status_win, get_line_attr(LINE_STATUS));
4765 }
4766
4767 static char *
4768 read_prompt(const char *prompt)
4769 {
4770         enum { READING, STOP, CANCEL } status = READING;
4771         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
4772         int pos = 0;
4773
4774         while (status == READING) {
4775                 struct view *view;
4776                 int i, key;
4777
4778                 input_mode = TRUE;
4779
4780                 foreach_view (view, i)
4781                         update_view(view);
4782
4783                 input_mode = FALSE;
4784
4785                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
4786                 wclrtoeol(status_win);
4787
4788                 /* Refresh, accept single keystroke of input */
4789                 key = wgetch(status_win);
4790                 switch (key) {
4791                 case KEY_RETURN:
4792                 case KEY_ENTER:
4793                 case '\n':
4794                         status = pos ? STOP : CANCEL;
4795                         break;
4796
4797                 case KEY_BACKSPACE:
4798                         if (pos > 0)
4799                                 pos--;
4800                         else
4801                                 status = CANCEL;
4802                         break;
4803
4804                 case KEY_ESC:
4805                         status = CANCEL;
4806                         break;
4807
4808                 case ERR:
4809                         break;
4810
4811                 default:
4812                         if (pos >= sizeof(buf)) {
4813                                 report("Input string too long");
4814                                 return NULL;
4815                         }
4816
4817                         if (isprint(key))
4818                                 buf[pos++] = (char) key;
4819                 }
4820         }
4821
4822         /* Clear the status window */
4823         status_empty = FALSE;
4824         report("");
4825
4826         if (status == CANCEL)
4827                 return NULL;
4828
4829         buf[pos++] = 0;
4830
4831         return buf;
4832 }
4833
4834 /*
4835  * Repository references
4836  */
4837
4838 static struct ref *refs = NULL;
4839 static size_t refs_alloc = 0;
4840 static size_t refs_size = 0;
4841
4842 /* Id <-> ref store */
4843 static struct ref ***id_refs = NULL;
4844 static size_t id_refs_alloc = 0;
4845 static size_t id_refs_size = 0;
4846
4847 static struct ref **
4848 get_refs(char *id)
4849 {
4850         struct ref ***tmp_id_refs;
4851         struct ref **ref_list = NULL;
4852         size_t ref_list_alloc = 0;
4853         size_t ref_list_size = 0;
4854         size_t i;
4855
4856         for (i = 0; i < id_refs_size; i++)
4857                 if (!strcmp(id, id_refs[i][0]->id))
4858                         return id_refs[i];
4859
4860         tmp_id_refs = realloc_items(id_refs, &id_refs_alloc, id_refs_size + 1,
4861                                     sizeof(*id_refs));
4862         if (!tmp_id_refs)
4863                 return NULL;
4864
4865         id_refs = tmp_id_refs;
4866
4867         for (i = 0; i < refs_size; i++) {
4868                 struct ref **tmp;
4869
4870                 if (strcmp(id, refs[i].id))
4871                         continue;
4872
4873                 tmp = realloc_items(ref_list, &ref_list_alloc,
4874                                     ref_list_size + 1, sizeof(*ref_list));
4875                 if (!tmp) {
4876                         if (ref_list)
4877                                 free(ref_list);
4878                         return NULL;
4879                 }
4880
4881                 ref_list = tmp;
4882                 if (ref_list_size > 0)
4883                         ref_list[ref_list_size - 1]->next = 1;
4884                 ref_list[ref_list_size] = &refs[i];
4885
4886                 /* XXX: The properties of the commit chains ensures that we can
4887                  * safely modify the shared ref. The repo references will
4888                  * always be similar for the same id. */
4889                 ref_list[ref_list_size]->next = 0;
4890                 ref_list_size++;
4891         }
4892
4893         if (ref_list)
4894                 id_refs[id_refs_size++] = ref_list;
4895
4896         return ref_list;
4897 }
4898
4899 static int
4900 read_ref(char *id, size_t idlen, char *name, size_t namelen)
4901 {
4902         struct ref *ref;
4903         bool tag = FALSE;
4904         bool ltag = FALSE;
4905         bool remote = FALSE;
4906         bool check_replace = FALSE;
4907
4908         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
4909                 if (!strcmp(name + namelen - 3, "^{}")) {
4910                         namelen -= 3;
4911                         name[namelen] = 0;
4912                         if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
4913                                 check_replace = TRUE;
4914                 } else {
4915                         ltag = TRUE;
4916                 }
4917
4918                 tag = TRUE;
4919                 namelen -= STRING_SIZE("refs/tags/");
4920                 name    += STRING_SIZE("refs/tags/");
4921
4922         } else if (!strncmp(name, "refs/remotes/", STRING_SIZE("refs/remotes/"))) {
4923                 remote = TRUE;
4924                 namelen -= STRING_SIZE("refs/remotes/");
4925                 name    += STRING_SIZE("refs/remotes/");
4926
4927         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
4928                 namelen -= STRING_SIZE("refs/heads/");
4929                 name    += STRING_SIZE("refs/heads/");
4930
4931         } else if (!strcmp(name, "HEAD")) {
4932                 return OK;
4933         }
4934
4935         if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
4936                 /* it's an annotated tag, replace the previous sha1 with the
4937                  * resolved commit id; relies on the fact git-ls-remote lists
4938                  * the commit id of an annotated tag right beofre the commit id
4939                  * it points to. */
4940                 refs[refs_size - 1].ltag = ltag;
4941                 string_copy_rev(refs[refs_size - 1].id, id);
4942
4943                 return OK;
4944         }
4945         refs = realloc_items(refs, &refs_alloc, refs_size + 1, sizeof(*refs));
4946         if (!refs)
4947                 return ERR;
4948
4949         ref = &refs[refs_size++];
4950         ref->name = malloc(namelen + 1);
4951         if (!ref->name)
4952                 return ERR;
4953
4954         strncpy(ref->name, name, namelen);
4955         ref->name[namelen] = 0;
4956         ref->tag = tag;
4957         ref->ltag = ltag;
4958         ref->remote = remote;
4959         string_copy_rev(ref->id, id);
4960
4961         return OK;
4962 }
4963
4964 static int
4965 load_refs(void)
4966 {
4967         const char *cmd_env = getenv("TIG_LS_REMOTE");
4968         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
4969
4970         return read_properties(popen(cmd, "r"), "\t", read_ref);
4971 }
4972
4973 static int
4974 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
4975 {
4976         if (!strcmp(name, "i18n.commitencoding"))
4977                 string_ncopy(opt_encoding, value, valuelen);
4978
4979         if (!strcmp(name, "core.editor"))
4980                 string_ncopy(opt_editor, value, valuelen);
4981
4982         return OK;
4983 }
4984
4985 static int
4986 load_repo_config(void)
4987 {
4988         return read_properties(popen(GIT_CONFIG " --list", "r"),
4989                                "=", read_repo_config_option);
4990 }
4991
4992 static int
4993 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
4994 {
4995         if (!opt_git_dir[0]) {
4996                 string_ncopy(opt_git_dir, name, namelen);
4997
4998         } else if (opt_is_inside_work_tree == -1) {
4999                 /* This can be 3 different values depending on the
5000                  * version of git being used. If git-rev-parse does not
5001                  * understand --is-inside-work-tree it will simply echo
5002                  * the option else either "true" or "false" is printed.
5003                  * Default to true for the unknown case. */
5004                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
5005
5006         } else {
5007                 string_ncopy(opt_cdup, name, namelen);
5008         }
5009
5010         return OK;
5011 }
5012
5013 /* XXX: The line outputted by "--show-cdup" can be empty so the option
5014  * must be the last one! */
5015 static int
5016 load_repo_info(void)
5017 {
5018         return read_properties(popen("git rev-parse --git-dir --is-inside-work-tree --show-cdup 2>/dev/null", "r"),
5019                                "=", read_repo_info);
5020 }
5021
5022 static int
5023 read_properties(FILE *pipe, const char *separators,
5024                 int (*read_property)(char *, size_t, char *, size_t))
5025 {
5026         char buffer[BUFSIZ];
5027         char *name;
5028         int state = OK;
5029
5030         if (!pipe)
5031                 return ERR;
5032
5033         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
5034                 char *value;
5035                 size_t namelen;
5036                 size_t valuelen;
5037
5038                 name = chomp_string(name);
5039                 namelen = strcspn(name, separators);
5040
5041                 if (name[namelen]) {
5042                         name[namelen] = 0;
5043                         value = chomp_string(name + namelen + 1);
5044                         valuelen = strlen(value);
5045
5046                 } else {
5047                         value = "";
5048                         valuelen = 0;
5049                 }
5050
5051                 state = read_property(name, namelen, value, valuelen);
5052         }
5053
5054         if (state != ERR && ferror(pipe))
5055                 state = ERR;
5056
5057         pclose(pipe);
5058
5059         return state;
5060 }
5061
5062
5063 /*
5064  * Main
5065  */
5066
5067 static void __NORETURN
5068 quit(int sig)
5069 {
5070         /* XXX: Restore tty modes and let the OS cleanup the rest! */
5071         if (cursed)
5072                 endwin();
5073         exit(0);
5074 }
5075
5076 static void __NORETURN
5077 die(const char *err, ...)
5078 {
5079         va_list args;
5080
5081         endwin();
5082
5083         va_start(args, err);
5084         fputs("tig: ", stderr);
5085         vfprintf(stderr, err, args);
5086         fputs("\n", stderr);
5087         va_end(args);
5088
5089         exit(1);
5090 }
5091
5092 static void
5093 warn(const char *msg, ...)
5094 {
5095         va_list args;
5096
5097         va_start(args, msg);
5098         fputs("tig warning: ", stderr);
5099         vfprintf(stderr, msg, args);
5100         fputs("\n", stderr);
5101         va_end(args);
5102 }
5103
5104 int
5105 main(int argc, char *argv[])
5106 {
5107         struct view *view;
5108         enum request request;
5109         size_t i;
5110
5111         signal(SIGINT, quit);
5112
5113         if (setlocale(LC_ALL, "")) {
5114                 char *codeset = nl_langinfo(CODESET);
5115
5116                 string_ncopy(opt_codeset, codeset, strlen(codeset));
5117         }
5118
5119         if (load_repo_info() == ERR)
5120                 die("Failed to load repo info.");
5121
5122         if (load_options() == ERR)
5123                 die("Failed to load user config.");
5124
5125         /* Load the repo config file so options can be overwritten from
5126          * the command line. */
5127         if (load_repo_config() == ERR)
5128                 die("Failed to load repo config.");
5129
5130         if (!parse_options(argc, argv))
5131                 return 0;
5132
5133         /* Require a git repository unless when running in pager mode. */
5134         if (!opt_git_dir[0])
5135                 die("Not a git repository");
5136
5137         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
5138                 opt_utf8 = FALSE;
5139
5140         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
5141                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
5142                 if (opt_iconv == ICONV_NONE)
5143                         die("Failed to initialize character set conversion");
5144         }
5145
5146         if (load_refs() == ERR)
5147                 die("Failed to load refs.");
5148
5149         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
5150                 view->cmd_env = getenv(view->cmd_env);
5151
5152         request = opt_request;
5153
5154         init_display();
5155
5156         while (view_driver(display[current_view], request)) {
5157                 int key;
5158                 int i;
5159
5160                 foreach_view (view, i)
5161                         update_view(view);
5162
5163                 /* Refresh, accept single keystroke of input */
5164                 key = wgetch(status_win);
5165
5166                 /* wgetch() with nodelay() enabled returns ERR when there's no
5167                  * input. */
5168                 if (key == ERR) {
5169                         request = REQ_NONE;
5170                         continue;
5171                 }
5172
5173                 request = get_keybinding(display[current_view]->keymap, key);
5174
5175                 /* Some low-level request handling. This keeps access to
5176                  * status_win restricted. */
5177                 switch (request) {
5178                 case REQ_PROMPT:
5179                 {
5180                         char *cmd = read_prompt(":");
5181
5182                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
5183                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
5184                                         opt_request = REQ_VIEW_DIFF;
5185                                 } else {
5186                                         opt_request = REQ_VIEW_PAGER;
5187                                 }
5188                                 break;
5189                         }
5190
5191                         request = REQ_NONE;
5192                         break;
5193                 }
5194                 case REQ_SEARCH:
5195                 case REQ_SEARCH_BACK:
5196                 {
5197                         const char *prompt = request == REQ_SEARCH
5198                                            ? "/" : "?";
5199                         char *search = read_prompt(prompt);
5200
5201                         if (search)
5202                                 string_ncopy(opt_search, search, strlen(search));
5203                         else
5204                                 request = REQ_NONE;
5205                         break;
5206                 }
5207                 case REQ_SCREEN_RESIZE:
5208                 {
5209                         int height, width;
5210
5211                         getmaxyx(stdscr, height, width);
5212
5213                         /* Resize the status view and let the view driver take
5214                          * care of resizing the displayed views. */
5215                         wresize(status_win, 1, width);
5216                         mvwin(status_win, height - 1, 0);
5217                         wrefresh(status_win);
5218                         break;
5219                 }
5220                 default:
5221                         break;
5222                 }
5223         }
5224
5225         quit(0);
5226
5227         return 0;
5228 }