IO API: obsolete opt_cmd by using prepare_update in status_enter
[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->dir && *io->dir &&
423             !string_format_from(buf, &bufpos, "cd %s;", io->dir))
424                 return FALSE;
425
426         if (!string_format_from(buf, &bufpos, "%s", io->sh))
427                 return FALSE;
428
429         if (io->type == IO_FG)
430                 return system(buf) == 0;
431
432         io->pipe = popen(io->sh, io->type == IO_RD ? "r" : "w");
433         return io->pipe != NULL;
434 }
435
436 static int
437 run_io_do(struct io *io)
438 {
439         return start_io(io) && done_io(io);
440 }
441
442 static bool
443 run_io_fg(const char **argv, const char *dir)
444 {
445         struct io io = {};
446
447         init_io(&io, dir, IO_FG);
448         if (!format_command(io.sh, argv, FORMAT_NONE))
449                 return FALSE;
450         return run_io_do(&io);
451 }
452
453 static bool
454 run_io_rd(struct io *io, const char **argv, enum format_flags flags)
455 {
456         return init_io_rd(io, argv, NULL, flags) && start_io(io);
457 }
458
459 static bool
460 io_eof(struct io *io)
461 {
462         return feof(io->pipe);
463 }
464
465 static int
466 io_error(struct io *io)
467 {
468         return io->error;
469 }
470
471 static bool
472 io_strerror(struct io *io)
473 {
474         return strerror(io->error);
475 }
476
477 static char *
478 io_gets(struct io *io)
479 {
480         if (!io->buf) {
481                 io->buf = malloc(BUFSIZ);
482                 if (!io->buf)
483                         return NULL;
484                 io->bufalloc = BUFSIZ;
485         }
486
487         if (!fgets(io->buf, io->bufalloc, io->pipe)) {
488                 if (ferror(io->pipe))
489                         io->error = errno;
490                 return NULL;
491         }
492
493         return io->buf;
494 }
495
496 static bool
497 run_io_buf(const char **argv, char buf[], size_t bufsize)
498 {
499         struct io io = {};
500         bool error;
501
502         if (!run_io_rd(&io, argv, FORMAT_NONE))
503                 return FALSE;
504
505         io.buf = buf;
506         io.bufalloc = bufsize;
507         error = !io_gets(&io) && io_error(&io);
508         io.buf = NULL;
509
510         return done_io(&io) || error;
511 }
512
513
514 /*
515  * User requests
516  */
517
518 #define REQ_INFO \
519         /* XXX: Keep the view request first and in sync with views[]. */ \
520         REQ_GROUP("View switching") \
521         REQ_(VIEW_MAIN,         "Show main view"), \
522         REQ_(VIEW_DIFF,         "Show diff view"), \
523         REQ_(VIEW_LOG,          "Show log view"), \
524         REQ_(VIEW_TREE,         "Show tree view"), \
525         REQ_(VIEW_BLOB,         "Show blob view"), \
526         REQ_(VIEW_BLAME,        "Show blame view"), \
527         REQ_(VIEW_HELP,         "Show help page"), \
528         REQ_(VIEW_PAGER,        "Show pager view"), \
529         REQ_(VIEW_STATUS,       "Show status view"), \
530         REQ_(VIEW_STAGE,        "Show stage view"), \
531         \
532         REQ_GROUP("View manipulation") \
533         REQ_(ENTER,             "Enter current line and scroll"), \
534         REQ_(NEXT,              "Move to next"), \
535         REQ_(PREVIOUS,          "Move to previous"), \
536         REQ_(VIEW_NEXT,         "Move focus to next view"), \
537         REQ_(REFRESH,           "Reload and refresh"), \
538         REQ_(MAXIMIZE,          "Maximize the current view"), \
539         REQ_(VIEW_CLOSE,        "Close the current view"), \
540         REQ_(QUIT,              "Close all views and quit"), \
541         \
542         REQ_GROUP("View specific requests") \
543         REQ_(STATUS_UPDATE,     "Update file status"), \
544         REQ_(STATUS_REVERT,     "Revert file changes"), \
545         REQ_(STATUS_MERGE,      "Merge file using external tool"), \
546         REQ_(STAGE_NEXT,        "Find next chunk to stage"), \
547         REQ_(TREE_PARENT,       "Switch to parent directory in tree view"), \
548         \
549         REQ_GROUP("Cursor navigation") \
550         REQ_(MOVE_UP,           "Move cursor one line up"), \
551         REQ_(MOVE_DOWN,         "Move cursor one line down"), \
552         REQ_(MOVE_PAGE_DOWN,    "Move cursor one page down"), \
553         REQ_(MOVE_PAGE_UP,      "Move cursor one page up"), \
554         REQ_(MOVE_FIRST_LINE,   "Move cursor to first line"), \
555         REQ_(MOVE_LAST_LINE,    "Move cursor to last line"), \
556         \
557         REQ_GROUP("Scrolling") \
558         REQ_(SCROLL_LINE_UP,    "Scroll one line up"), \
559         REQ_(SCROLL_LINE_DOWN,  "Scroll one line down"), \
560         REQ_(SCROLL_PAGE_UP,    "Scroll one page up"), \
561         REQ_(SCROLL_PAGE_DOWN,  "Scroll one page down"), \
562         \
563         REQ_GROUP("Searching") \
564         REQ_(SEARCH,            "Search the view"), \
565         REQ_(SEARCH_BACK,       "Search backwards in the view"), \
566         REQ_(FIND_NEXT,         "Find next search match"), \
567         REQ_(FIND_PREV,         "Find previous search match"), \
568         \
569         REQ_GROUP("Option manipulation") \
570         REQ_(TOGGLE_LINENO,     "Toggle line numbers"), \
571         REQ_(TOGGLE_DATE,       "Toggle date display"), \
572         REQ_(TOGGLE_AUTHOR,     "Toggle author display"), \
573         REQ_(TOGGLE_REV_GRAPH,  "Toggle revision graph visualization"), \
574         REQ_(TOGGLE_REFS,       "Toggle reference display (tags/branches)"), \
575         \
576         REQ_GROUP("Misc") \
577         REQ_(PROMPT,            "Bring up the prompt"), \
578         REQ_(SCREEN_REDRAW,     "Redraw the screen"), \
579         REQ_(SCREEN_RESIZE,     "Resize the screen"), \
580         REQ_(SHOW_VERSION,      "Show version information"), \
581         REQ_(STOP_LOADING,      "Stop all loading views"), \
582         REQ_(EDIT,              "Open in editor"), \
583         REQ_(NONE,              "Do nothing")
584
585
586 /* User action requests. */
587 enum request {
588 #define REQ_GROUP(help)
589 #define REQ_(req, help) REQ_##req
590
591         /* Offset all requests to avoid conflicts with ncurses getch values. */
592         REQ_OFFSET = KEY_MAX + 1,
593         REQ_INFO
594
595 #undef  REQ_GROUP
596 #undef  REQ_
597 };
598
599 struct request_info {
600         enum request request;
601         const char *name;
602         int namelen;
603         const char *help;
604 };
605
606 static struct request_info req_info[] = {
607 #define REQ_GROUP(help) { 0, NULL, 0, (help) },
608 #define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) }
609         REQ_INFO
610 #undef  REQ_GROUP
611 #undef  REQ_
612 };
613
614 static enum request
615 get_request(const char *name)
616 {
617         int namelen = strlen(name);
618         int i;
619
620         for (i = 0; i < ARRAY_SIZE(req_info); i++)
621                 if (req_info[i].namelen == namelen &&
622                     !string_enum_compare(req_info[i].name, name, namelen))
623                         return req_info[i].request;
624
625         return REQ_NONE;
626 }
627
628
629 /*
630  * Options
631  */
632
633 static const char usage[] =
634 "tig " TIG_VERSION " (" __DATE__ ")\n"
635 "\n"
636 "Usage: tig        [options] [revs] [--] [paths]\n"
637 "   or: tig show   [options] [revs] [--] [paths]\n"
638 "   or: tig blame  [rev] path\n"
639 "   or: tig status\n"
640 "   or: tig <      [git command output]\n"
641 "\n"
642 "Options:\n"
643 "  -v, --version   Show version and exit\n"
644 "  -h, --help      Show help message and exit";
645
646 /* Option and state variables. */
647 static bool opt_date                    = TRUE;
648 static bool opt_author                  = TRUE;
649 static bool opt_line_number             = FALSE;
650 static bool opt_line_graphics           = TRUE;
651 static bool opt_rev_graph               = FALSE;
652 static bool opt_show_refs               = TRUE;
653 static int opt_num_interval             = NUMBER_INTERVAL;
654 static int opt_tab_size                 = TAB_SIZE;
655 static int opt_author_cols              = AUTHOR_COLS-1;
656 static char opt_path[SIZEOF_STR]        = "";
657 static char opt_file[SIZEOF_STR]        = "";
658 static char opt_ref[SIZEOF_REF]         = "";
659 static char opt_head[SIZEOF_REF]        = "";
660 static char opt_head_rev[SIZEOF_REV]    = "";
661 static char opt_remote[SIZEOF_REF]      = "";
662 static FILE *opt_pipe                   = NULL;
663 static char opt_encoding[20]            = "UTF-8";
664 static bool opt_utf8                    = TRUE;
665 static char opt_codeset[20]             = "UTF-8";
666 static iconv_t opt_iconv                = ICONV_NONE;
667 static char opt_search[SIZEOF_STR]      = "";
668 static char opt_cdup[SIZEOF_STR]        = "";
669 static char opt_git_dir[SIZEOF_STR]     = "";
670 static signed char opt_is_inside_work_tree      = -1; /* set to TRUE or FALSE */
671 static char opt_editor[SIZEOF_STR]      = "";
672 static FILE *opt_tty                    = NULL;
673
674 #define is_initial_commit()     (!*opt_head_rev)
675 #define is_head_commit(rev)     (!strcmp((rev), "HEAD") || !strcmp(opt_head_rev, (rev)))
676
677 static enum request
678 parse_options(int argc, const char *argv[], const char ***run_argv)
679 {
680         enum request request = REQ_VIEW_MAIN;
681         const char *subcommand;
682         bool seen_dashdash = FALSE;
683         /* XXX: This is vulnerable to the user overriding options
684          * required for the main view parser. */
685         const char *custom_argv[SIZEOF_ARG] = {
686                 "git", "log", "--no-color", "--pretty=raw", "--parents",
687                         "--topo-order", NULL
688         };
689         int i, j = 6;
690
691         if (!isatty(STDIN_FILENO)) {
692                 opt_pipe = stdin;
693                 return REQ_VIEW_PAGER;
694         }
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 begin_update(struct view *view, bool refresh)
2525 {
2526         if (init_io_fd(&view->io, opt_pipe)) {
2527                 opt_pipe = NULL;
2528
2529         } else if (refresh) {
2530                 if (!start_io(&view->io))
2531                         return FALSE;
2532
2533         } else {
2534                 if (view == VIEW(REQ_VIEW_TREE) && strcmp(view->vid, view->id))
2535                         opt_path[0] = 0;
2536
2537                 if (!run_io_rd(&view->io, view->ops->argv, FORMAT_ALL))
2538                         return FALSE;
2539
2540                 /* Put the current ref_* value to the view title ref
2541                  * member. This is needed by the blob view. Most other
2542                  * views sets it automatically after loading because the
2543                  * first line is a commit line. */
2544                 string_copy_rev(view->ref, view->id);
2545         }
2546
2547         setup_update(view, view->id);
2548
2549         return TRUE;
2550 }
2551
2552 #define ITEM_CHUNK_SIZE 256
2553 static void *
2554 realloc_items(void *mem, size_t *size, size_t new_size, size_t item_size)
2555 {
2556         size_t num_chunks = *size / ITEM_CHUNK_SIZE;
2557         size_t num_chunks_new = (new_size + ITEM_CHUNK_SIZE - 1) / ITEM_CHUNK_SIZE;
2558
2559         if (mem == NULL || num_chunks != num_chunks_new) {
2560                 *size = num_chunks_new * ITEM_CHUNK_SIZE;
2561                 mem = realloc(mem, *size * item_size);
2562         }
2563
2564         return mem;
2565 }
2566
2567 static struct line *
2568 realloc_lines(struct view *view, size_t line_size)
2569 {
2570         size_t alloc = view->line_alloc;
2571         struct line *tmp = realloc_items(view->line, &alloc, line_size,
2572                                          sizeof(*view->line));
2573
2574         if (!tmp)
2575                 return NULL;
2576
2577         view->line = tmp;
2578         view->line_alloc = alloc;
2579         view->line_size = line_size;
2580         return view->line;
2581 }
2582
2583 static bool
2584 update_view(struct view *view)
2585 {
2586         char out_buffer[BUFSIZ * 2];
2587         char *line;
2588         /* The number of lines to read. If too low it will cause too much
2589          * redrawing (and possible flickering), if too high responsiveness
2590          * will suffer. */
2591         unsigned long lines = view->height;
2592         int redraw_from = -1;
2593
2594         if (!view->pipe)
2595                 return TRUE;
2596
2597         /* Only redraw if lines are visible. */
2598         if (view->offset + view->height >= view->lines)
2599                 redraw_from = view->lines - view->offset;
2600
2601         /* FIXME: This is probably not perfect for backgrounded views. */
2602         if (!realloc_lines(view, view->lines + lines))
2603                 goto alloc_error;
2604
2605         while ((line = io_gets(view->pipe))) {
2606                 size_t linelen = strlen(line);
2607
2608                 if (linelen)
2609                         line[linelen - 1] = 0;
2610
2611                 if (opt_iconv != ICONV_NONE) {
2612                         ICONV_CONST char *inbuf = line;
2613                         size_t inlen = linelen;
2614
2615                         char *outbuf = out_buffer;
2616                         size_t outlen = sizeof(out_buffer);
2617
2618                         size_t ret;
2619
2620                         ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
2621                         if (ret != (size_t) -1) {
2622                                 line = out_buffer;
2623                                 linelen = strlen(out_buffer);
2624                         }
2625                 }
2626
2627                 if (!view->ops->read(view, line))
2628                         goto alloc_error;
2629
2630                 if (lines-- == 1)
2631                         break;
2632         }
2633
2634         {
2635                 int digits;
2636
2637                 lines = view->lines;
2638                 for (digits = 0; lines; digits++)
2639                         lines /= 10;
2640
2641                 /* Keep the displayed view in sync with line number scaling. */
2642                 if (digits != view->digits) {
2643                         view->digits = digits;
2644                         redraw_from = 0;
2645                 }
2646         }
2647
2648         if (io_error(view->pipe)) {
2649                 report("Failed to read: %s", io_strerror(view->pipe));
2650                 end_update(view, TRUE);
2651
2652         } else if (io_eof(view->pipe)) {
2653                 report("");
2654                 end_update(view, FALSE);
2655         }
2656
2657         if (!view_is_displayed(view))
2658                 return TRUE;
2659
2660         if (view == VIEW(REQ_VIEW_TREE)) {
2661                 /* Clear the view and redraw everything since the tree sorting
2662                  * might have rearranged things. */
2663                 redraw_view(view);
2664
2665         } else if (redraw_from >= 0) {
2666                 /* If this is an incremental update, redraw the previous line
2667                  * since for commits some members could have changed when
2668                  * loading the main view. */
2669                 if (redraw_from > 0)
2670                         redraw_from--;
2671
2672                 /* Since revision graph visualization requires knowledge
2673                  * about the parent commit, it causes a further one-off
2674                  * needed to be redrawn for incremental updates. */
2675                 if (redraw_from > 0 && opt_rev_graph)
2676                         redraw_from--;
2677
2678                 /* Incrementally draw avoids flickering. */
2679                 redraw_view_from(view, redraw_from);
2680         }
2681
2682         if (view == VIEW(REQ_VIEW_BLAME))
2683                 redraw_view_dirty(view);
2684
2685         /* Update the title _after_ the redraw so that if the redraw picks up a
2686          * commit reference in view->ref it'll be available here. */
2687         update_view_title(view);
2688         return TRUE;
2689
2690 alloc_error:
2691         report("Allocation failure");
2692         end_update(view, TRUE);
2693         return FALSE;
2694 }
2695
2696 static struct line *
2697 add_line_data(struct view *view, void *data, enum line_type type)
2698 {
2699         struct line *line = &view->line[view->lines++];
2700
2701         memset(line, 0, sizeof(*line));
2702         line->type = type;
2703         line->data = data;
2704
2705         return line;
2706 }
2707
2708 static struct line *
2709 add_line_text(struct view *view, const char *text, enum line_type type)
2710 {
2711         char *data = text ? strdup(text) : NULL;
2712
2713         return data ? add_line_data(view, data, type) : NULL;
2714 }
2715
2716
2717 /*
2718  * View opening
2719  */
2720
2721 enum open_flags {
2722         OPEN_DEFAULT = 0,       /* Use default view switching. */
2723         OPEN_SPLIT = 1,         /* Split current view. */
2724         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
2725         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2726         OPEN_NOMAXIMIZE = 8,    /* Do not maximize the current view. */
2727         OPEN_REFRESH = 16,      /* Refresh view using previous command. */
2728         OPEN_PREPARED = 32,     /* Open already prepared command. */
2729 };
2730
2731 static void
2732 open_view(struct view *prev, enum request request, enum open_flags flags)
2733 {
2734         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
2735         bool split = !!(flags & OPEN_SPLIT);
2736         bool reload = !!(flags & (OPEN_RELOAD | OPEN_REFRESH | OPEN_PREPARED));
2737         bool nomaximize = !!(flags & (OPEN_NOMAXIMIZE | OPEN_REFRESH));
2738         struct view *view = VIEW(request);
2739         int nviews = displayed_views();
2740         struct view *base_view = display[0];
2741
2742         if (view == prev && nviews == 1 && !reload) {
2743                 report("Already in %s view", view->name);
2744                 return;
2745         }
2746
2747         if (view->git_dir && !opt_git_dir[0]) {
2748                 report("The %s view is disabled in pager view", view->name);
2749                 return;
2750         }
2751
2752         if (split) {
2753                 display[1] = view;
2754                 if (!backgrounded)
2755                         current_view = 1;
2756         } else if (!nomaximize) {
2757                 /* Maximize the current view. */
2758                 memset(display, 0, sizeof(display));
2759                 current_view = 0;
2760                 display[current_view] = view;
2761         }
2762
2763         /* Resize the view when switching between split- and full-screen,
2764          * or when switching between two different full-screen views. */
2765         if (nviews != displayed_views() ||
2766             (nviews == 1 && base_view != display[0]))
2767                 resize_display();
2768
2769         if (view->pipe)
2770                 end_update(view, TRUE);
2771
2772         if (view->ops->open) {
2773                 if (!view->ops->open(view)) {
2774                         report("Failed to load %s view", view->name);
2775                         return;
2776                 }
2777
2778         } else if ((reload || strcmp(view->vid, view->id)) &&
2779                    !begin_update(view, flags & (OPEN_REFRESH | OPEN_PREPARED))) {
2780                 report("Failed to load %s view", view->name);
2781                 return;
2782         }
2783
2784         if (split && prev->lineno - prev->offset >= prev->height) {
2785                 /* Take the title line into account. */
2786                 int lines = prev->lineno - prev->offset - prev->height + 1;
2787
2788                 /* Scroll the view that was split if the current line is
2789                  * outside the new limited view. */
2790                 do_scroll_view(prev, lines);
2791         }
2792
2793         if (prev && view != prev) {
2794                 if (split && !backgrounded) {
2795                         /* "Blur" the previous view. */
2796                         update_view_title(prev);
2797                 }
2798
2799                 view->parent = prev;
2800         }
2801
2802         if (view->pipe && view->lines == 0) {
2803                 /* Clear the old view and let the incremental updating refill
2804                  * the screen. */
2805                 werase(view->win);
2806                 report("");
2807         } else if (view_is_displayed(view)) {
2808                 redraw_view(view);
2809                 report("");
2810         }
2811
2812         /* If the view is backgrounded the above calls to report()
2813          * won't redraw the view title. */
2814         if (backgrounded)
2815                 update_view_title(view);
2816 }
2817
2818 static void
2819 open_external_viewer(const char *argv[], const char *dir)
2820 {
2821         def_prog_mode();           /* save current tty modes */
2822         endwin();                  /* restore original tty modes */
2823         run_io_fg(argv, dir);
2824         fprintf(stderr, "Press Enter to continue");
2825         getc(opt_tty);
2826         reset_prog_mode();
2827         redraw_display();
2828 }
2829
2830 static void
2831 open_mergetool(const char *file)
2832 {
2833         const char *mergetool_argv[] = { "git", "mergetool", file, NULL };
2834
2835         open_external_viewer(mergetool_argv, NULL);
2836 }
2837
2838 static void
2839 open_editor(bool from_root, const char *file)
2840 {
2841         const char *editor_argv[] = { "vi", file, NULL };
2842         const char *editor;
2843
2844         editor = getenv("GIT_EDITOR");
2845         if (!editor && *opt_editor)
2846                 editor = opt_editor;
2847         if (!editor)
2848                 editor = getenv("VISUAL");
2849         if (!editor)
2850                 editor = getenv("EDITOR");
2851         if (!editor)
2852                 editor = "vi";
2853
2854         editor_argv[0] = editor;
2855         open_external_viewer(editor_argv, from_root ? opt_cdup : NULL);
2856 }
2857
2858 static void
2859 open_run_request(enum request request)
2860 {
2861         struct run_request *req = get_run_request(request);
2862         const char *argv[ARRAY_SIZE(req->argv)] = { NULL };
2863
2864         if (!req) {
2865                 report("Unknown run request");
2866                 return;
2867         }
2868
2869         if (format_argv(argv, req->argv, FORMAT_ALL))
2870                 open_external_viewer(argv, NULL);
2871         free_argv(argv);
2872 }
2873
2874 /*
2875  * User request switch noodle
2876  */
2877
2878 static int
2879 view_driver(struct view *view, enum request request)
2880 {
2881         int i;
2882
2883         if (request == REQ_NONE) {
2884                 doupdate();
2885                 return TRUE;
2886         }
2887
2888         if (request > REQ_NONE) {
2889                 open_run_request(request);
2890                 /* FIXME: When all views can refresh always do this. */
2891                 if (view == VIEW(REQ_VIEW_STATUS) ||
2892                     view == VIEW(REQ_VIEW_MAIN) ||
2893                     view == VIEW(REQ_VIEW_LOG) ||
2894                     view == VIEW(REQ_VIEW_STAGE))
2895                         request = REQ_REFRESH;
2896                 else
2897                         return TRUE;
2898         }
2899
2900         if (view && view->lines) {
2901                 request = view->ops->request(view, request, &view->line[view->lineno]);
2902                 if (request == REQ_NONE)
2903                         return TRUE;
2904         }
2905
2906         switch (request) {
2907         case REQ_MOVE_UP:
2908         case REQ_MOVE_DOWN:
2909         case REQ_MOVE_PAGE_UP:
2910         case REQ_MOVE_PAGE_DOWN:
2911         case REQ_MOVE_FIRST_LINE:
2912         case REQ_MOVE_LAST_LINE:
2913                 move_view(view, request);
2914                 break;
2915
2916         case REQ_SCROLL_LINE_DOWN:
2917         case REQ_SCROLL_LINE_UP:
2918         case REQ_SCROLL_PAGE_DOWN:
2919         case REQ_SCROLL_PAGE_UP:
2920                 scroll_view(view, request);
2921                 break;
2922
2923         case REQ_VIEW_BLAME:
2924                 if (!opt_file[0]) {
2925                         report("No file chosen, press %s to open tree view",
2926                                get_key(REQ_VIEW_TREE));
2927                         break;
2928                 }
2929                 open_view(view, request, OPEN_DEFAULT);
2930                 break;
2931
2932         case REQ_VIEW_BLOB:
2933                 if (!ref_blob[0]) {
2934                         report("No file chosen, press %s to open tree view",
2935                                get_key(REQ_VIEW_TREE));
2936                         break;
2937                 }
2938                 open_view(view, request, OPEN_DEFAULT);
2939                 break;
2940
2941         case REQ_VIEW_PAGER:
2942                 if (!opt_pipe && !VIEW(REQ_VIEW_PAGER)->lines) {
2943                         report("No pager content, press %s to run command from prompt",
2944                                get_key(REQ_PROMPT));
2945                         break;
2946                 }
2947                 open_view(view, request, OPEN_DEFAULT);
2948                 break;
2949
2950         case REQ_VIEW_STAGE:
2951                 if (!VIEW(REQ_VIEW_STAGE)->lines) {
2952                         report("No stage content, press %s to open the status view and choose file",
2953                                get_key(REQ_VIEW_STATUS));
2954                         break;
2955                 }
2956                 open_view(view, request, OPEN_DEFAULT);
2957                 break;
2958
2959         case REQ_VIEW_STATUS:
2960                 if (opt_is_inside_work_tree == FALSE) {
2961                         report("The status view requires a working tree");
2962                         break;
2963                 }
2964                 open_view(view, request, OPEN_DEFAULT);
2965                 break;
2966
2967         case REQ_VIEW_MAIN:
2968         case REQ_VIEW_DIFF:
2969         case REQ_VIEW_LOG:
2970         case REQ_VIEW_TREE:
2971         case REQ_VIEW_HELP:
2972                 open_view(view, request, OPEN_DEFAULT);
2973                 break;
2974
2975         case REQ_NEXT:
2976         case REQ_PREVIOUS:
2977                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
2978
2979                 if ((view == VIEW(REQ_VIEW_DIFF) &&
2980                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
2981                    (view == VIEW(REQ_VIEW_DIFF) &&
2982                      view->parent == VIEW(REQ_VIEW_BLAME)) ||
2983                    (view == VIEW(REQ_VIEW_STAGE) &&
2984                      view->parent == VIEW(REQ_VIEW_STATUS)) ||
2985                    (view == VIEW(REQ_VIEW_BLOB) &&
2986                      view->parent == VIEW(REQ_VIEW_TREE))) {
2987                         int line;
2988
2989                         view = view->parent;
2990                         line = view->lineno;
2991                         move_view(view, request);
2992                         if (view_is_displayed(view))
2993                                 update_view_title(view);
2994                         if (line != view->lineno)
2995                                 view->ops->request(view, REQ_ENTER,
2996                                                    &view->line[view->lineno]);
2997
2998                 } else {
2999                         move_view(view, request);
3000                 }
3001                 break;
3002
3003         case REQ_VIEW_NEXT:
3004         {
3005                 int nviews = displayed_views();
3006                 int next_view = (current_view + 1) % nviews;
3007
3008                 if (next_view == current_view) {
3009                         report("Only one view is displayed");
3010                         break;
3011                 }
3012
3013                 current_view = next_view;
3014                 /* Blur out the title of the previous view. */
3015                 update_view_title(view);
3016                 report("");
3017                 break;
3018         }
3019         case REQ_REFRESH:
3020                 report("Refreshing is not yet supported for the %s view", view->name);
3021                 break;
3022
3023         case REQ_MAXIMIZE:
3024                 if (displayed_views() == 2)
3025                         open_view(view, VIEW_REQ(view), OPEN_DEFAULT);
3026                 break;
3027
3028         case REQ_TOGGLE_LINENO:
3029                 opt_line_number = !opt_line_number;
3030                 redraw_display();
3031                 break;
3032
3033         case REQ_TOGGLE_DATE:
3034                 opt_date = !opt_date;
3035                 redraw_display();
3036                 break;
3037
3038         case REQ_TOGGLE_AUTHOR:
3039                 opt_author = !opt_author;
3040                 redraw_display();
3041                 break;
3042
3043         case REQ_TOGGLE_REV_GRAPH:
3044                 opt_rev_graph = !opt_rev_graph;
3045                 redraw_display();
3046                 break;
3047
3048         case REQ_TOGGLE_REFS:
3049                 opt_show_refs = !opt_show_refs;
3050                 redraw_display();
3051                 break;
3052
3053         case REQ_SEARCH:
3054         case REQ_SEARCH_BACK:
3055                 search_view(view, request);
3056                 break;
3057
3058         case REQ_FIND_NEXT:
3059         case REQ_FIND_PREV:
3060                 find_next(view, request);
3061                 break;
3062
3063         case REQ_STOP_LOADING:
3064                 for (i = 0; i < ARRAY_SIZE(views); i++) {
3065                         view = &views[i];
3066                         if (view->pipe)
3067                                 report("Stopped loading the %s view", view->name),
3068                         end_update(view, TRUE);
3069                 }
3070                 break;
3071
3072         case REQ_SHOW_VERSION:
3073                 report("tig-%s (built %s)", TIG_VERSION, __DATE__);
3074                 return TRUE;
3075
3076         case REQ_SCREEN_RESIZE:
3077                 resize_display();
3078                 /* Fall-through */
3079         case REQ_SCREEN_REDRAW:
3080                 redraw_display();
3081                 break;
3082
3083         case REQ_EDIT:
3084                 report("Nothing to edit");
3085                 break;
3086
3087         case REQ_ENTER:
3088                 report("Nothing to enter");
3089                 break;
3090
3091         case REQ_VIEW_CLOSE:
3092                 /* XXX: Mark closed views by letting view->parent point to the
3093                  * view itself. Parents to closed view should never be
3094                  * followed. */
3095                 if (view->parent &&
3096                     view->parent->parent != view->parent) {
3097                         memset(display, 0, sizeof(display));
3098                         current_view = 0;
3099                         display[current_view] = view->parent;
3100                         view->parent = view;
3101                         resize_display();
3102                         redraw_display();
3103                         report("");
3104                         break;
3105                 }
3106                 /* Fall-through */
3107         case REQ_QUIT:
3108                 return FALSE;
3109
3110         default:
3111                 report("Unknown key, press 'h' for help");
3112                 return TRUE;
3113         }
3114
3115         return TRUE;
3116 }
3117
3118
3119 /*
3120  * Pager backend
3121  */
3122
3123 static bool
3124 pager_draw(struct view *view, struct line *line, unsigned int lineno)
3125 {
3126         char *text = line->data;
3127
3128         if (opt_line_number && draw_lineno(view, lineno))
3129                 return TRUE;
3130
3131         draw_text(view, line->type, text, TRUE);
3132         return TRUE;
3133 }
3134
3135 static bool
3136 add_describe_ref(char *buf, size_t *bufpos, const char *commit_id, const char *sep)
3137 {
3138         const char *describe_argv[] = { "git", "describe", commit_id, NULL };
3139         char refbuf[SIZEOF_STR];
3140         char *ref = NULL;
3141
3142         if (run_io_buf(describe_argv, refbuf, sizeof(refbuf)))
3143                 ref = chomp_string(refbuf);
3144
3145         if (!ref || !*ref)
3146                 return TRUE;
3147
3148         /* This is the only fatal call, since it can "corrupt" the buffer. */
3149         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
3150                 return FALSE;
3151
3152         return TRUE;
3153 }
3154
3155 static void
3156 add_pager_refs(struct view *view, struct line *line)
3157 {
3158         char buf[SIZEOF_STR];
3159         char *commit_id = (char *)line->data + STRING_SIZE("commit ");
3160         struct ref **refs;
3161         size_t bufpos = 0, refpos = 0;
3162         const char *sep = "Refs: ";
3163         bool is_tag = FALSE;
3164
3165         assert(line->type == LINE_COMMIT);
3166
3167         refs = get_refs(commit_id);
3168         if (!refs) {
3169                 if (view == VIEW(REQ_VIEW_DIFF))
3170                         goto try_add_describe_ref;
3171                 return;
3172         }
3173
3174         do {
3175                 struct ref *ref = refs[refpos];
3176                 const char *fmt = ref->tag    ? "%s[%s]" :
3177                                   ref->remote ? "%s<%s>" : "%s%s";
3178
3179                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
3180                         return;
3181                 sep = ", ";
3182                 if (ref->tag)
3183                         is_tag = TRUE;
3184         } while (refs[refpos++]->next);
3185
3186         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
3187 try_add_describe_ref:
3188                 /* Add <tag>-g<commit_id> "fake" reference. */
3189                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
3190                         return;
3191         }
3192
3193         if (bufpos == 0)
3194                 return;
3195
3196         if (!realloc_lines(view, view->line_size + 1))
3197                 return;
3198
3199         add_line_text(view, buf, LINE_PP_REFS);
3200 }
3201
3202 static bool
3203 pager_read(struct view *view, char *data)
3204 {
3205         struct line *line;
3206
3207         if (!data)
3208                 return TRUE;
3209
3210         line = add_line_text(view, data, get_line_type(data));
3211         if (!line)
3212                 return FALSE;
3213
3214         if (line->type == LINE_COMMIT &&
3215             (view == VIEW(REQ_VIEW_DIFF) ||
3216              view == VIEW(REQ_VIEW_LOG)))
3217                 add_pager_refs(view, line);
3218
3219         return TRUE;
3220 }
3221
3222 static enum request
3223 pager_request(struct view *view, enum request request, struct line *line)
3224 {
3225         int split = 0;
3226
3227         if (request != REQ_ENTER)
3228                 return request;
3229
3230         if (line->type == LINE_COMMIT &&
3231            (view == VIEW(REQ_VIEW_LOG) ||
3232             view == VIEW(REQ_VIEW_PAGER))) {
3233                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
3234                 split = 1;
3235         }
3236
3237         /* Always scroll the view even if it was split. That way
3238          * you can use Enter to scroll through the log view and
3239          * split open each commit diff. */
3240         scroll_view(view, REQ_SCROLL_LINE_DOWN);
3241
3242         /* FIXME: A minor workaround. Scrolling the view will call report("")
3243          * but if we are scrolling a non-current view this won't properly
3244          * update the view title. */
3245         if (split)
3246                 update_view_title(view);
3247
3248         return REQ_NONE;
3249 }
3250
3251 static bool
3252 pager_grep(struct view *view, struct line *line)
3253 {
3254         regmatch_t pmatch;
3255         char *text = line->data;
3256
3257         if (!*text)
3258                 return FALSE;
3259
3260         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
3261                 return FALSE;
3262
3263         return TRUE;
3264 }
3265
3266 static void
3267 pager_select(struct view *view, struct line *line)
3268 {
3269         if (line->type == LINE_COMMIT) {
3270                 char *text = (char *)line->data + STRING_SIZE("commit ");
3271
3272                 if (view != VIEW(REQ_VIEW_PAGER))
3273                         string_copy_rev(view->ref, text);
3274                 string_copy_rev(ref_commit, text);
3275         }
3276 }
3277
3278 static struct view_ops pager_ops = {
3279         "line",
3280         NULL,
3281         NULL,
3282         pager_read,
3283         pager_draw,
3284         pager_request,
3285         pager_grep,
3286         pager_select,
3287 };
3288
3289 static const char *log_argv[SIZEOF_ARG] = {
3290         "git", "log", "--no-color", "--cc", "--stat", "-n100", "%(head)", NULL
3291 };
3292
3293 static enum request
3294 log_request(struct view *view, enum request request, struct line *line)
3295 {
3296         switch (request) {
3297         case REQ_REFRESH:
3298                 load_refs();
3299                 open_view(view, REQ_VIEW_LOG, OPEN_REFRESH);
3300                 return REQ_NONE;
3301         default:
3302                 return pager_request(view, request, line);
3303         }
3304 }
3305
3306 static struct view_ops log_ops = {
3307         "line",
3308         log_argv,
3309         NULL,
3310         pager_read,
3311         pager_draw,
3312         log_request,
3313         pager_grep,
3314         pager_select,
3315 };
3316
3317 static const char *diff_argv[SIZEOF_ARG] = {
3318         "git", "show", "--pretty=fuller", "--no-color", "--root",
3319                 "--patch-with-stat", "--find-copies-harder", "-C", "%(commit)", NULL
3320 };
3321
3322 static struct view_ops diff_ops = {
3323         "line",
3324         diff_argv,
3325         NULL,
3326         pager_read,
3327         pager_draw,
3328         pager_request,
3329         pager_grep,
3330         pager_select,
3331 };
3332
3333 /*
3334  * Help backend
3335  */
3336
3337 static bool
3338 help_open(struct view *view)
3339 {
3340         char buf[BUFSIZ];
3341         int lines = ARRAY_SIZE(req_info) + 2;
3342         int i;
3343
3344         if (view->lines > 0)
3345                 return TRUE;
3346
3347         for (i = 0; i < ARRAY_SIZE(req_info); i++)
3348                 if (!req_info[i].request)
3349                         lines++;
3350
3351         lines += run_requests + 1;
3352
3353         view->line = calloc(lines, sizeof(*view->line));
3354         if (!view->line)
3355                 return FALSE;
3356
3357         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
3358
3359         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
3360                 const char *key;
3361
3362                 if (req_info[i].request == REQ_NONE)
3363                         continue;
3364
3365                 if (!req_info[i].request) {
3366                         add_line_text(view, "", LINE_DEFAULT);
3367                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
3368                         continue;
3369                 }
3370
3371                 key = get_key(req_info[i].request);
3372                 if (!*key)
3373                         key = "(no key defined)";
3374
3375                 if (!string_format(buf, "    %-25s %s", key, req_info[i].help))
3376                         continue;
3377
3378                 add_line_text(view, buf, LINE_DEFAULT);
3379         }
3380
3381         if (run_requests) {
3382                 add_line_text(view, "", LINE_DEFAULT);
3383                 add_line_text(view, "External commands:", LINE_DEFAULT);
3384         }
3385
3386         for (i = 0; i < run_requests; i++) {
3387                 struct run_request *req = get_run_request(REQ_NONE + i + 1);
3388                 const char *key;
3389                 char cmd[SIZEOF_STR];
3390                 size_t bufpos;
3391                 int argc;
3392
3393                 if (!req)
3394                         continue;
3395
3396                 key = get_key_name(req->key);
3397                 if (!*key)
3398                         key = "(no key defined)";
3399
3400                 for (bufpos = 0, argc = 0; req->argv[argc]; argc++)
3401                         if (!string_format_from(cmd, &bufpos, "%s%s",
3402                                                 argc ? " " : "", req->argv[argc]))
3403                                 return REQ_NONE;
3404
3405                 if (!string_format(buf, "    %-10s %-14s `%s`",
3406                                    keymap_table[req->keymap].name, key, cmd))
3407                         continue;
3408
3409                 add_line_text(view, buf, LINE_DEFAULT);
3410         }
3411
3412         return TRUE;
3413 }
3414
3415 static struct view_ops help_ops = {
3416         "line",
3417         NULL,
3418         help_open,
3419         NULL,
3420         pager_draw,
3421         pager_request,
3422         pager_grep,
3423         pager_select,
3424 };
3425
3426
3427 /*
3428  * Tree backend
3429  */
3430
3431 struct tree_stack_entry {
3432         struct tree_stack_entry *prev;  /* Entry below this in the stack */
3433         unsigned long lineno;           /* Line number to restore */
3434         char *name;                     /* Position of name in opt_path */
3435 };
3436
3437 /* The top of the path stack. */
3438 static struct tree_stack_entry *tree_stack = NULL;
3439 unsigned long tree_lineno = 0;
3440
3441 static void
3442 pop_tree_stack_entry(void)
3443 {
3444         struct tree_stack_entry *entry = tree_stack;
3445
3446         tree_lineno = entry->lineno;
3447         entry->name[0] = 0;
3448         tree_stack = entry->prev;
3449         free(entry);
3450 }
3451
3452 static void
3453 push_tree_stack_entry(const char *name, unsigned long lineno)
3454 {
3455         struct tree_stack_entry *entry = calloc(1, sizeof(*entry));
3456         size_t pathlen = strlen(opt_path);
3457
3458         if (!entry)
3459                 return;
3460
3461         entry->prev = tree_stack;
3462         entry->name = opt_path + pathlen;
3463         tree_stack = entry;
3464
3465         if (!string_format_from(opt_path, &pathlen, "%s/", name)) {
3466                 pop_tree_stack_entry();
3467                 return;
3468         }
3469
3470         /* Move the current line to the first tree entry. */
3471         tree_lineno = 1;
3472         entry->lineno = lineno;
3473 }
3474
3475 /* Parse output from git-ls-tree(1):
3476  *
3477  * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile
3478  * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README
3479  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
3480  * 100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38 web.conf
3481  */
3482
3483 #define SIZEOF_TREE_ATTR \
3484         STRING_SIZE("100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38\t")
3485
3486 #define TREE_UP_FORMAT "040000 tree %s\t.."
3487
3488 static int
3489 tree_compare_entry(enum line_type type1, const char *name1,
3490                    enum line_type type2, const char *name2)
3491 {
3492         if (type1 != type2) {
3493                 if (type1 == LINE_TREE_DIR)
3494                         return -1;
3495                 return 1;
3496         }
3497
3498         return strcmp(name1, name2);
3499 }
3500
3501 static const char *
3502 tree_path(struct line *line)
3503 {
3504         const char *path = line->data;
3505
3506         return path + SIZEOF_TREE_ATTR;
3507 }
3508
3509 static bool
3510 tree_read(struct view *view, char *text)
3511 {
3512         size_t textlen = text ? strlen(text) : 0;
3513         char buf[SIZEOF_STR];
3514         unsigned long pos;
3515         enum line_type type;
3516         bool first_read = view->lines == 0;
3517
3518         if (!text)
3519                 return TRUE;
3520         if (textlen <= SIZEOF_TREE_ATTR)
3521                 return FALSE;
3522
3523         type = text[STRING_SIZE("100644 ")] == 't'
3524              ? LINE_TREE_DIR : LINE_TREE_FILE;
3525
3526         if (first_read) {
3527                 /* Add path info line */
3528                 if (!string_format(buf, "Directory path /%s", opt_path) ||
3529                     !realloc_lines(view, view->line_size + 1) ||
3530                     !add_line_text(view, buf, LINE_DEFAULT))
3531                         return FALSE;
3532
3533                 /* Insert "link" to parent directory. */
3534                 if (*opt_path) {
3535                         if (!string_format(buf, TREE_UP_FORMAT, view->ref) ||
3536                             !realloc_lines(view, view->line_size + 1) ||
3537                             !add_line_text(view, buf, LINE_TREE_DIR))
3538                                 return FALSE;
3539                 }
3540         }
3541
3542         /* Strip the path part ... */
3543         if (*opt_path) {
3544                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
3545                 size_t striplen = strlen(opt_path);
3546                 char *path = text + SIZEOF_TREE_ATTR;
3547
3548                 if (pathlen > striplen)
3549                         memmove(path, path + striplen,
3550                                 pathlen - striplen + 1);
3551         }
3552
3553         /* Skip "Directory ..." and ".." line. */
3554         for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
3555                 struct line *line = &view->line[pos];
3556                 const char *path1 = tree_path(line);
3557                 char *path2 = text + SIZEOF_TREE_ATTR;
3558                 int cmp = tree_compare_entry(line->type, path1, type, path2);
3559
3560                 if (cmp <= 0)
3561                         continue;
3562
3563                 text = strdup(text);
3564                 if (!text)
3565                         return FALSE;
3566
3567                 if (view->lines > pos)
3568                         memmove(&view->line[pos + 1], &view->line[pos],
3569                                 (view->lines - pos) * sizeof(*line));
3570
3571                 line = &view->line[pos];
3572                 line->data = text;
3573                 line->type = type;
3574                 view->lines++;
3575                 return TRUE;
3576         }
3577
3578         if (!add_line_text(view, text, type))
3579                 return FALSE;
3580
3581         if (tree_lineno > view->lineno) {
3582                 view->lineno = tree_lineno;
3583                 tree_lineno = 0;
3584         }
3585
3586         return TRUE;
3587 }
3588
3589 static enum request
3590 tree_request(struct view *view, enum request request, struct line *line)
3591 {
3592         enum open_flags flags;
3593
3594         switch (request) {
3595         case REQ_VIEW_BLAME:
3596                 if (line->type != LINE_TREE_FILE) {
3597                         report("Blame only supported for files");
3598                         return REQ_NONE;
3599                 }
3600
3601                 string_copy(opt_ref, view->vid);
3602                 return request;
3603
3604         case REQ_EDIT:
3605                 if (line->type != LINE_TREE_FILE) {
3606                         report("Edit only supported for files");
3607                 } else if (!is_head_commit(view->vid)) {
3608                         report("Edit only supported for files in the current work tree");
3609                 } else {
3610                         open_editor(TRUE, opt_file);
3611                 }
3612                 return REQ_NONE;
3613
3614         case REQ_TREE_PARENT:
3615                 if (!*opt_path) {
3616                         /* quit view if at top of tree */
3617                         return REQ_VIEW_CLOSE;
3618                 }
3619                 /* fake 'cd  ..' */
3620                 line = &view->line[1];
3621                 break;
3622
3623         case REQ_ENTER:
3624                 break;
3625
3626         default:
3627                 return request;
3628         }
3629
3630         /* Cleanup the stack if the tree view is at a different tree. */
3631         while (!*opt_path && tree_stack)
3632                 pop_tree_stack_entry();
3633
3634         switch (line->type) {
3635         case LINE_TREE_DIR:
3636                 /* Depending on whether it is a subdir or parent (updir?) link
3637                  * mangle the path buffer. */
3638                 if (line == &view->line[1] && *opt_path) {
3639                         pop_tree_stack_entry();
3640
3641                 } else {
3642                         const char *basename = tree_path(line);
3643
3644                         push_tree_stack_entry(basename, view->lineno);
3645                 }
3646
3647                 /* Trees and subtrees share the same ID, so they are not not
3648                  * unique like blobs. */
3649                 flags = OPEN_RELOAD;
3650                 request = REQ_VIEW_TREE;
3651                 break;
3652
3653         case LINE_TREE_FILE:
3654                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3655                 request = REQ_VIEW_BLOB;
3656                 break;
3657
3658         default:
3659                 return TRUE;
3660         }
3661
3662         open_view(view, request, flags);
3663         if (request == REQ_VIEW_TREE) {
3664                 view->lineno = tree_lineno;
3665         }
3666
3667         return REQ_NONE;
3668 }
3669
3670 static void
3671 tree_select(struct view *view, struct line *line)
3672 {
3673         char *text = (char *)line->data + STRING_SIZE("100644 blob ");
3674
3675         if (line->type == LINE_TREE_FILE) {
3676                 string_copy_rev(ref_blob, text);
3677                 string_format(opt_file, "%s%s", opt_path, tree_path(line));
3678
3679         } else if (line->type != LINE_TREE_DIR) {
3680                 return;
3681         }
3682
3683         string_copy_rev(view->ref, text);
3684 }
3685
3686 static const char *tree_argv[SIZEOF_ARG] = {
3687         "git", "ls-tree", "%(commit)", "%(directory)", NULL
3688 };
3689
3690 static struct view_ops tree_ops = {
3691         "file",
3692         tree_argv,
3693         NULL,
3694         tree_read,
3695         pager_draw,
3696         tree_request,
3697         pager_grep,
3698         tree_select,
3699 };
3700
3701 static bool
3702 blob_read(struct view *view, char *line)
3703 {
3704         if (!line)
3705                 return TRUE;
3706         return add_line_text(view, line, LINE_DEFAULT) != NULL;
3707 }
3708
3709 static const char *blob_argv[SIZEOF_ARG] = {
3710         "git", "cat-file", "blob", "%(blob)", NULL
3711 };
3712
3713 static struct view_ops blob_ops = {
3714         "line",
3715         blob_argv,
3716         NULL,
3717         blob_read,
3718         pager_draw,
3719         pager_request,
3720         pager_grep,
3721         pager_select,
3722 };
3723
3724 /*
3725  * Blame backend
3726  *
3727  * Loading the blame view is a two phase job:
3728  *
3729  *  1. File content is read either using opt_file from the
3730  *     filesystem or using git-cat-file.
3731  *  2. Then blame information is incrementally added by
3732  *     reading output from git-blame.
3733  */
3734
3735 static const char *blame_head_argv[] = {
3736         "git", "blame", "--incremental", "--", "%(file)", NULL
3737 };
3738
3739 static const char *blame_ref_argv[] = {
3740         "git", "blame", "--incremental", "%(ref)", "--", "%(file)", NULL
3741 };
3742
3743 static const char *blame_cat_file_argv[] = {
3744         "git", "cat-file", "blob", "%(ref):%(file)", NULL
3745 };
3746
3747 struct blame_commit {
3748         char id[SIZEOF_REV];            /* SHA1 ID. */
3749         char title[128];                /* First line of the commit message. */
3750         char author[75];                /* Author of the commit. */
3751         struct tm time;                 /* Date from the author ident. */
3752         char filename[128];             /* Name of file. */
3753 };
3754
3755 struct blame {
3756         struct blame_commit *commit;
3757         char text[1];
3758 };
3759
3760 static bool
3761 blame_open(struct view *view)
3762 {
3763         if (*opt_ref || !init_io_fd(&view->io, fopen(opt_file, "r"))) {
3764                 if (!run_io_rd(&view->io, blame_cat_file_argv, FORMAT_ALL))
3765                         return FALSE;
3766         }
3767
3768         setup_update(view, opt_file);
3769         string_format(view->ref, "%s ...", opt_file);
3770
3771         return TRUE;
3772 }
3773
3774 static struct blame_commit *
3775 get_blame_commit(struct view *view, const char *id)
3776 {
3777         size_t i;
3778
3779         for (i = 0; i < view->lines; i++) {
3780                 struct blame *blame = view->line[i].data;
3781
3782                 if (!blame->commit)
3783                         continue;
3784
3785                 if (!strncmp(blame->commit->id, id, SIZEOF_REV - 1))
3786                         return blame->commit;
3787         }
3788
3789         {
3790                 struct blame_commit *commit = calloc(1, sizeof(*commit));
3791
3792                 if (commit)
3793                         string_ncopy(commit->id, id, SIZEOF_REV);
3794                 return commit;
3795         }
3796 }
3797
3798 static bool
3799 parse_number(const char **posref, size_t *number, size_t min, size_t max)
3800 {
3801         const char *pos = *posref;
3802
3803         *posref = NULL;
3804         pos = strchr(pos + 1, ' ');
3805         if (!pos || !isdigit(pos[1]))
3806                 return FALSE;
3807         *number = atoi(pos + 1);
3808         if (*number < min || *number > max)
3809                 return FALSE;
3810
3811         *posref = pos;
3812         return TRUE;
3813 }
3814
3815 static struct blame_commit *
3816 parse_blame_commit(struct view *view, const char *text, int *blamed)
3817 {
3818         struct blame_commit *commit;
3819         struct blame *blame;
3820         const char *pos = text + SIZEOF_REV - 1;
3821         size_t lineno;
3822         size_t group;
3823
3824         if (strlen(text) <= SIZEOF_REV || *pos != ' ')
3825                 return NULL;
3826
3827         if (!parse_number(&pos, &lineno, 1, view->lines) ||
3828             !parse_number(&pos, &group, 1, view->lines - lineno + 1))
3829                 return NULL;
3830
3831         commit = get_blame_commit(view, text);
3832         if (!commit)
3833                 return NULL;
3834
3835         *blamed += group;
3836         while (group--) {
3837                 struct line *line = &view->line[lineno + group - 1];
3838
3839                 blame = line->data;
3840                 blame->commit = commit;
3841                 line->dirty = 1;
3842         }
3843
3844         return commit;
3845 }
3846
3847 static bool
3848 blame_read_file(struct view *view, const char *line, bool *read_file)
3849 {
3850         if (!line) {
3851                 const char **argv = *opt_ref ? blame_ref_argv : blame_head_argv;
3852                 struct io io = {};
3853
3854                 if (view->lines == 0 && !view->parent)
3855                         die("No blame exist for %s", view->vid);
3856
3857                 if (view->lines == 0 || !run_io_rd(&io, argv, FORMAT_ALL)) {
3858                         report("Failed to load blame data");
3859                         return TRUE;
3860                 }
3861
3862                 done_io(view->pipe);
3863                 view->io = io;
3864                 *read_file = FALSE;
3865                 return FALSE;
3866
3867         } else {
3868                 size_t linelen = strlen(line);
3869                 struct blame *blame = malloc(sizeof(*blame) + linelen);
3870
3871                 blame->commit = NULL;
3872                 strncpy(blame->text, line, linelen);
3873                 blame->text[linelen] = 0;
3874                 return add_line_data(view, blame, LINE_BLAME_ID) != NULL;
3875         }
3876 }
3877
3878 static bool
3879 match_blame_header(const char *name, char **line)
3880 {
3881         size_t namelen = strlen(name);
3882         bool matched = !strncmp(name, *line, namelen);
3883
3884         if (matched)
3885                 *line += namelen;
3886
3887         return matched;
3888 }
3889
3890 static bool
3891 blame_read(struct view *view, char *line)
3892 {
3893         static struct blame_commit *commit = NULL;
3894         static int blamed = 0;
3895         static time_t author_time;
3896         static bool read_file = TRUE;
3897
3898         if (read_file)
3899                 return blame_read_file(view, line, &read_file);
3900
3901         if (!line) {
3902                 /* Reset all! */
3903                 commit = NULL;
3904                 blamed = 0;
3905                 read_file = TRUE;
3906                 string_format(view->ref, "%s", view->vid);
3907                 if (view_is_displayed(view)) {
3908                         update_view_title(view);
3909                         redraw_view_from(view, 0);
3910                 }
3911                 return TRUE;
3912         }
3913
3914         if (!commit) {
3915                 commit = parse_blame_commit(view, line, &blamed);
3916                 string_format(view->ref, "%s %2d%%", view->vid,
3917                               blamed * 100 / view->lines);
3918
3919         } else if (match_blame_header("author ", &line)) {
3920                 string_ncopy(commit->author, line, strlen(line));
3921
3922         } else if (match_blame_header("author-time ", &line)) {
3923                 author_time = (time_t) atol(line);
3924
3925         } else if (match_blame_header("author-tz ", &line)) {
3926                 long tz;
3927
3928                 tz  = ('0' - line[1]) * 60 * 60 * 10;
3929                 tz += ('0' - line[2]) * 60 * 60;
3930                 tz += ('0' - line[3]) * 60;
3931                 tz += ('0' - line[4]) * 60;
3932
3933                 if (line[0] == '-')
3934                         tz = -tz;
3935
3936                 author_time -= tz;
3937                 gmtime_r(&author_time, &commit->time);
3938
3939         } else if (match_blame_header("summary ", &line)) {
3940                 string_ncopy(commit->title, line, strlen(line));
3941
3942         } else if (match_blame_header("filename ", &line)) {
3943                 string_ncopy(commit->filename, line, strlen(line));
3944                 commit = NULL;
3945         }
3946
3947         return TRUE;
3948 }
3949
3950 static bool
3951 blame_draw(struct view *view, struct line *line, unsigned int lineno)
3952 {
3953         struct blame *blame = line->data;
3954         struct tm *time = NULL;
3955         const char *id = NULL, *author = NULL;
3956
3957         if (blame->commit && *blame->commit->filename) {
3958                 id = blame->commit->id;
3959                 author = blame->commit->author;
3960                 time = &blame->commit->time;
3961         }
3962
3963         if (opt_date && draw_date(view, time))
3964                 return TRUE;
3965
3966         if (opt_author &&
3967             draw_field(view, LINE_MAIN_AUTHOR, author, opt_author_cols, TRUE))
3968                 return TRUE;
3969
3970         if (draw_field(view, LINE_BLAME_ID, id, ID_COLS, FALSE))
3971                 return TRUE;
3972
3973         if (draw_lineno(view, lineno))
3974                 return TRUE;
3975
3976         draw_text(view, LINE_DEFAULT, blame->text, TRUE);
3977         return TRUE;
3978 }
3979
3980 static enum request
3981 blame_request(struct view *view, enum request request, struct line *line)
3982 {
3983         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3984         struct blame *blame = line->data;
3985
3986         switch (request) {
3987         case REQ_VIEW_BLAME:
3988                 if (!blame->commit || !strcmp(blame->commit->id, NULL_ID)) {
3989                         report("Commit ID unknown");
3990                         break;
3991                 }
3992                 string_copy(opt_ref, blame->commit->id);
3993                 open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
3994                 return request;
3995
3996         case REQ_ENTER:
3997                 if (!blame->commit) {
3998                         report("No commit loaded yet");
3999                         break;
4000                 }
4001
4002                 if (view_is_displayed(VIEW(REQ_VIEW_DIFF)) &&
4003                     !strcmp(blame->commit->id, VIEW(REQ_VIEW_DIFF)->ref))
4004                         break;
4005
4006                 if (!strcmp(blame->commit->id, NULL_ID)) {
4007                         struct view *diff = VIEW(REQ_VIEW_DIFF);
4008                         const char *diff_index_argv[] = {
4009                                 "git", "diff-index", "--root", "--cached",
4010                                         "--patch-with-stat", "-C", "-M",
4011                                         "HEAD", "--", view->vid, NULL
4012                         };
4013
4014                         if (!prepare_update(diff, diff_index_argv, NULL, FORMAT_DASH)) {
4015                                 report("Failed to allocate diff command");
4016                                 break;
4017                         }
4018                         flags |= OPEN_PREPARED;
4019                 }
4020
4021                 open_view(view, REQ_VIEW_DIFF, flags);
4022                 break;
4023
4024         default:
4025                 return request;
4026         }
4027
4028         return REQ_NONE;
4029 }
4030
4031 static bool
4032 blame_grep(struct view *view, struct line *line)
4033 {
4034         struct blame *blame = line->data;
4035         struct blame_commit *commit = blame->commit;
4036         regmatch_t pmatch;
4037
4038 #define MATCH(text, on)                                                 \
4039         (on && *text && regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4040
4041         if (commit) {
4042                 char buf[DATE_COLS + 1];
4043
4044                 if (MATCH(commit->title, 1) ||
4045                     MATCH(commit->author, opt_author) ||
4046                     MATCH(commit->id, opt_date))
4047                         return TRUE;
4048
4049                 if (strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time) &&
4050                     MATCH(buf, 1))
4051                         return TRUE;
4052         }
4053
4054         return MATCH(blame->text, 1);
4055
4056 #undef MATCH
4057 }
4058
4059 static void
4060 blame_select(struct view *view, struct line *line)
4061 {
4062         struct blame *blame = line->data;
4063         struct blame_commit *commit = blame->commit;
4064
4065         if (!commit)
4066                 return;
4067
4068         if (!strcmp(commit->id, NULL_ID))
4069                 string_ncopy(ref_commit, "HEAD", 4);
4070         else
4071                 string_copy_rev(ref_commit, commit->id);
4072 }
4073
4074 static struct view_ops blame_ops = {
4075         "line",
4076         NULL,
4077         blame_open,
4078         blame_read,
4079         blame_draw,
4080         blame_request,
4081         blame_grep,
4082         blame_select,
4083 };
4084
4085 /*
4086  * Status backend
4087  */
4088
4089 struct status {
4090         char status;
4091         struct {
4092                 mode_t mode;
4093                 char rev[SIZEOF_REV];
4094                 char name[SIZEOF_STR];
4095         } old;
4096         struct {
4097                 mode_t mode;
4098                 char rev[SIZEOF_REV];
4099                 char name[SIZEOF_STR];
4100         } new;
4101 };
4102
4103 static char status_onbranch[SIZEOF_STR];
4104 static struct status stage_status;
4105 static enum line_type stage_line_type;
4106 static size_t stage_chunks;
4107 static int *stage_chunk;
4108
4109 /* This should work even for the "On branch" line. */
4110 static inline bool
4111 status_has_none(struct view *view, struct line *line)
4112 {
4113         return line < view->line + view->lines && !line[1].data;
4114 }
4115
4116 /* Get fields from the diff line:
4117  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
4118  */
4119 static inline bool
4120 status_get_diff(struct status *file, const char *buf, size_t bufsize)
4121 {
4122         const char *old_mode = buf +  1;
4123         const char *new_mode = buf +  8;
4124         const char *old_rev  = buf + 15;
4125         const char *new_rev  = buf + 56;
4126         const char *status   = buf + 97;
4127
4128         if (bufsize < 99 ||
4129             old_mode[-1] != ':' ||
4130             new_mode[-1] != ' ' ||
4131             old_rev[-1]  != ' ' ||
4132             new_rev[-1]  != ' ' ||
4133             status[-1]   != ' ')
4134                 return FALSE;
4135
4136         file->status = *status;
4137
4138         string_copy_rev(file->old.rev, old_rev);
4139         string_copy_rev(file->new.rev, new_rev);
4140
4141         file->old.mode = strtoul(old_mode, NULL, 8);
4142         file->new.mode = strtoul(new_mode, NULL, 8);
4143
4144         file->old.name[0] = file->new.name[0] = 0;
4145
4146         return TRUE;
4147 }
4148
4149 static bool
4150 status_run(struct view *view, const char cmd[], char status, enum line_type type)
4151 {
4152         struct status *file = NULL;
4153         struct status *unmerged = NULL;
4154         char buf[SIZEOF_STR * 4];
4155         size_t bufsize = 0;
4156         FILE *pipe;
4157
4158         pipe = popen(cmd, "r");
4159         if (!pipe)
4160                 return FALSE;
4161
4162         add_line_data(view, NULL, type);
4163
4164         while (!feof(pipe) && !ferror(pipe)) {
4165                 char *sep;
4166                 size_t readsize;
4167
4168                 readsize = fread(buf + bufsize, 1, sizeof(buf) - bufsize, pipe);
4169                 if (!readsize)
4170                         break;
4171                 bufsize += readsize;
4172
4173                 /* Process while we have NUL chars. */
4174                 while ((sep = memchr(buf, 0, bufsize))) {
4175                         size_t sepsize = sep - buf + 1;
4176
4177                         if (!file) {
4178                                 if (!realloc_lines(view, view->line_size + 1))
4179                                         goto error_out;
4180
4181                                 file = calloc(1, sizeof(*file));
4182                                 if (!file)
4183                                         goto error_out;
4184
4185                                 add_line_data(view, file, type);
4186                         }
4187
4188                         /* Parse diff info part. */
4189                         if (status) {
4190                                 file->status = status;
4191                                 if (status == 'A')
4192                                         string_copy(file->old.rev, NULL_ID);
4193
4194                         } else if (!file->status) {
4195                                 if (!status_get_diff(file, buf, sepsize))
4196                                         goto error_out;
4197
4198                                 bufsize -= sepsize;
4199                                 memmove(buf, sep + 1, bufsize);
4200
4201                                 sep = memchr(buf, 0, bufsize);
4202                                 if (!sep)
4203                                         break;
4204                                 sepsize = sep - buf + 1;
4205
4206                                 /* Collapse all 'M'odified entries that
4207                                  * follow a associated 'U'nmerged entry.
4208                                  */
4209                                 if (file->status == 'U') {
4210                                         unmerged = file;
4211
4212                                 } else if (unmerged) {
4213                                         int collapse = !strcmp(buf, unmerged->new.name);
4214
4215                                         unmerged = NULL;
4216                                         if (collapse) {
4217                                                 free(file);
4218                                                 view->lines--;
4219                                                 continue;
4220                                         }
4221                                 }
4222                         }
4223
4224                         /* Grab the old name for rename/copy. */
4225                         if (!*file->old.name &&
4226                             (file->status == 'R' || file->status == 'C')) {
4227                                 sepsize = sep - buf + 1;
4228                                 string_ncopy(file->old.name, buf, sepsize);
4229                                 bufsize -= sepsize;
4230                                 memmove(buf, sep + 1, bufsize);
4231
4232                                 sep = memchr(buf, 0, bufsize);
4233                                 if (!sep)
4234                                         break;
4235                                 sepsize = sep - buf + 1;
4236                         }
4237
4238                         /* git-ls-files just delivers a NUL separated
4239                          * list of file names similar to the second half
4240                          * of the git-diff-* output. */
4241                         string_ncopy(file->new.name, buf, sepsize);
4242                         if (!*file->old.name)
4243                                 string_copy(file->old.name, file->new.name);
4244                         bufsize -= sepsize;
4245                         memmove(buf, sep + 1, bufsize);
4246                         file = NULL;
4247                 }
4248         }
4249
4250         if (ferror(pipe)) {
4251 error_out:
4252                 pclose(pipe);
4253                 return FALSE;
4254         }
4255
4256         if (!view->line[view->lines - 1].data)
4257                 add_line_data(view, NULL, LINE_STAT_NONE);
4258
4259         pclose(pipe);
4260         return TRUE;
4261 }
4262
4263 /* Don't show unmerged entries in the staged section. */
4264 #define STATUS_DIFF_INDEX_CMD "git diff-index -z --diff-filter=ACDMRTXB --cached -M HEAD"
4265 #define STATUS_DIFF_FILES_CMD "git diff-files -z"
4266 #define STATUS_LIST_OTHER_CMD \
4267         "git ls-files -z --others --exclude-standard"
4268 #define STATUS_LIST_NO_HEAD_CMD \
4269         "git ls-files -z --cached --exclude-standard"
4270
4271 /* First parse staged info using git-diff-index(1), then parse unstaged
4272  * info using git-diff-files(1), and finally untracked files using
4273  * git-ls-files(1). */
4274 static bool
4275 status_open(struct view *view)
4276 {
4277         unsigned long prev_lineno = view->lineno;
4278
4279         reset_view(view);
4280
4281         if (!realloc_lines(view, view->line_size + 7))
4282                 return FALSE;
4283
4284         add_line_data(view, NULL, LINE_STAT_HEAD);
4285         if (is_initial_commit())
4286                 string_copy(status_onbranch, "Initial commit");
4287         else if (!*opt_head)
4288                 string_copy(status_onbranch, "Not currently on any branch");
4289         else if (!string_format(status_onbranch, "On branch %s", opt_head))
4290                 return FALSE;
4291
4292         system("git update-index -q --refresh >/dev/null 2>/dev/null");
4293
4294         if (is_initial_commit()) {
4295                 if (!status_run(view, STATUS_LIST_NO_HEAD_CMD, 'A', LINE_STAT_STAGED))
4296                         return FALSE;
4297         } else if (!status_run(view, STATUS_DIFF_INDEX_CMD, 0, LINE_STAT_STAGED)) {
4298                 return FALSE;
4299         }
4300
4301         if (!status_run(view, STATUS_DIFF_FILES_CMD, 0, LINE_STAT_UNSTAGED) ||
4302             !status_run(view, STATUS_LIST_OTHER_CMD, '?', LINE_STAT_UNTRACKED))
4303                 return FALSE;
4304
4305         /* If all went well restore the previous line number to stay in
4306          * the context or select a line with something that can be
4307          * updated. */
4308         if (prev_lineno >= view->lines)
4309                 prev_lineno = view->lines - 1;
4310         while (prev_lineno < view->lines && !view->line[prev_lineno].data)
4311                 prev_lineno++;
4312         while (prev_lineno > 0 && !view->line[prev_lineno].data)
4313                 prev_lineno--;
4314
4315         /* If the above fails, always skip the "On branch" line. */
4316         if (prev_lineno < view->lines)
4317                 view->lineno = prev_lineno;
4318         else
4319                 view->lineno = 1;
4320
4321         if (view->lineno < view->offset)
4322                 view->offset = view->lineno;
4323         else if (view->offset + view->height <= view->lineno)
4324                 view->offset = view->lineno - view->height + 1;
4325
4326         return TRUE;
4327 }
4328
4329 static bool
4330 status_draw(struct view *view, struct line *line, unsigned int lineno)
4331 {
4332         struct status *status = line->data;
4333         enum line_type type;
4334         const char *text;
4335
4336         if (!status) {
4337                 switch (line->type) {
4338                 case LINE_STAT_STAGED:
4339                         type = LINE_STAT_SECTION;
4340                         text = "Changes to be committed:";
4341                         break;
4342
4343                 case LINE_STAT_UNSTAGED:
4344                         type = LINE_STAT_SECTION;
4345                         text = "Changed but not updated:";
4346                         break;
4347
4348                 case LINE_STAT_UNTRACKED:
4349                         type = LINE_STAT_SECTION;
4350                         text = "Untracked files:";
4351                         break;
4352
4353                 case LINE_STAT_NONE:
4354                         type = LINE_DEFAULT;
4355                         text = "    (no files)";
4356                         break;
4357
4358                 case LINE_STAT_HEAD:
4359                         type = LINE_STAT_HEAD;
4360                         text = status_onbranch;
4361                         break;
4362
4363                 default:
4364                         return FALSE;
4365                 }
4366         } else {
4367                 static char buf[] = { '?', ' ', ' ', ' ', 0 };
4368
4369                 buf[0] = status->status;
4370                 if (draw_text(view, line->type, buf, TRUE))
4371                         return TRUE;
4372                 type = LINE_DEFAULT;
4373                 text = status->new.name;
4374         }
4375
4376         draw_text(view, type, text, TRUE);
4377         return TRUE;
4378 }
4379
4380 static enum request
4381 status_enter(struct view *view, struct line *line)
4382 {
4383         struct status *status = line->data;
4384         const char *oldpath = status ? status->old.name : NULL;
4385         /* Diffs for unmerged entries are empty when passing the new
4386          * path, so leave it empty. */
4387         const char *newpath = status && status->status != 'U' ? status->new.name : NULL;
4388         const char *info;
4389         enum open_flags split;
4390         struct view *stage = VIEW(REQ_VIEW_STAGE);
4391
4392         if (line->type == LINE_STAT_NONE ||
4393             (!status && line[1].type == LINE_STAT_NONE)) {
4394                 report("No file to diff");
4395                 return REQ_NONE;
4396         }
4397
4398         switch (line->type) {
4399         case LINE_STAT_STAGED:
4400                 if (is_initial_commit()) {
4401                         const char *no_head_diff_argv[] = {
4402                                 "git", "diff", "--no-color", "--patch-with-stat",
4403                                         "--", "/dev/null", newpath, NULL
4404                         };
4405
4406                         if (!prepare_update(stage, no_head_diff_argv, opt_cdup, FORMAT_DASH))
4407                                 return REQ_QUIT;
4408                 } else {
4409                         const char *index_show_argv[] = {
4410                                 "git", "diff-index", "--root", "--patch-with-stat",
4411                                         "-C", "-M", "--cached", "HEAD", "--",
4412                                         oldpath, newpath, NULL
4413                         };
4414
4415                         if (!prepare_update(stage, index_show_argv, opt_cdup, FORMAT_DASH))
4416                                 return REQ_QUIT;
4417                 }
4418
4419                 if (status)
4420                         info = "Staged changes to %s";
4421                 else
4422                         info = "Staged changes";
4423                 break;
4424
4425         case LINE_STAT_UNSTAGED:
4426         {
4427                 const char *files_show_argv[] = {
4428                         "git", "diff-files", "--root", "--patch-with-stat",
4429                                 "-C", "-M", "--", oldpath, newpath, NULL
4430                 };
4431
4432                 if (!prepare_update(stage, files_show_argv, opt_cdup, FORMAT_DASH))
4433                         return REQ_QUIT;
4434                 if (status)
4435                         info = "Unstaged changes to %s";
4436                 else
4437                         info = "Unstaged changes";
4438                 break;
4439         }
4440         case LINE_STAT_UNTRACKED:
4441                 if (opt_pipe)
4442                         return REQ_QUIT;
4443
4444                 if (!newpath) {
4445                         report("No file to show");
4446                         return REQ_NONE;
4447                 }
4448
4449                 if (!suffixcmp(status->new.name, -1, "/")) {
4450                         report("Cannot display a directory");
4451                         return REQ_NONE;
4452                 }
4453
4454                 opt_pipe = fopen(status->new.name, "r");
4455                 info = "Untracked file %s";
4456                 break;
4457
4458         case LINE_STAT_HEAD:
4459                 return REQ_NONE;
4460
4461         default:
4462                 die("line type %d not handled in switch", line->type);
4463         }
4464
4465         split = view_is_displayed(view) ? OPEN_SPLIT : 0;
4466         open_view(view, REQ_VIEW_STAGE, OPEN_REFRESH | split);
4467         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
4468                 if (status) {
4469                         stage_status = *status;
4470                 } else {
4471                         memset(&stage_status, 0, sizeof(stage_status));
4472                 }
4473
4474                 stage_line_type = line->type;
4475                 stage_chunks = 0;
4476                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
4477         }
4478
4479         return REQ_NONE;
4480 }
4481
4482 static bool
4483 status_exists(struct status *status, enum line_type type)
4484 {
4485         struct view *view = VIEW(REQ_VIEW_STATUS);
4486         struct line *line;
4487
4488         for (line = view->line; line < view->line + view->lines; line++) {
4489                 struct status *pos = line->data;
4490
4491                 if (line->type == type && pos &&
4492                     !strcmp(status->new.name, pos->new.name))
4493                         return TRUE;
4494         }
4495
4496         return FALSE;
4497 }
4498
4499
4500 static FILE *
4501 status_update_prepare(enum line_type type)
4502 {
4503         char cmd[SIZEOF_STR];
4504         size_t cmdsize = 0;
4505
4506         if (opt_cdup[0] &&
4507             type != LINE_STAT_UNTRACKED &&
4508             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
4509                 return NULL;
4510
4511         switch (type) {
4512         case LINE_STAT_STAGED:
4513                 string_add(cmd, cmdsize, "git update-index -z --index-info");
4514                 break;
4515
4516         case LINE_STAT_UNSTAGED:
4517         case LINE_STAT_UNTRACKED:
4518                 string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin");
4519                 break;
4520
4521         default:
4522                 die("line type %d not handled in switch", type);
4523         }
4524
4525         return popen(cmd, "w");
4526 }
4527
4528 static bool
4529 status_update_write(FILE *pipe, struct status *status, enum line_type type)
4530 {
4531         char buf[SIZEOF_STR];
4532         size_t bufsize = 0;
4533         size_t written = 0;
4534
4535         switch (type) {
4536         case LINE_STAT_STAGED:
4537                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
4538                                         status->old.mode,
4539                                         status->old.rev,
4540                                         status->old.name, 0))
4541                         return FALSE;
4542                 break;
4543
4544         case LINE_STAT_UNSTAGED:
4545         case LINE_STAT_UNTRACKED:
4546                 if (!string_format_from(buf, &bufsize, "%s%c", status->new.name, 0))
4547                         return FALSE;
4548                 break;
4549
4550         default:
4551                 die("line type %d not handled in switch", type);
4552         }
4553
4554         while (!ferror(pipe) && written < bufsize) {
4555                 written += fwrite(buf + written, 1, bufsize - written, pipe);
4556         }
4557
4558         return written == bufsize;
4559 }
4560
4561 static bool
4562 status_update_file(struct status *status, enum line_type type)
4563 {
4564         FILE *pipe = status_update_prepare(type);
4565         bool result;
4566
4567         if (!pipe)
4568                 return FALSE;
4569
4570         result = status_update_write(pipe, status, type);
4571         pclose(pipe);
4572         return result;
4573 }
4574
4575 static bool
4576 status_update_files(struct view *view, struct line *line)
4577 {
4578         FILE *pipe = status_update_prepare(line->type);
4579         bool result = TRUE;
4580         struct line *pos = view->line + view->lines;
4581         int files = 0;
4582         int file, done;
4583
4584         if (!pipe)
4585                 return FALSE;
4586
4587         for (pos = line; pos < view->line + view->lines && pos->data; pos++)
4588                 files++;
4589
4590         for (file = 0, done = 0; result && file < files; line++, file++) {
4591                 int almost_done = file * 100 / files;
4592
4593                 if (almost_done > done) {
4594                         done = almost_done;
4595                         string_format(view->ref, "updating file %u of %u (%d%% done)",
4596                                       file, files, done);
4597                         update_view_title(view);
4598                 }
4599                 result = status_update_write(pipe, line->data, line->type);
4600         }
4601
4602         pclose(pipe);
4603         return result;
4604 }
4605
4606 static bool
4607 status_update(struct view *view)
4608 {
4609         struct line *line = &view->line[view->lineno];
4610
4611         assert(view->lines);
4612
4613         if (!line->data) {
4614                 /* This should work even for the "On branch" line. */
4615                 if (line < view->line + view->lines && !line[1].data) {
4616                         report("Nothing to update");
4617                         return FALSE;
4618                 }
4619
4620                 if (!status_update_files(view, line + 1)) {
4621                         report("Failed to update file status");
4622                         return FALSE;
4623                 }
4624
4625         } else if (!status_update_file(line->data, line->type)) {
4626                 report("Failed to update file status");
4627                 return FALSE;
4628         }
4629
4630         return TRUE;
4631 }
4632
4633 static bool
4634 status_revert(struct status *status, enum line_type type, bool has_none)
4635 {
4636         if (!status || type != LINE_STAT_UNSTAGED) {
4637                 if (type == LINE_STAT_STAGED) {
4638                         report("Cannot revert changes to staged files");
4639                 } else if (type == LINE_STAT_UNTRACKED) {
4640                         report("Cannot revert changes to untracked files");
4641                 } else if (has_none) {
4642                         report("Nothing to revert");
4643                 } else {
4644                         report("Cannot revert changes to multiple files");
4645                 }
4646                 return FALSE;
4647
4648         } else {
4649                 const char *checkout_argv[] = {
4650                         "git", "checkout", "--", status->old.name, NULL
4651                 };
4652
4653                 if (!prompt_yesno("Are you sure you want to overwrite any changes?"))
4654                         return FALSE;
4655                 return run_io_fg(checkout_argv, opt_cdup);
4656         }
4657 }
4658
4659 static enum request
4660 status_request(struct view *view, enum request request, struct line *line)
4661 {
4662         struct status *status = line->data;
4663
4664         switch (request) {
4665         case REQ_STATUS_UPDATE:
4666                 if (!status_update(view))
4667                         return REQ_NONE;
4668                 break;
4669
4670         case REQ_STATUS_REVERT:
4671                 if (!status_revert(status, line->type, status_has_none(view, line)))
4672                         return REQ_NONE;
4673                 break;
4674
4675         case REQ_STATUS_MERGE:
4676                 if (!status || status->status != 'U') {
4677                         report("Merging only possible for files with unmerged status ('U').");
4678                         return REQ_NONE;
4679                 }
4680                 open_mergetool(status->new.name);
4681                 break;
4682
4683         case REQ_EDIT:
4684                 if (!status)
4685                         return request;
4686                 if (status->status == 'D') {
4687                         report("File has been deleted.");
4688                         return REQ_NONE;
4689                 }
4690
4691                 open_editor(status->status != '?', status->new.name);
4692                 break;
4693
4694         case REQ_VIEW_BLAME:
4695                 if (status) {
4696                         string_copy(opt_file, status->new.name);
4697                         opt_ref[0] = 0;
4698                 }
4699                 return request;
4700
4701         case REQ_ENTER:
4702                 /* After returning the status view has been split to
4703                  * show the stage view. No further reloading is
4704                  * necessary. */
4705                 status_enter(view, line);
4706                 return REQ_NONE;
4707
4708         case REQ_REFRESH:
4709                 /* Simply reload the view. */
4710                 break;
4711
4712         default:
4713                 return request;
4714         }
4715
4716         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
4717
4718         return REQ_NONE;
4719 }
4720
4721 static void
4722 status_select(struct view *view, struct line *line)
4723 {
4724         struct status *status = line->data;
4725         char file[SIZEOF_STR] = "all files";
4726         const char *text;
4727         const char *key;
4728
4729         if (status && !string_format(file, "'%s'", status->new.name))
4730                 return;
4731
4732         if (!status && line[1].type == LINE_STAT_NONE)
4733                 line++;
4734
4735         switch (line->type) {
4736         case LINE_STAT_STAGED:
4737                 text = "Press %s to unstage %s for commit";
4738                 break;
4739
4740         case LINE_STAT_UNSTAGED:
4741                 text = "Press %s to stage %s for commit";
4742                 break;
4743
4744         case LINE_STAT_UNTRACKED:
4745                 text = "Press %s to stage %s for addition";
4746                 break;
4747
4748         case LINE_STAT_HEAD:
4749         case LINE_STAT_NONE:
4750                 text = "Nothing to update";
4751                 break;
4752
4753         default:
4754                 die("line type %d not handled in switch", line->type);
4755         }
4756
4757         if (status && status->status == 'U') {
4758                 text = "Press %s to resolve conflict in %s";
4759                 key = get_key(REQ_STATUS_MERGE);
4760
4761         } else {
4762                 key = get_key(REQ_STATUS_UPDATE);
4763         }
4764
4765         string_format(view->ref, text, key, file);
4766 }
4767
4768 static bool
4769 status_grep(struct view *view, struct line *line)
4770 {
4771         struct status *status = line->data;
4772         enum { S_STATUS, S_NAME, S_END } state;
4773         char buf[2] = "?";
4774         regmatch_t pmatch;
4775
4776         if (!status)
4777                 return FALSE;
4778
4779         for (state = S_STATUS; state < S_END; state++) {
4780                 const char *text;
4781
4782                 switch (state) {
4783                 case S_NAME:    text = status->new.name;        break;
4784                 case S_STATUS:
4785                         buf[0] = status->status;
4786                         text = buf;
4787                         break;
4788
4789                 default:
4790                         return FALSE;
4791                 }
4792
4793                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4794                         return TRUE;
4795         }
4796
4797         return FALSE;
4798 }
4799
4800 static struct view_ops status_ops = {
4801         "file",
4802         NULL,
4803         status_open,
4804         NULL,
4805         status_draw,
4806         status_request,
4807         status_grep,
4808         status_select,
4809 };
4810
4811
4812 static bool
4813 stage_diff_line(FILE *pipe, struct line *line)
4814 {
4815         const char *buf = line->data;
4816         size_t bufsize = strlen(buf);
4817         size_t written = 0;
4818
4819         while (!ferror(pipe) && written < bufsize) {
4820                 written += fwrite(buf + written, 1, bufsize - written, pipe);
4821         }
4822
4823         fputc('\n', pipe);
4824
4825         return written == bufsize;
4826 }
4827
4828 static bool
4829 stage_diff_write(FILE *pipe, struct line *line, struct line *end)
4830 {
4831         while (line < end) {
4832                 if (!stage_diff_line(pipe, line++))
4833                         return FALSE;
4834                 if (line->type == LINE_DIFF_CHUNK ||
4835                     line->type == LINE_DIFF_HEADER)
4836                         break;
4837         }
4838
4839         return TRUE;
4840 }
4841
4842 static struct line *
4843 stage_diff_find(struct view *view, struct line *line, enum line_type type)
4844 {
4845         for (; view->line < line; line--)
4846                 if (line->type == type)
4847                         return line;
4848
4849         return NULL;
4850 }
4851
4852 static bool
4853 stage_apply_chunk(struct view *view, struct line *chunk, bool revert)
4854 {
4855         char cmd[SIZEOF_STR];
4856         size_t cmdsize = 0;
4857         struct line *diff_hdr;
4858         FILE *pipe;
4859
4860         diff_hdr = stage_diff_find(view, chunk, LINE_DIFF_HEADER);
4861         if (!diff_hdr)
4862                 return FALSE;
4863
4864         if (opt_cdup[0] &&
4865             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
4866                 return FALSE;
4867
4868         if (!string_format_from(cmd, &cmdsize,
4869                                 "git apply --whitespace=nowarn %s %s - && "
4870                                 "git update-index -q --unmerged --refresh 2>/dev/null",
4871                                 revert ? "" : "--cached",
4872                                 revert || stage_line_type == LINE_STAT_STAGED ? "-R" : ""))
4873                 return FALSE;
4874
4875         pipe = popen(cmd, "w");
4876         if (!pipe)
4877                 return FALSE;
4878
4879         if (!stage_diff_write(pipe, diff_hdr, chunk) ||
4880             !stage_diff_write(pipe, chunk, view->line + view->lines))
4881                 chunk = NULL;
4882
4883         pclose(pipe);
4884
4885         return chunk ? TRUE : FALSE;
4886 }
4887
4888 static bool
4889 stage_update(struct view *view, struct line *line)
4890 {
4891         struct line *chunk = NULL;
4892
4893         if (!is_initial_commit() && stage_line_type != LINE_STAT_UNTRACKED)
4894                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
4895
4896         if (chunk) {
4897                 if (!stage_apply_chunk(view, chunk, FALSE)) {
4898                         report("Failed to apply chunk");
4899                         return FALSE;
4900                 }
4901
4902         } else if (!stage_status.status) {
4903                 view = VIEW(REQ_VIEW_STATUS);
4904
4905                 for (line = view->line; line < view->line + view->lines; line++)
4906                         if (line->type == stage_line_type)
4907                                 break;
4908
4909                 if (!status_update_files(view, line + 1)) {
4910                         report("Failed to update files");
4911                         return FALSE;
4912                 }
4913
4914         } else if (!status_update_file(&stage_status, stage_line_type)) {
4915                 report("Failed to update file");
4916                 return FALSE;
4917         }
4918
4919         return TRUE;
4920 }
4921
4922 static bool
4923 stage_revert(struct view *view, struct line *line)
4924 {
4925         struct line *chunk = NULL;
4926
4927         if (!is_initial_commit() && stage_line_type == LINE_STAT_UNSTAGED)
4928                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
4929
4930         if (chunk) {
4931                 if (!prompt_yesno("Are you sure you want to revert changes?"))
4932                         return FALSE;
4933
4934                 if (!stage_apply_chunk(view, chunk, TRUE)) {
4935                         report("Failed to revert chunk");
4936                         return FALSE;
4937                 }
4938                 return TRUE;
4939
4940         } else {
4941                 return status_revert(stage_status.status ? &stage_status : NULL,
4942                                      stage_line_type, FALSE);
4943         }
4944 }
4945
4946
4947 static void
4948 stage_next(struct view *view, struct line *line)
4949 {
4950         int i;
4951
4952         if (!stage_chunks) {
4953                 static size_t alloc = 0;
4954                 int *tmp;
4955
4956                 for (line = view->line; line < view->line + view->lines; line++) {
4957                         if (line->type != LINE_DIFF_CHUNK)
4958                                 continue;
4959
4960                         tmp = realloc_items(stage_chunk, &alloc,
4961                                             stage_chunks, sizeof(*tmp));
4962                         if (!tmp) {
4963                                 report("Allocation failure");
4964                                 return;
4965                         }
4966
4967                         stage_chunk = tmp;
4968                         stage_chunk[stage_chunks++] = line - view->line;
4969                 }
4970         }
4971
4972         for (i = 0; i < stage_chunks; i++) {
4973                 if (stage_chunk[i] > view->lineno) {
4974                         do_scroll_view(view, stage_chunk[i] - view->lineno);
4975                         report("Chunk %d of %d", i + 1, stage_chunks);
4976                         return;
4977                 }
4978         }
4979
4980         report("No next chunk found");
4981 }
4982
4983 static enum request
4984 stage_request(struct view *view, enum request request, struct line *line)
4985 {
4986         switch (request) {
4987         case REQ_STATUS_UPDATE:
4988                 if (!stage_update(view, line))
4989                         return REQ_NONE;
4990                 break;
4991
4992         case REQ_STATUS_REVERT:
4993                 if (!stage_revert(view, line))
4994                         return REQ_NONE;
4995                 break;
4996
4997         case REQ_STAGE_NEXT:
4998                 if (stage_line_type == LINE_STAT_UNTRACKED) {
4999                         report("File is untracked; press %s to add",
5000                                get_key(REQ_STATUS_UPDATE));
5001                         return REQ_NONE;
5002                 }
5003                 stage_next(view, line);
5004                 return REQ_NONE;
5005
5006         case REQ_EDIT:
5007                 if (!stage_status.new.name[0])
5008                         return request;
5009                 if (stage_status.status == 'D') {
5010                         report("File has been deleted.");
5011                         return REQ_NONE;
5012                 }
5013
5014                 open_editor(stage_status.status != '?', stage_status.new.name);
5015                 break;
5016
5017         case REQ_REFRESH:
5018                 /* Reload everything ... */
5019                 break;
5020
5021         case REQ_VIEW_BLAME:
5022                 if (stage_status.new.name[0]) {
5023                         string_copy(opt_file, stage_status.new.name);
5024                         opt_ref[0] = 0;
5025                 }
5026                 return request;
5027
5028         case REQ_ENTER:
5029                 return pager_request(view, request, line);
5030
5031         default:
5032                 return request;
5033         }
5034
5035         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD | OPEN_NOMAXIMIZE);
5036
5037         /* Check whether the staged entry still exists, and close the
5038          * stage view if it doesn't. */
5039         if (!status_exists(&stage_status, stage_line_type))
5040                 return REQ_VIEW_CLOSE;
5041
5042         if (stage_line_type == LINE_STAT_UNTRACKED) {
5043                 if (!suffixcmp(stage_status.new.name, -1, "/")) {
5044                         report("Cannot display a directory");
5045                         return REQ_NONE;
5046                 }
5047
5048                 opt_pipe = fopen(stage_status.new.name, "r");
5049         }
5050         open_view(view, REQ_VIEW_STAGE, OPEN_REFRESH);
5051
5052         return REQ_NONE;
5053 }
5054
5055 static struct view_ops stage_ops = {
5056         "line",
5057         NULL,
5058         NULL,
5059         pager_read,
5060         pager_draw,
5061         stage_request,
5062         pager_grep,
5063         pager_select,
5064 };
5065
5066
5067 /*
5068  * Revision graph
5069  */
5070
5071 struct commit {
5072         char id[SIZEOF_REV];            /* SHA1 ID. */
5073         char title[128];                /* First line of the commit message. */
5074         char author[75];                /* Author of the commit. */
5075         struct tm time;                 /* Date from the author ident. */
5076         struct ref **refs;              /* Repository references. */
5077         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
5078         size_t graph_size;              /* The width of the graph array. */
5079         bool has_parents;               /* Rewritten --parents seen. */
5080 };
5081
5082 /* Size of rev graph with no  "padding" columns */
5083 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
5084
5085 struct rev_graph {
5086         struct rev_graph *prev, *next, *parents;
5087         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
5088         size_t size;
5089         struct commit *commit;
5090         size_t pos;
5091         unsigned int boundary:1;
5092 };
5093
5094 /* Parents of the commit being visualized. */
5095 static struct rev_graph graph_parents[4];
5096
5097 /* The current stack of revisions on the graph. */
5098 static struct rev_graph graph_stacks[4] = {
5099         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
5100         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
5101         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
5102         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
5103 };
5104
5105 static inline bool
5106 graph_parent_is_merge(struct rev_graph *graph)
5107 {
5108         return graph->parents->size > 1;
5109 }
5110
5111 static inline void
5112 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
5113 {
5114         struct commit *commit = graph->commit;
5115
5116         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
5117                 commit->graph[commit->graph_size++] = symbol;
5118 }
5119
5120 static void
5121 clear_rev_graph(struct rev_graph *graph)
5122 {
5123         graph->boundary = 0;
5124         graph->size = graph->pos = 0;
5125         graph->commit = NULL;
5126         memset(graph->parents, 0, sizeof(*graph->parents));
5127 }
5128
5129 static void
5130 done_rev_graph(struct rev_graph *graph)
5131 {
5132         if (graph_parent_is_merge(graph) &&
5133             graph->pos < graph->size - 1 &&
5134             graph->next->size == graph->size + graph->parents->size - 1) {
5135                 size_t i = graph->pos + graph->parents->size - 1;
5136
5137                 graph->commit->graph_size = i * 2;
5138                 while (i < graph->next->size - 1) {
5139                         append_to_rev_graph(graph, ' ');
5140                         append_to_rev_graph(graph, '\\');
5141                         i++;
5142                 }
5143         }
5144
5145         clear_rev_graph(graph);
5146 }
5147
5148 static void
5149 push_rev_graph(struct rev_graph *graph, const char *parent)
5150 {
5151         int i;
5152
5153         /* "Collapse" duplicate parents lines.
5154          *
5155          * FIXME: This needs to also update update the drawn graph but
5156          * for now it just serves as a method for pruning graph lines. */
5157         for (i = 0; i < graph->size; i++)
5158                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
5159                         return;
5160
5161         if (graph->size < SIZEOF_REVITEMS) {
5162                 string_copy_rev(graph->rev[graph->size++], parent);
5163         }
5164 }
5165
5166 static chtype
5167 get_rev_graph_symbol(struct rev_graph *graph)
5168 {
5169         chtype symbol;
5170
5171         if (graph->boundary)
5172                 symbol = REVGRAPH_BOUND;
5173         else if (graph->parents->size == 0)
5174                 symbol = REVGRAPH_INIT;
5175         else if (graph_parent_is_merge(graph))
5176                 symbol = REVGRAPH_MERGE;
5177         else if (graph->pos >= graph->size)
5178                 symbol = REVGRAPH_BRANCH;
5179         else
5180                 symbol = REVGRAPH_COMMIT;
5181
5182         return symbol;
5183 }
5184
5185 static void
5186 draw_rev_graph(struct rev_graph *graph)
5187 {
5188         struct rev_filler {
5189                 chtype separator, line;
5190         };
5191         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
5192         static struct rev_filler fillers[] = {
5193                 { ' ',  '|' },
5194                 { '`',  '.' },
5195                 { '\'', ' ' },
5196                 { '/',  ' ' },
5197         };
5198         chtype symbol = get_rev_graph_symbol(graph);
5199         struct rev_filler *filler;
5200         size_t i;
5201
5202         if (opt_line_graphics)
5203                 fillers[DEFAULT].line = line_graphics[LINE_GRAPHIC_VLINE];
5204
5205         filler = &fillers[DEFAULT];
5206
5207         for (i = 0; i < graph->pos; i++) {
5208                 append_to_rev_graph(graph, filler->line);
5209                 if (graph_parent_is_merge(graph->prev) &&
5210                     graph->prev->pos == i)
5211                         filler = &fillers[RSHARP];
5212
5213                 append_to_rev_graph(graph, filler->separator);
5214         }
5215
5216         /* Place the symbol for this revision. */
5217         append_to_rev_graph(graph, symbol);
5218
5219         if (graph->prev->size > graph->size)
5220                 filler = &fillers[RDIAG];
5221         else
5222                 filler = &fillers[DEFAULT];
5223
5224         i++;
5225
5226         for (; i < graph->size; i++) {
5227                 append_to_rev_graph(graph, filler->separator);
5228                 append_to_rev_graph(graph, filler->line);
5229                 if (graph_parent_is_merge(graph->prev) &&
5230                     i < graph->prev->pos + graph->parents->size)
5231                         filler = &fillers[RSHARP];
5232                 if (graph->prev->size > graph->size)
5233                         filler = &fillers[LDIAG];
5234         }
5235
5236         if (graph->prev->size > graph->size) {
5237                 append_to_rev_graph(graph, filler->separator);
5238                 if (filler->line != ' ')
5239                         append_to_rev_graph(graph, filler->line);
5240         }
5241 }
5242
5243 /* Prepare the next rev graph */
5244 static void
5245 prepare_rev_graph(struct rev_graph *graph)
5246 {
5247         size_t i;
5248
5249         /* First, traverse all lines of revisions up to the active one. */
5250         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
5251                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
5252                         break;
5253
5254                 push_rev_graph(graph->next, graph->rev[graph->pos]);
5255         }
5256
5257         /* Interleave the new revision parent(s). */
5258         for (i = 0; !graph->boundary && i < graph->parents->size; i++)
5259                 push_rev_graph(graph->next, graph->parents->rev[i]);
5260
5261         /* Lastly, put any remaining revisions. */
5262         for (i = graph->pos + 1; i < graph->size; i++)
5263                 push_rev_graph(graph->next, graph->rev[i]);
5264 }
5265
5266 static void
5267 update_rev_graph(struct rev_graph *graph)
5268 {
5269         /* If this is the finalizing update ... */
5270         if (graph->commit)
5271                 prepare_rev_graph(graph);
5272
5273         /* Graph visualization needs a one rev look-ahead,
5274          * so the first update doesn't visualize anything. */
5275         if (!graph->prev->commit)
5276                 return;
5277
5278         draw_rev_graph(graph->prev);
5279         done_rev_graph(graph->prev->prev);
5280 }
5281
5282
5283 /*
5284  * Main view backend
5285  */
5286
5287 static const char *main_argv[SIZEOF_ARG] = {
5288         "git", "log", "--no-color", "--pretty=raw", "--parents",
5289                       "--topo-order", "%(head)", NULL
5290 };
5291
5292 static bool
5293 main_draw(struct view *view, struct line *line, unsigned int lineno)
5294 {
5295         struct commit *commit = line->data;
5296
5297         if (!*commit->author)
5298                 return FALSE;
5299
5300         if (opt_date && draw_date(view, &commit->time))
5301                 return TRUE;
5302
5303         if (opt_author &&
5304             draw_field(view, LINE_MAIN_AUTHOR, commit->author, opt_author_cols, TRUE))
5305                 return TRUE;
5306
5307         if (opt_rev_graph && commit->graph_size &&
5308             draw_graphic(view, LINE_MAIN_REVGRAPH, commit->graph, commit->graph_size))
5309                 return TRUE;
5310
5311         if (opt_show_refs && commit->refs) {
5312                 size_t i = 0;
5313
5314                 do {
5315                         enum line_type type;
5316
5317                         if (commit->refs[i]->head)
5318                                 type = LINE_MAIN_HEAD;
5319                         else if (commit->refs[i]->ltag)
5320                                 type = LINE_MAIN_LOCAL_TAG;
5321                         else if (commit->refs[i]->tag)
5322                                 type = LINE_MAIN_TAG;
5323                         else if (commit->refs[i]->tracked)
5324                                 type = LINE_MAIN_TRACKED;
5325                         else if (commit->refs[i]->remote)
5326                                 type = LINE_MAIN_REMOTE;
5327                         else
5328                                 type = LINE_MAIN_REF;
5329
5330                         if (draw_text(view, type, "[", TRUE) ||
5331                             draw_text(view, type, commit->refs[i]->name, TRUE) ||
5332                             draw_text(view, type, "]", TRUE))
5333                                 return TRUE;
5334
5335                         if (draw_text(view, LINE_DEFAULT, " ", TRUE))
5336                                 return TRUE;
5337                 } while (commit->refs[i++]->next);
5338         }
5339
5340         draw_text(view, LINE_DEFAULT, commit->title, TRUE);
5341         return TRUE;
5342 }
5343
5344 /* Reads git log --pretty=raw output and parses it into the commit struct. */
5345 static bool
5346 main_read(struct view *view, char *line)
5347 {
5348         static struct rev_graph *graph = graph_stacks;
5349         enum line_type type;
5350         struct commit *commit;
5351
5352         if (!line) {
5353                 int i;
5354
5355                 if (!view->lines && !view->parent)
5356                         die("No revisions match the given arguments.");
5357                 if (view->lines > 0) {
5358                         commit = view->line[view->lines - 1].data;
5359                         if (!*commit->author) {
5360                                 view->lines--;
5361                                 free(commit);
5362                                 graph->commit = NULL;
5363                         }
5364                 }
5365                 update_rev_graph(graph);
5366
5367                 for (i = 0; i < ARRAY_SIZE(graph_stacks); i++)
5368                         clear_rev_graph(&graph_stacks[i]);
5369                 return TRUE;
5370         }
5371
5372         type = get_line_type(line);
5373         if (type == LINE_COMMIT) {
5374                 commit = calloc(1, sizeof(struct commit));
5375                 if (!commit)
5376                         return FALSE;
5377
5378                 line += STRING_SIZE("commit ");
5379                 if (*line == '-') {
5380                         graph->boundary = 1;
5381                         line++;
5382                 }
5383
5384                 string_copy_rev(commit->id, line);
5385                 commit->refs = get_refs(commit->id);
5386                 graph->commit = commit;
5387                 add_line_data(view, commit, LINE_MAIN_COMMIT);
5388
5389                 while ((line = strchr(line, ' '))) {
5390                         line++;
5391                         push_rev_graph(graph->parents, line);
5392                         commit->has_parents = TRUE;
5393                 }
5394                 return TRUE;
5395         }
5396
5397         if (!view->lines)
5398                 return TRUE;
5399         commit = view->line[view->lines - 1].data;
5400
5401         switch (type) {
5402         case LINE_PARENT:
5403                 if (commit->has_parents)
5404                         break;
5405                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
5406                 break;
5407
5408         case LINE_AUTHOR:
5409         {
5410                 /* Parse author lines where the name may be empty:
5411                  *      author  <email@address.tld> 1138474660 +0100
5412                  */
5413                 char *ident = line + STRING_SIZE("author ");
5414                 char *nameend = strchr(ident, '<');
5415                 char *emailend = strchr(ident, '>');
5416
5417                 if (!nameend || !emailend)
5418                         break;
5419
5420                 update_rev_graph(graph);
5421                 graph = graph->next;
5422
5423                 *nameend = *emailend = 0;
5424                 ident = chomp_string(ident);
5425                 if (!*ident) {
5426                         ident = chomp_string(nameend + 1);
5427                         if (!*ident)
5428                                 ident = "Unknown";
5429                 }
5430
5431                 string_ncopy(commit->author, ident, strlen(ident));
5432
5433                 /* Parse epoch and timezone */
5434                 if (emailend[1] == ' ') {
5435                         char *secs = emailend + 2;
5436                         char *zone = strchr(secs, ' ');
5437                         time_t time = (time_t) atol(secs);
5438
5439                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
5440                                 long tz;
5441
5442                                 zone++;
5443                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
5444                                 tz += ('0' - zone[2]) * 60 * 60;
5445                                 tz += ('0' - zone[3]) * 60;
5446                                 tz += ('0' - zone[4]) * 60;
5447
5448                                 if (zone[0] == '-')
5449                                         tz = -tz;
5450
5451                                 time -= tz;
5452                         }
5453
5454                         gmtime_r(&time, &commit->time);
5455                 }
5456                 break;
5457         }
5458         default:
5459                 /* Fill in the commit title if it has not already been set. */
5460                 if (commit->title[0])
5461                         break;
5462
5463                 /* Require titles to start with a non-space character at the
5464                  * offset used by git log. */
5465                 if (strncmp(line, "    ", 4))
5466                         break;
5467                 line += 4;
5468                 /* Well, if the title starts with a whitespace character,
5469                  * try to be forgiving.  Otherwise we end up with no title. */
5470                 while (isspace(*line))
5471                         line++;
5472                 if (*line == '\0')
5473                         break;
5474                 /* FIXME: More graceful handling of titles; append "..." to
5475                  * shortened titles, etc. */
5476
5477                 string_ncopy(commit->title, line, strlen(line));
5478         }
5479
5480         return TRUE;
5481 }
5482
5483 static enum request
5484 main_request(struct view *view, enum request request, struct line *line)
5485 {
5486         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
5487
5488         switch (request) {
5489         case REQ_ENTER:
5490                 open_view(view, REQ_VIEW_DIFF, flags);
5491                 break;
5492         case REQ_REFRESH:
5493                 load_refs();
5494                 open_view(view, REQ_VIEW_MAIN, OPEN_REFRESH);
5495                 break;
5496         default:
5497                 return request;
5498         }
5499
5500         return REQ_NONE;
5501 }
5502
5503 static bool
5504 grep_refs(struct ref **refs, regex_t *regex)
5505 {
5506         regmatch_t pmatch;
5507         size_t i = 0;
5508
5509         if (!refs)
5510                 return FALSE;
5511         do {
5512                 if (regexec(regex, refs[i]->name, 1, &pmatch, 0) != REG_NOMATCH)
5513                         return TRUE;
5514         } while (refs[i++]->next);
5515
5516         return FALSE;
5517 }
5518
5519 static bool
5520 main_grep(struct view *view, struct line *line)
5521 {
5522         struct commit *commit = line->data;
5523         enum { S_TITLE, S_AUTHOR, S_DATE, S_REFS, S_END } state;
5524         char buf[DATE_COLS + 1];
5525         regmatch_t pmatch;
5526
5527         for (state = S_TITLE; state < S_END; state++) {
5528                 char *text;
5529
5530                 switch (state) {
5531                 case S_TITLE:   text = commit->title;   break;
5532                 case S_AUTHOR:
5533                         if (!opt_author)
5534                                 continue;
5535                         text = commit->author;
5536                         break;
5537                 case S_DATE:
5538                         if (!opt_date)
5539                                 continue;
5540                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
5541                                 continue;
5542                         text = buf;
5543                         break;
5544                 case S_REFS:
5545                         if (!opt_show_refs)
5546                                 continue;
5547                         if (grep_refs(commit->refs, view->regex) == TRUE)
5548                                 return TRUE;
5549                         continue;
5550                 default:
5551                         return FALSE;
5552                 }
5553
5554                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5555                         return TRUE;
5556         }
5557
5558         return FALSE;
5559 }
5560
5561 static void
5562 main_select(struct view *view, struct line *line)
5563 {
5564         struct commit *commit = line->data;
5565
5566         string_copy_rev(view->ref, commit->id);
5567         string_copy_rev(ref_commit, view->ref);
5568 }
5569
5570 static struct view_ops main_ops = {
5571         "commit",
5572         main_argv,
5573         NULL,
5574         main_read,
5575         main_draw,
5576         main_request,
5577         main_grep,
5578         main_select,
5579 };
5580
5581
5582 /*
5583  * Unicode / UTF-8 handling
5584  *
5585  * NOTE: Much of the following code for dealing with unicode is derived from
5586  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
5587  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
5588  */
5589
5590 /* I've (over)annotated a lot of code snippets because I am not entirely
5591  * confident that the approach taken by this small UTF-8 interface is correct.
5592  * --jonas */
5593
5594 static inline int
5595 unicode_width(unsigned long c)
5596 {
5597         if (c >= 0x1100 &&
5598            (c <= 0x115f                         /* Hangul Jamo */
5599             || c == 0x2329
5600             || c == 0x232a
5601             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
5602                                                 /* CJK ... Yi */
5603             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
5604             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
5605             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
5606             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
5607             || (c >= 0xffe0  && c <= 0xffe6)
5608             || (c >= 0x20000 && c <= 0x2fffd)
5609             || (c >= 0x30000 && c <= 0x3fffd)))
5610                 return 2;
5611
5612         if (c == '\t')
5613                 return opt_tab_size;
5614
5615         return 1;
5616 }
5617
5618 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
5619  * Illegal bytes are set one. */
5620 static const unsigned char utf8_bytes[256] = {
5621         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,
5622         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,
5623         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,
5624         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,
5625         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,
5626         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,
5627         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,
5628         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,
5629 };
5630
5631 /* Decode UTF-8 multi-byte representation into a unicode character. */
5632 static inline unsigned long
5633 utf8_to_unicode(const char *string, size_t length)
5634 {
5635         unsigned long unicode;
5636
5637         switch (length) {
5638         case 1:
5639                 unicode  =   string[0];
5640                 break;
5641         case 2:
5642                 unicode  =  (string[0] & 0x1f) << 6;
5643                 unicode +=  (string[1] & 0x3f);
5644                 break;
5645         case 3:
5646                 unicode  =  (string[0] & 0x0f) << 12;
5647                 unicode += ((string[1] & 0x3f) << 6);
5648                 unicode +=  (string[2] & 0x3f);
5649                 break;
5650         case 4:
5651                 unicode  =  (string[0] & 0x0f) << 18;
5652                 unicode += ((string[1] & 0x3f) << 12);
5653                 unicode += ((string[2] & 0x3f) << 6);
5654                 unicode +=  (string[3] & 0x3f);
5655                 break;
5656         case 5:
5657                 unicode  =  (string[0] & 0x0f) << 24;
5658                 unicode += ((string[1] & 0x3f) << 18);
5659                 unicode += ((string[2] & 0x3f) << 12);
5660                 unicode += ((string[3] & 0x3f) << 6);
5661                 unicode +=  (string[4] & 0x3f);
5662                 break;
5663         case 6:
5664                 unicode  =  (string[0] & 0x01) << 30;
5665                 unicode += ((string[1] & 0x3f) << 24);
5666                 unicode += ((string[2] & 0x3f) << 18);
5667                 unicode += ((string[3] & 0x3f) << 12);
5668                 unicode += ((string[4] & 0x3f) << 6);
5669                 unicode +=  (string[5] & 0x3f);
5670                 break;
5671         default:
5672                 die("Invalid unicode length");
5673         }
5674
5675         /* Invalid characters could return the special 0xfffd value but NUL
5676          * should be just as good. */
5677         return unicode > 0xffff ? 0 : unicode;
5678 }
5679
5680 /* Calculates how much of string can be shown within the given maximum width
5681  * and sets trimmed parameter to non-zero value if all of string could not be
5682  * shown. If the reserve flag is TRUE, it will reserve at least one
5683  * trailing character, which can be useful when drawing a delimiter.
5684  *
5685  * Returns the number of bytes to output from string to satisfy max_width. */
5686 static size_t
5687 utf8_length(const char *string, int *width, size_t max_width, int *trimmed, bool reserve)
5688 {
5689         const char *start = string;
5690         const char *end = strchr(string, '\0');
5691         unsigned char last_bytes = 0;
5692         size_t last_ucwidth = 0;
5693
5694         *width = 0;
5695         *trimmed = 0;
5696
5697         while (string < end) {
5698                 int c = *(unsigned char *) string;
5699                 unsigned char bytes = utf8_bytes[c];
5700                 size_t ucwidth;
5701                 unsigned long unicode;
5702
5703                 if (string + bytes > end)
5704                         break;
5705
5706                 /* Change representation to figure out whether
5707                  * it is a single- or double-width character. */
5708
5709                 unicode = utf8_to_unicode(string, bytes);
5710                 /* FIXME: Graceful handling of invalid unicode character. */
5711                 if (!unicode)
5712                         break;
5713
5714                 ucwidth = unicode_width(unicode);
5715                 *width  += ucwidth;
5716                 if (*width > max_width) {
5717                         *trimmed = 1;
5718                         *width -= ucwidth;
5719                         if (reserve && *width == max_width) {
5720                                 string -= last_bytes;
5721                                 *width -= last_ucwidth;
5722                         }
5723                         break;
5724                 }
5725
5726                 string  += bytes;
5727                 last_bytes = bytes;
5728                 last_ucwidth = ucwidth;
5729         }
5730
5731         return string - start;
5732 }
5733
5734
5735 /*
5736  * Status management
5737  */
5738
5739 /* Whether or not the curses interface has been initialized. */
5740 static bool cursed = FALSE;
5741
5742 /* The status window is used for polling keystrokes. */
5743 static WINDOW *status_win;
5744
5745 static bool status_empty = TRUE;
5746
5747 /* Update status and title window. */
5748 static void
5749 report(const char *msg, ...)
5750 {
5751         struct view *view = display[current_view];
5752
5753         if (input_mode)
5754                 return;
5755
5756         if (!view) {
5757                 char buf[SIZEOF_STR];
5758                 va_list args;
5759
5760                 va_start(args, msg);
5761                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
5762                         buf[sizeof(buf) - 1] = 0;
5763                         buf[sizeof(buf) - 2] = '.';
5764                         buf[sizeof(buf) - 3] = '.';
5765                         buf[sizeof(buf) - 4] = '.';
5766                 }
5767                 va_end(args);
5768                 die("%s", buf);
5769         }
5770
5771         if (!status_empty || *msg) {
5772                 va_list args;
5773
5774                 va_start(args, msg);
5775
5776                 wmove(status_win, 0, 0);
5777                 if (*msg) {
5778                         vwprintw(status_win, msg, args);
5779                         status_empty = FALSE;
5780                 } else {
5781                         status_empty = TRUE;
5782                 }
5783                 wclrtoeol(status_win);
5784                 wrefresh(status_win);
5785
5786                 va_end(args);
5787         }
5788
5789         update_view_title(view);
5790         update_display_cursor(view);
5791 }
5792
5793 /* Controls when nodelay should be in effect when polling user input. */
5794 static void
5795 set_nonblocking_input(bool loading)
5796 {
5797         static unsigned int loading_views;
5798
5799         if ((loading == FALSE && loading_views-- == 1) ||
5800             (loading == TRUE  && loading_views++ == 0))
5801                 nodelay(status_win, loading);
5802 }
5803
5804 static void
5805 init_display(void)
5806 {
5807         int x, y;
5808
5809         /* Initialize the curses library */
5810         if (isatty(STDIN_FILENO)) {
5811                 cursed = !!initscr();
5812                 opt_tty = stdin;
5813         } else {
5814                 /* Leave stdin and stdout alone when acting as a pager. */
5815                 opt_tty = fopen("/dev/tty", "r+");
5816                 if (!opt_tty)
5817                         die("Failed to open /dev/tty");
5818                 cursed = !!newterm(NULL, opt_tty, opt_tty);
5819         }
5820
5821         if (!cursed)
5822                 die("Failed to initialize curses");
5823
5824         nonl();         /* Tell curses not to do NL->CR/NL on output */
5825         cbreak();       /* Take input chars one at a time, no wait for \n */
5826         noecho();       /* Don't echo input */
5827         leaveok(stdscr, TRUE);
5828
5829         if (has_colors())
5830                 init_colors();
5831
5832         getmaxyx(stdscr, y, x);
5833         status_win = newwin(1, 0, y - 1, 0);
5834         if (!status_win)
5835                 die("Failed to create status window");
5836
5837         /* Enable keyboard mapping */
5838         keypad(status_win, TRUE);
5839         wbkgdset(status_win, get_line_attr(LINE_STATUS));
5840
5841         TABSIZE = opt_tab_size;
5842         if (opt_line_graphics) {
5843                 line_graphics[LINE_GRAPHIC_VLINE] = ACS_VLINE;
5844         }
5845 }
5846
5847 static bool
5848 prompt_yesno(const char *prompt)
5849 {
5850         enum { WAIT, STOP, CANCEL  } status = WAIT;
5851         bool answer = FALSE;
5852
5853         while (status == WAIT) {
5854                 struct view *view;
5855                 int i, key;
5856
5857                 input_mode = TRUE;
5858
5859                 foreach_view (view, i)
5860                         update_view(view);
5861
5862                 input_mode = FALSE;
5863
5864                 mvwprintw(status_win, 0, 0, "%s [Yy]/[Nn]", prompt);
5865                 wclrtoeol(status_win);
5866
5867                 /* Refresh, accept single keystroke of input */
5868                 key = wgetch(status_win);
5869                 switch (key) {
5870                 case ERR:
5871                         break;
5872
5873                 case 'y':
5874                 case 'Y':
5875                         answer = TRUE;
5876                         status = STOP;
5877                         break;
5878
5879                 case KEY_ESC:
5880                 case KEY_RETURN:
5881                 case KEY_ENTER:
5882                 case KEY_BACKSPACE:
5883                 case 'n':
5884                 case 'N':
5885                 case '\n':
5886                 default:
5887                         answer = FALSE;
5888                         status = CANCEL;
5889                 }
5890         }
5891
5892         /* Clear the status window */
5893         status_empty = FALSE;
5894         report("");
5895
5896         return answer;
5897 }
5898
5899 static char *
5900 read_prompt(const char *prompt)
5901 {
5902         enum { READING, STOP, CANCEL } status = READING;
5903         static char buf[SIZEOF_STR];
5904         int pos = 0;
5905
5906         while (status == READING) {
5907                 struct view *view;
5908                 int i, key;
5909
5910                 input_mode = TRUE;
5911
5912                 foreach_view (view, i)
5913                         update_view(view);
5914
5915                 input_mode = FALSE;
5916
5917                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
5918                 wclrtoeol(status_win);
5919
5920                 /* Refresh, accept single keystroke of input */
5921                 key = wgetch(status_win);
5922                 switch (key) {
5923                 case KEY_RETURN:
5924                 case KEY_ENTER:
5925                 case '\n':
5926                         status = pos ? STOP : CANCEL;
5927                         break;
5928
5929                 case KEY_BACKSPACE:
5930                         if (pos > 0)
5931                                 pos--;
5932                         else
5933                                 status = CANCEL;
5934                         break;
5935
5936                 case KEY_ESC:
5937                         status = CANCEL;
5938                         break;
5939
5940                 case ERR:
5941                         break;
5942
5943                 default:
5944                         if (pos >= sizeof(buf)) {
5945                                 report("Input string too long");
5946                                 return NULL;
5947                         }
5948
5949                         if (isprint(key))
5950                                 buf[pos++] = (char) key;
5951                 }
5952         }
5953
5954         /* Clear the status window */
5955         status_empty = FALSE;
5956         report("");
5957
5958         if (status == CANCEL)
5959                 return NULL;
5960
5961         buf[pos++] = 0;
5962
5963         return buf;
5964 }
5965
5966 /*
5967  * Repository references
5968  */
5969
5970 static struct ref *refs = NULL;
5971 static size_t refs_alloc = 0;
5972 static size_t refs_size = 0;
5973
5974 /* Id <-> ref store */
5975 static struct ref ***id_refs = NULL;
5976 static size_t id_refs_alloc = 0;
5977 static size_t id_refs_size = 0;
5978
5979 static int
5980 compare_refs(const void *ref1_, const void *ref2_)
5981 {
5982         const struct ref *ref1 = *(const struct ref **)ref1_;
5983         const struct ref *ref2 = *(const struct ref **)ref2_;
5984
5985         if (ref1->tag != ref2->tag)
5986                 return ref2->tag - ref1->tag;
5987         if (ref1->ltag != ref2->ltag)
5988                 return ref2->ltag - ref2->ltag;
5989         if (ref1->head != ref2->head)
5990                 return ref2->head - ref1->head;
5991         if (ref1->tracked != ref2->tracked)
5992                 return ref2->tracked - ref1->tracked;
5993         if (ref1->remote != ref2->remote)
5994                 return ref2->remote - ref1->remote;
5995         return strcmp(ref1->name, ref2->name);
5996 }
5997
5998 static struct ref **
5999 get_refs(const char *id)
6000 {
6001         struct ref ***tmp_id_refs;
6002         struct ref **ref_list = NULL;
6003         size_t ref_list_alloc = 0;
6004         size_t ref_list_size = 0;
6005         size_t i;
6006
6007         for (i = 0; i < id_refs_size; i++)
6008                 if (!strcmp(id, id_refs[i][0]->id))
6009                         return id_refs[i];
6010
6011         tmp_id_refs = realloc_items(id_refs, &id_refs_alloc, id_refs_size + 1,
6012                                     sizeof(*id_refs));
6013         if (!tmp_id_refs)
6014                 return NULL;
6015
6016         id_refs = tmp_id_refs;
6017
6018         for (i = 0; i < refs_size; i++) {
6019                 struct ref **tmp;
6020
6021                 if (strcmp(id, refs[i].id))
6022                         continue;
6023
6024                 tmp = realloc_items(ref_list, &ref_list_alloc,
6025                                     ref_list_size + 1, sizeof(*ref_list));
6026                 if (!tmp) {
6027                         if (ref_list)
6028                                 free(ref_list);
6029                         return NULL;
6030                 }
6031
6032                 ref_list = tmp;
6033                 ref_list[ref_list_size] = &refs[i];
6034                 /* XXX: The properties of the commit chains ensures that we can
6035                  * safely modify the shared ref. The repo references will
6036                  * always be similar for the same id. */
6037                 ref_list[ref_list_size]->next = 1;
6038
6039                 ref_list_size++;
6040         }
6041
6042         if (ref_list) {
6043                 qsort(ref_list, ref_list_size, sizeof(*ref_list), compare_refs);
6044                 ref_list[ref_list_size - 1]->next = 0;
6045                 id_refs[id_refs_size++] = ref_list;
6046         }
6047
6048         return ref_list;
6049 }
6050
6051 static int
6052 read_ref(char *id, size_t idlen, char *name, size_t namelen)
6053 {
6054         struct ref *ref;
6055         bool tag = FALSE;
6056         bool ltag = FALSE;
6057         bool remote = FALSE;
6058         bool tracked = FALSE;
6059         bool check_replace = FALSE;
6060         bool head = FALSE;
6061
6062         if (!prefixcmp(name, "refs/tags/")) {
6063                 if (!suffixcmp(name, namelen, "^{}")) {
6064                         namelen -= 3;
6065                         name[namelen] = 0;
6066                         if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
6067                                 check_replace = TRUE;
6068                 } else {
6069                         ltag = TRUE;
6070                 }
6071
6072                 tag = TRUE;
6073                 namelen -= STRING_SIZE("refs/tags/");
6074                 name    += STRING_SIZE("refs/tags/");
6075
6076         } else if (!prefixcmp(name, "refs/remotes/")) {
6077                 remote = TRUE;
6078                 namelen -= STRING_SIZE("refs/remotes/");
6079                 name    += STRING_SIZE("refs/remotes/");
6080                 tracked  = !strcmp(opt_remote, name);
6081
6082         } else if (!prefixcmp(name, "refs/heads/")) {
6083                 namelen -= STRING_SIZE("refs/heads/");
6084                 name    += STRING_SIZE("refs/heads/");
6085                 head     = !strncmp(opt_head, name, namelen);
6086
6087         } else if (!strcmp(name, "HEAD")) {
6088                 string_ncopy(opt_head_rev, id, idlen);
6089                 return OK;
6090         }
6091
6092         if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
6093                 /* it's an annotated tag, replace the previous sha1 with the
6094                  * resolved commit id; relies on the fact git-ls-remote lists
6095                  * the commit id of an annotated tag right before the commit id
6096                  * it points to. */
6097                 refs[refs_size - 1].ltag = ltag;
6098                 string_copy_rev(refs[refs_size - 1].id, id);
6099
6100                 return OK;
6101         }
6102         refs = realloc_items(refs, &refs_alloc, refs_size + 1, sizeof(*refs));
6103         if (!refs)
6104                 return ERR;
6105
6106         ref = &refs[refs_size++];
6107         ref->name = malloc(namelen + 1);
6108         if (!ref->name)
6109                 return ERR;
6110
6111         strncpy(ref->name, name, namelen);
6112         ref->name[namelen] = 0;
6113         ref->head = head;
6114         ref->tag = tag;
6115         ref->ltag = ltag;
6116         ref->remote = remote;
6117         ref->tracked = tracked;
6118         string_copy_rev(ref->id, id);
6119
6120         return OK;
6121 }
6122
6123 static int
6124 load_refs(void)
6125 {
6126         const char *cmd_env = getenv("TIG_LS_REMOTE");
6127         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
6128
6129         if (!*opt_git_dir)
6130                 return OK;
6131
6132         while (refs_size > 0)
6133                 free(refs[--refs_size].name);
6134         while (id_refs_size > 0)
6135                 free(id_refs[--id_refs_size]);
6136
6137         return read_properties(popen(cmd, "r"), "\t", read_ref);
6138 }
6139
6140 static int
6141 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
6142 {
6143         if (!strcmp(name, "i18n.commitencoding"))
6144                 string_ncopy(opt_encoding, value, valuelen);
6145
6146         if (!strcmp(name, "core.editor"))
6147                 string_ncopy(opt_editor, value, valuelen);
6148
6149         /* branch.<head>.remote */
6150         if (*opt_head &&
6151             !strncmp(name, "branch.", 7) &&
6152             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
6153             !strcmp(name + 7 + strlen(opt_head), ".remote"))
6154                 string_ncopy(opt_remote, value, valuelen);
6155
6156         if (*opt_head && *opt_remote &&
6157             !strncmp(name, "branch.", 7) &&
6158             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
6159             !strcmp(name + 7 + strlen(opt_head), ".merge")) {
6160                 size_t from = strlen(opt_remote);
6161
6162                 if (!prefixcmp(value, "refs/heads/")) {
6163                         value += STRING_SIZE("refs/heads/");
6164                         valuelen -= STRING_SIZE("refs/heads/");
6165                 }
6166
6167                 if (!string_format_from(opt_remote, &from, "/%s", value))
6168                         opt_remote[0] = 0;
6169         }
6170
6171         return OK;
6172 }
6173
6174 static int
6175 load_git_config(void)
6176 {
6177         return read_properties(popen("git " GIT_CONFIG " --list", "r"),
6178                                "=", read_repo_config_option);
6179 }
6180
6181 static int
6182 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
6183 {
6184         if (!opt_git_dir[0]) {
6185                 string_ncopy(opt_git_dir, name, namelen);
6186
6187         } else if (opt_is_inside_work_tree == -1) {
6188                 /* This can be 3 different values depending on the
6189                  * version of git being used. If git-rev-parse does not
6190                  * understand --is-inside-work-tree it will simply echo
6191                  * the option else either "true" or "false" is printed.
6192                  * Default to true for the unknown case. */
6193                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
6194
6195         } else if (opt_cdup[0] == ' ') {
6196                 string_ncopy(opt_cdup, name, namelen);
6197         } else {
6198                 if (!prefixcmp(name, "refs/heads/")) {
6199                         namelen -= STRING_SIZE("refs/heads/");
6200                         name    += STRING_SIZE("refs/heads/");
6201                         string_ncopy(opt_head, name, namelen);
6202                 }
6203         }
6204
6205         return OK;
6206 }
6207
6208 static int
6209 load_repo_info(void)
6210 {
6211         int result;
6212         FILE *pipe = popen("(git rev-parse --git-dir --is-inside-work-tree "
6213                            " --show-cdup; git symbolic-ref HEAD) 2>/dev/null", "r");
6214
6215         /* XXX: The line outputted by "--show-cdup" can be empty so
6216          * initialize it to something invalid to make it possible to
6217          * detect whether it has been set or not. */
6218         opt_cdup[0] = ' ';
6219
6220         result = read_properties(pipe, "=", read_repo_info);
6221         if (opt_cdup[0] == ' ')
6222                 opt_cdup[0] = 0;
6223
6224         return result;
6225 }
6226
6227 static int
6228 read_properties(FILE *pipe, const char *separators,
6229                 int (*read_property)(char *, size_t, char *, size_t))
6230 {
6231         char buffer[BUFSIZ];
6232         char *name;
6233         int state = OK;
6234
6235         if (!pipe)
6236                 return ERR;
6237
6238         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
6239                 char *value;
6240                 size_t namelen;
6241                 size_t valuelen;
6242
6243                 name = chomp_string(name);
6244                 namelen = strcspn(name, separators);
6245
6246                 if (name[namelen]) {
6247                         name[namelen] = 0;
6248                         value = chomp_string(name + namelen + 1);
6249                         valuelen = strlen(value);
6250
6251                 } else {
6252                         value = "";
6253                         valuelen = 0;
6254                 }
6255
6256                 state = read_property(name, namelen, value, valuelen);
6257         }
6258
6259         if (state != ERR && ferror(pipe))
6260                 state = ERR;
6261
6262         pclose(pipe);
6263
6264         return state;
6265 }
6266
6267
6268 /*
6269  * Main
6270  */
6271
6272 static void __NORETURN
6273 quit(int sig)
6274 {
6275         /* XXX: Restore tty modes and let the OS cleanup the rest! */
6276         if (cursed)
6277                 endwin();
6278         exit(0);
6279 }
6280
6281 static void __NORETURN
6282 die(const char *err, ...)
6283 {
6284         va_list args;
6285
6286         endwin();
6287
6288         va_start(args, err);
6289         fputs("tig: ", stderr);
6290         vfprintf(stderr, err, args);
6291         fputs("\n", stderr);
6292         va_end(args);
6293
6294         exit(1);
6295 }
6296
6297 static void
6298 warn(const char *msg, ...)
6299 {
6300         va_list args;
6301
6302         va_start(args, msg);
6303         fputs("tig warning: ", stderr);
6304         vfprintf(stderr, msg, args);
6305         fputs("\n", stderr);
6306         va_end(args);
6307 }
6308
6309 int
6310 main(int argc, const char *argv[])
6311 {
6312         const char **run_argv = NULL;
6313         struct view *view;
6314         enum request request;
6315         size_t i;
6316
6317         signal(SIGINT, quit);
6318
6319         if (setlocale(LC_ALL, "")) {
6320                 char *codeset = nl_langinfo(CODESET);
6321
6322                 string_ncopy(opt_codeset, codeset, strlen(codeset));
6323         }
6324
6325         if (load_repo_info() == ERR)
6326                 die("Failed to load repo info.");
6327
6328         if (load_options() == ERR)
6329                 die("Failed to load user config.");
6330
6331         if (load_git_config() == ERR)
6332                 die("Failed to load repo config.");
6333
6334         request = parse_options(argc, argv, &run_argv);
6335         if (request == REQ_NONE)
6336                 return 0;
6337
6338         /* Require a git repository unless when running in pager mode. */
6339         if (!opt_git_dir[0] && request != REQ_VIEW_PAGER)
6340                 die("Not a git repository");
6341
6342         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
6343                 opt_utf8 = FALSE;
6344
6345         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
6346                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
6347                 if (opt_iconv == ICONV_NONE)
6348                         die("Failed to initialize character set conversion");
6349         }
6350
6351         if (load_refs() == ERR)
6352                 die("Failed to load refs.");
6353
6354         foreach_view (view, i)
6355                 argv_from_env(view->ops->argv, view->cmd_env);
6356
6357         init_display();
6358
6359         if (run_argv) {
6360                 if (!prepare_update(VIEW(request), run_argv, NULL, FORMAT_NONE))
6361                         die("Failed to format arguments");
6362                 open_view(display[current_view], request, OPEN_PREPARED);
6363                 request = REQ_NONE;
6364         }
6365
6366         while (view_driver(display[current_view], request)) {
6367                 int key;
6368                 int i;
6369
6370                 foreach_view (view, i)
6371                         update_view(view);
6372                 view = display[current_view];
6373
6374                 /* Refresh, accept single keystroke of input */
6375                 key = wgetch(status_win);
6376
6377                 /* wgetch() with nodelay() enabled returns ERR when there's no
6378                  * input. */
6379                 if (key == ERR) {
6380                         request = REQ_NONE;
6381                         continue;
6382                 }
6383
6384                 request = get_keybinding(view->keymap, key);
6385
6386                 /* Some low-level request handling. This keeps access to
6387                  * status_win restricted. */
6388                 switch (request) {
6389                 case REQ_PROMPT:
6390                 {
6391                         char *cmd = read_prompt(":");
6392
6393                         if (cmd) {
6394                                 struct view *next = VIEW(REQ_VIEW_PAGER);
6395                                 const char *argv[SIZEOF_ARG] = { "git" };
6396                                 int argc = 1;
6397
6398                                 /* When running random commands, initially show the
6399                                  * command in the title. However, it maybe later be
6400                                  * overwritten if a commit line is selected. */
6401                                 string_ncopy(next->ref, cmd, strlen(cmd));
6402
6403                                 if (!argv_from_string(argv, &argc, cmd)) {
6404                                         report("Too many arguments");
6405                                 } else if (!prepare_update(next, argv, NULL, FORMAT_DASH)) {
6406                                         report("Failed to format command");
6407                                 } else {
6408                                         open_view(view, REQ_VIEW_PAGER, OPEN_PREPARED);
6409                                 }
6410                         }
6411
6412                         request = REQ_NONE;
6413                         break;
6414                 }
6415                 case REQ_SEARCH:
6416                 case REQ_SEARCH_BACK:
6417                 {
6418                         const char *prompt = request == REQ_SEARCH ? "/" : "?";
6419                         char *search = read_prompt(prompt);
6420
6421                         if (search)
6422                                 string_ncopy(opt_search, search, strlen(search));
6423                         else
6424                                 request = REQ_NONE;
6425                         break;
6426                 }
6427                 case REQ_SCREEN_RESIZE:
6428                 {
6429                         int height, width;
6430
6431                         getmaxyx(stdscr, height, width);
6432
6433                         /* Resize the status view and let the view driver take
6434                          * care of resizing the displayed views. */
6435                         wresize(status_win, 1, width);
6436                         mvwin(status_win, height - 1, 0);
6437                         wrefresh(status_win);
6438                         break;
6439                 }
6440                 default:
6441                         break;
6442                 }
6443         }
6444
6445         quit(0);
6446
6447         return 0;
6448 }