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