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