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