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