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