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