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