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