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