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