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