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