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