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