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