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