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