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