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