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