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