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