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