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