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