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