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