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