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