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