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