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