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