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