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