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