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