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