Use enum_map for handling obsolete request names
[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 run_io_buf(const char **argv, char buf[], size_t bufsize)
680 {
681         struct io io = {};
682         bool error;
683
684         if (!run_io_rd(&io, argv, FORMAT_NONE))
685                 return FALSE;
686
687         io.buf = io.bufpos = buf;
688         io.bufalloc = bufsize;
689         error = !io_get(&io, '\n', TRUE) && io_error(&io);
690         io.buf = NULL;
691
692         return done_io(&io) || error;
693 }
694
695 static int
696 io_load(struct io *io, const char *separators,
697         int (*read_property)(char *, size_t, char *, size_t))
698 {
699         char *name;
700         int state = OK;
701
702         if (!start_io(io))
703                 return ERR;
704
705         while (state == OK && (name = io_get(io, '\n', TRUE))) {
706                 char *value;
707                 size_t namelen;
708                 size_t valuelen;
709
710                 name = chomp_string(name);
711                 namelen = strcspn(name, separators);
712
713                 if (name[namelen]) {
714                         name[namelen] = 0;
715                         value = chomp_string(name + namelen + 1);
716                         valuelen = strlen(value);
717
718                 } else {
719                         value = "";
720                         valuelen = 0;
721                 }
722
723                 state = read_property(name, namelen, value, valuelen);
724         }
725
726         if (state != ERR && io_error(io))
727                 state = ERR;
728         done_io(io);
729
730         return state;
731 }
732
733 static int
734 run_io_load(const char **argv, const char *separators,
735             int (*read_property)(char *, size_t, char *, size_t))
736 {
737         struct io io = {};
738
739         return init_io_rd(&io, argv, NULL, FORMAT_NONE)
740                 ? io_load(&io, separators, read_property) : ERR;
741 }
742
743
744 /*
745  * User requests
746  */
747
748 #define REQ_INFO \
749         /* XXX: Keep the view request first and in sync with views[]. */ \
750         REQ_GROUP("View switching") \
751         REQ_(VIEW_MAIN,         "Show main view"), \
752         REQ_(VIEW_DIFF,         "Show diff view"), \
753         REQ_(VIEW_LOG,          "Show log view"), \
754         REQ_(VIEW_TREE,         "Show tree view"), \
755         REQ_(VIEW_BLOB,         "Show blob view"), \
756         REQ_(VIEW_BLAME,        "Show blame view"), \
757         REQ_(VIEW_HELP,         "Show help page"), \
758         REQ_(VIEW_PAGER,        "Show pager view"), \
759         REQ_(VIEW_STATUS,       "Show status view"), \
760         REQ_(VIEW_STAGE,        "Show stage view"), \
761         \
762         REQ_GROUP("View manipulation") \
763         REQ_(ENTER,             "Enter current line and scroll"), \
764         REQ_(NEXT,              "Move to next"), \
765         REQ_(PREVIOUS,          "Move to previous"), \
766         REQ_(PARENT,            "Move to parent"), \
767         REQ_(VIEW_NEXT,         "Move focus to next view"), \
768         REQ_(REFRESH,           "Reload and refresh"), \
769         REQ_(MAXIMIZE,          "Maximize the current view"), \
770         REQ_(VIEW_CLOSE,        "Close the current view"), \
771         REQ_(QUIT,              "Close all views and quit"), \
772         \
773         REQ_GROUP("View specific requests") \
774         REQ_(STATUS_UPDATE,     "Update file status"), \
775         REQ_(STATUS_REVERT,     "Revert file changes"), \
776         REQ_(STATUS_MERGE,      "Merge file using external tool"), \
777         REQ_(STAGE_NEXT,        "Find next chunk to stage"), \
778         \
779         REQ_GROUP("Cursor navigation") \
780         REQ_(MOVE_UP,           "Move cursor one line up"), \
781         REQ_(MOVE_DOWN,         "Move cursor one line down"), \
782         REQ_(MOVE_PAGE_DOWN,    "Move cursor one page down"), \
783         REQ_(MOVE_PAGE_UP,      "Move cursor one page up"), \
784         REQ_(MOVE_FIRST_LINE,   "Move cursor to first line"), \
785         REQ_(MOVE_LAST_LINE,    "Move cursor to last line"), \
786         \
787         REQ_GROUP("Scrolling") \
788         REQ_(SCROLL_LEFT,       "Scroll two columns left"), \
789         REQ_(SCROLL_RIGHT,      "Scroll two columns right"), \
790         REQ_(SCROLL_LINE_UP,    "Scroll one line up"), \
791         REQ_(SCROLL_LINE_DOWN,  "Scroll one line down"), \
792         REQ_(SCROLL_PAGE_UP,    "Scroll one page up"), \
793         REQ_(SCROLL_PAGE_DOWN,  "Scroll one page down"), \
794         \
795         REQ_GROUP("Searching") \
796         REQ_(SEARCH,            "Search the view"), \
797         REQ_(SEARCH_BACK,       "Search backwards in the view"), \
798         REQ_(FIND_NEXT,         "Find next search match"), \
799         REQ_(FIND_PREV,         "Find previous search match"), \
800         \
801         REQ_GROUP("Option manipulation") \
802         REQ_(TOGGLE_LINENO,     "Toggle line numbers"), \
803         REQ_(TOGGLE_DATE,       "Toggle date display"), \
804         REQ_(TOGGLE_AUTHOR,     "Toggle author display"), \
805         REQ_(TOGGLE_REV_GRAPH,  "Toggle revision graph visualization"), \
806         REQ_(TOGGLE_REFS,       "Toggle reference display (tags/branches)"), \
807         \
808         REQ_GROUP("Misc") \
809         REQ_(PROMPT,            "Bring up the prompt"), \
810         REQ_(SCREEN_REDRAW,     "Redraw the screen"), \
811         REQ_(SHOW_VERSION,      "Show version information"), \
812         REQ_(STOP_LOADING,      "Stop all loading views"), \
813         REQ_(EDIT,              "Open in editor"), \
814         REQ_(NONE,              "Do nothing")
815
816
817 /* User action requests. */
818 enum request {
819 #define REQ_GROUP(help)
820 #define REQ_(req, help) REQ_##req
821
822         /* Offset all requests to avoid conflicts with ncurses getch values. */
823         REQ_OFFSET = KEY_MAX + 1,
824         REQ_INFO
825
826 #undef  REQ_GROUP
827 #undef  REQ_
828 };
829
830 struct request_info {
831         enum request request;
832         const char *name;
833         int namelen;
834         const char *help;
835 };
836
837 static struct request_info req_info[] = {
838 #define REQ_GROUP(help) { 0, NULL, 0, (help) },
839 #define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) }
840         REQ_INFO
841 #undef  REQ_GROUP
842 #undef  REQ_
843 };
844
845 static enum request
846 get_request(const char *name)
847 {
848         int namelen = strlen(name);
849         int i;
850
851         for (i = 0; i < ARRAY_SIZE(req_info); i++)
852                 if (req_info[i].namelen == namelen &&
853                     !string_enum_compare(req_info[i].name, name, namelen))
854                         return req_info[i].request;
855
856         return REQ_NONE;
857 }
858
859
860 /*
861  * Options
862  */
863
864 /* Option and state variables. */
865 static bool opt_date                    = TRUE;
866 static bool opt_author                  = TRUE;
867 static bool opt_line_number             = FALSE;
868 static bool opt_line_graphics           = TRUE;
869 static bool opt_rev_graph               = FALSE;
870 static bool opt_show_refs               = TRUE;
871 static int opt_num_interval             = NUMBER_INTERVAL;
872 static int opt_tab_size                 = TAB_SIZE;
873 static int opt_author_cols              = AUTHOR_COLS-1;
874 static char opt_path[SIZEOF_STR]        = "";
875 static char opt_file[SIZEOF_STR]        = "";
876 static char opt_ref[SIZEOF_REF]         = "";
877 static char opt_head[SIZEOF_REF]        = "";
878 static char opt_head_rev[SIZEOF_REV]    = "";
879 static char opt_remote[SIZEOF_REF]      = "";
880 static char opt_encoding[20]            = "UTF-8";
881 static bool opt_utf8                    = TRUE;
882 static char opt_codeset[20]             = "UTF-8";
883 static iconv_t opt_iconv                = ICONV_NONE;
884 static char opt_search[SIZEOF_STR]      = "";
885 static char opt_cdup[SIZEOF_STR]        = "";
886 static char opt_prefix[SIZEOF_STR]      = "";
887 static char opt_git_dir[SIZEOF_STR]     = "";
888 static signed char opt_is_inside_work_tree      = -1; /* set to TRUE or FALSE */
889 static char opt_editor[SIZEOF_STR]      = "";
890 static FILE *opt_tty                    = NULL;
891
892 #define is_initial_commit()     (!*opt_head_rev)
893 #define is_head_commit(rev)     (!strcmp((rev), "HEAD") || !strcmp(opt_head_rev, (rev)))
894
895
896 /*
897  * Line-oriented content detection.
898  */
899
900 #define LINE_INFO \
901 LINE(DIFF_HEADER,  "diff --git ",       COLOR_YELLOW,   COLOR_DEFAULT,  0), \
902 LINE(DIFF_CHUNK,   "@@",                COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
903 LINE(DIFF_ADD,     "+",                 COLOR_GREEN,    COLOR_DEFAULT,  0), \
904 LINE(DIFF_DEL,     "-",                 COLOR_RED,      COLOR_DEFAULT,  0), \
905 LINE(DIFF_INDEX,        "index ",         COLOR_BLUE,   COLOR_DEFAULT,  0), \
906 LINE(DIFF_OLDMODE,      "old file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
907 LINE(DIFF_NEWMODE,      "new file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
908 LINE(DIFF_COPY_FROM,    "copy from",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
909 LINE(DIFF_COPY_TO,      "copy to",        COLOR_YELLOW, COLOR_DEFAULT,  0), \
910 LINE(DIFF_RENAME_FROM,  "rename from",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
911 LINE(DIFF_RENAME_TO,    "rename to",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
912 LINE(DIFF_SIMILARITY,   "similarity ",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
913 LINE(DIFF_DISSIMILARITY,"dissimilarity ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
914 LINE(DIFF_TREE,         "diff-tree ",     COLOR_BLUE,   COLOR_DEFAULT,  0), \
915 LINE(PP_AUTHOR,    "Author: ",          COLOR_CYAN,     COLOR_DEFAULT,  0), \
916 LINE(PP_COMMIT,    "Commit: ",          COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
917 LINE(PP_MERGE,     "Merge: ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
918 LINE(PP_DATE,      "Date:   ",          COLOR_YELLOW,   COLOR_DEFAULT,  0), \
919 LINE(PP_ADATE,     "AuthorDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
920 LINE(PP_CDATE,     "CommitDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
921 LINE(PP_REFS,      "Refs: ",            COLOR_RED,      COLOR_DEFAULT,  0), \
922 LINE(COMMIT,       "commit ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
923 LINE(PARENT,       "parent ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
924 LINE(TREE,         "tree ",             COLOR_BLUE,     COLOR_DEFAULT,  0), \
925 LINE(AUTHOR,       "author ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
926 LINE(COMMITTER,    "committer ",        COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
927 LINE(SIGNOFF,      "    Signed-off-by", COLOR_YELLOW,   COLOR_DEFAULT,  0), \
928 LINE(ACKED,        "    Acked-by",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
929 LINE(DEFAULT,      "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
930 LINE(CURSOR,       "",                  COLOR_WHITE,    COLOR_GREEN,    A_BOLD), \
931 LINE(STATUS,       "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
932 LINE(DELIMITER,    "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
933 LINE(DATE,         "",                  COLOR_BLUE,     COLOR_DEFAULT,  0), \
934 LINE(MODE,         "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
935 LINE(LINE_NUMBER,  "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
936 LINE(TITLE_BLUR,   "",                  COLOR_WHITE,    COLOR_BLUE,     0), \
937 LINE(TITLE_FOCUS,  "",                  COLOR_WHITE,    COLOR_BLUE,     A_BOLD), \
938 LINE(MAIN_COMMIT,  "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
939 LINE(MAIN_TAG,     "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
940 LINE(MAIN_LOCAL_TAG,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
941 LINE(MAIN_REMOTE,  "",                  COLOR_YELLOW,   COLOR_DEFAULT,  0), \
942 LINE(MAIN_TRACKED, "",                  COLOR_YELLOW,   COLOR_DEFAULT,  A_BOLD), \
943 LINE(MAIN_REF,     "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
944 LINE(MAIN_HEAD,    "",                  COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD), \
945 LINE(MAIN_REVGRAPH,"",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
946 LINE(TREE_HEAD,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_BOLD), \
947 LINE(TREE_DIR,     "",                  COLOR_YELLOW,   COLOR_DEFAULT,  A_NORMAL), \
948 LINE(TREE_FILE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
949 LINE(STAT_HEAD,    "",                  COLOR_YELLOW,   COLOR_DEFAULT,  0), \
950 LINE(STAT_SECTION, "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
951 LINE(STAT_NONE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
952 LINE(STAT_STAGED,  "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
953 LINE(STAT_UNSTAGED,"",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
954 LINE(STAT_UNTRACKED,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
955 LINE(BLAME_ID,     "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0)
956
957 enum line_type {
958 #define LINE(type, line, fg, bg, attr) \
959         LINE_##type
960         LINE_INFO,
961         LINE_NONE
962 #undef  LINE
963 };
964
965 struct line_info {
966         const char *name;       /* Option name. */
967         int namelen;            /* Size of option name. */
968         const char *line;       /* The start of line to match. */
969         int linelen;            /* Size of string to match. */
970         int fg, bg, attr;       /* Color and text attributes for the lines. */
971 };
972
973 static struct line_info line_info[] = {
974 #define LINE(type, line, fg, bg, attr) \
975         { #type, STRING_SIZE(#type), (line), STRING_SIZE(line), (fg), (bg), (attr) }
976         LINE_INFO
977 #undef  LINE
978 };
979
980 static enum line_type
981 get_line_type(const char *line)
982 {
983         int linelen = strlen(line);
984         enum line_type type;
985
986         for (type = 0; type < ARRAY_SIZE(line_info); type++)
987                 /* Case insensitive search matches Signed-off-by lines better. */
988                 if (linelen >= line_info[type].linelen &&
989                     !strncasecmp(line_info[type].line, line, line_info[type].linelen))
990                         return type;
991
992         return LINE_DEFAULT;
993 }
994
995 static inline int
996 get_line_attr(enum line_type type)
997 {
998         assert(type < ARRAY_SIZE(line_info));
999         return COLOR_PAIR(type) | line_info[type].attr;
1000 }
1001
1002 static struct line_info *
1003 get_line_info(const char *name)
1004 {
1005         size_t namelen = strlen(name);
1006         enum line_type type;
1007
1008         for (type = 0; type < ARRAY_SIZE(line_info); type++)
1009                 if (namelen == line_info[type].namelen &&
1010                     !string_enum_compare(line_info[type].name, name, namelen))
1011                         return &line_info[type];
1012
1013         return NULL;
1014 }
1015
1016 static void
1017 init_colors(void)
1018 {
1019         int default_bg = line_info[LINE_DEFAULT].bg;
1020         int default_fg = line_info[LINE_DEFAULT].fg;
1021         enum line_type type;
1022
1023         start_color();
1024
1025         if (assume_default_colors(default_fg, default_bg) == ERR) {
1026                 default_bg = COLOR_BLACK;
1027                 default_fg = COLOR_WHITE;
1028         }
1029
1030         for (type = 0; type < ARRAY_SIZE(line_info); type++) {
1031                 struct line_info *info = &line_info[type];
1032                 int bg = info->bg == COLOR_DEFAULT ? default_bg : info->bg;
1033                 int fg = info->fg == COLOR_DEFAULT ? default_fg : info->fg;
1034
1035                 init_pair(type, fg, bg);
1036         }
1037 }
1038
1039 struct line {
1040         enum line_type type;
1041
1042         /* State flags */
1043         unsigned int selected:1;
1044         unsigned int dirty:1;
1045         unsigned int cleareol:1;
1046
1047         void *data;             /* User data */
1048 };
1049
1050
1051 /*
1052  * Keys
1053  */
1054
1055 struct keybinding {
1056         int alias;
1057         enum request request;
1058 };
1059
1060 static struct keybinding default_keybindings[] = {
1061         /* View switching */
1062         { 'm',          REQ_VIEW_MAIN },
1063         { 'd',          REQ_VIEW_DIFF },
1064         { 'l',          REQ_VIEW_LOG },
1065         { 't',          REQ_VIEW_TREE },
1066         { 'f',          REQ_VIEW_BLOB },
1067         { 'B',          REQ_VIEW_BLAME },
1068         { 'p',          REQ_VIEW_PAGER },
1069         { 'h',          REQ_VIEW_HELP },
1070         { 'S',          REQ_VIEW_STATUS },
1071         { 'c',          REQ_VIEW_STAGE },
1072
1073         /* View manipulation */
1074         { 'q',          REQ_VIEW_CLOSE },
1075         { KEY_TAB,      REQ_VIEW_NEXT },
1076         { KEY_RETURN,   REQ_ENTER },
1077         { KEY_UP,       REQ_PREVIOUS },
1078         { KEY_DOWN,     REQ_NEXT },
1079         { 'R',          REQ_REFRESH },
1080         { KEY_F(5),     REQ_REFRESH },
1081         { 'O',          REQ_MAXIMIZE },
1082
1083         /* Cursor navigation */
1084         { 'k',          REQ_MOVE_UP },
1085         { 'j',          REQ_MOVE_DOWN },
1086         { KEY_HOME,     REQ_MOVE_FIRST_LINE },
1087         { KEY_END,      REQ_MOVE_LAST_LINE },
1088         { KEY_NPAGE,    REQ_MOVE_PAGE_DOWN },
1089         { ' ',          REQ_MOVE_PAGE_DOWN },
1090         { KEY_PPAGE,    REQ_MOVE_PAGE_UP },
1091         { 'b',          REQ_MOVE_PAGE_UP },
1092         { '-',          REQ_MOVE_PAGE_UP },
1093
1094         /* Scrolling */
1095         { KEY_LEFT,     REQ_SCROLL_LEFT },
1096         { KEY_RIGHT,    REQ_SCROLL_RIGHT },
1097         { KEY_IC,       REQ_SCROLL_LINE_UP },
1098         { KEY_DC,       REQ_SCROLL_LINE_DOWN },
1099         { 'w',          REQ_SCROLL_PAGE_UP },
1100         { 's',          REQ_SCROLL_PAGE_DOWN },
1101
1102         /* Searching */
1103         { '/',          REQ_SEARCH },
1104         { '?',          REQ_SEARCH_BACK },
1105         { 'n',          REQ_FIND_NEXT },
1106         { 'N',          REQ_FIND_PREV },
1107
1108         /* Misc */
1109         { 'Q',          REQ_QUIT },
1110         { 'z',          REQ_STOP_LOADING },
1111         { 'v',          REQ_SHOW_VERSION },
1112         { 'r',          REQ_SCREEN_REDRAW },
1113         { '.',          REQ_TOGGLE_LINENO },
1114         { 'D',          REQ_TOGGLE_DATE },
1115         { 'A',          REQ_TOGGLE_AUTHOR },
1116         { 'g',          REQ_TOGGLE_REV_GRAPH },
1117         { 'F',          REQ_TOGGLE_REFS },
1118         { ':',          REQ_PROMPT },
1119         { 'u',          REQ_STATUS_UPDATE },
1120         { '!',          REQ_STATUS_REVERT },
1121         { 'M',          REQ_STATUS_MERGE },
1122         { '@',          REQ_STAGE_NEXT },
1123         { ',',          REQ_PARENT },
1124         { 'e',          REQ_EDIT },
1125 };
1126
1127 #define KEYMAP_INFO \
1128         KEYMAP_(GENERIC), \
1129         KEYMAP_(MAIN), \
1130         KEYMAP_(DIFF), \
1131         KEYMAP_(LOG), \
1132         KEYMAP_(TREE), \
1133         KEYMAP_(BLOB), \
1134         KEYMAP_(BLAME), \
1135         KEYMAP_(PAGER), \
1136         KEYMAP_(HELP), \
1137         KEYMAP_(STATUS), \
1138         KEYMAP_(STAGE)
1139
1140 enum keymap {
1141 #define KEYMAP_(name) KEYMAP_##name
1142         KEYMAP_INFO
1143 #undef  KEYMAP_
1144 };
1145
1146 static struct enum_map keymap_table[] = {
1147 #define KEYMAP_(name) ENUM_MAP(#name, KEYMAP_##name)
1148         KEYMAP_INFO
1149 #undef  KEYMAP_
1150 };
1151
1152 #define set_keymap(map, name) map_enum(map, keymap_table, name)
1153
1154 struct keybinding_table {
1155         struct keybinding *data;
1156         size_t size;
1157 };
1158
1159 static struct keybinding_table keybindings[ARRAY_SIZE(keymap_table)];
1160
1161 static void
1162 add_keybinding(enum keymap keymap, enum request request, int key)
1163 {
1164         struct keybinding_table *table = &keybindings[keymap];
1165
1166         table->data = realloc(table->data, (table->size + 1) * sizeof(*table->data));
1167         if (!table->data)
1168                 die("Failed to allocate keybinding");
1169         table->data[table->size].alias = key;
1170         table->data[table->size++].request = request;
1171 }
1172
1173 /* Looks for a key binding first in the given map, then in the generic map, and
1174  * lastly in the default keybindings. */
1175 static enum request
1176 get_keybinding(enum keymap keymap, int key)
1177 {
1178         size_t i;
1179
1180         for (i = 0; i < keybindings[keymap].size; i++)
1181                 if (keybindings[keymap].data[i].alias == key)
1182                         return keybindings[keymap].data[i].request;
1183
1184         for (i = 0; i < keybindings[KEYMAP_GENERIC].size; i++)
1185                 if (keybindings[KEYMAP_GENERIC].data[i].alias == key)
1186                         return keybindings[KEYMAP_GENERIC].data[i].request;
1187
1188         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++)
1189                 if (default_keybindings[i].alias == key)
1190                         return default_keybindings[i].request;
1191
1192         return (enum request) key;
1193 }
1194
1195
1196 struct key {
1197         const char *name;
1198         int value;
1199 };
1200
1201 static struct key key_table[] = {
1202         { "Enter",      KEY_RETURN },
1203         { "Space",      ' ' },
1204         { "Backspace",  KEY_BACKSPACE },
1205         { "Tab",        KEY_TAB },
1206         { "Escape",     KEY_ESC },
1207         { "Left",       KEY_LEFT },
1208         { "Right",      KEY_RIGHT },
1209         { "Up",         KEY_UP },
1210         { "Down",       KEY_DOWN },
1211         { "Insert",     KEY_IC },
1212         { "Delete",     KEY_DC },
1213         { "Hash",       '#' },
1214         { "Home",       KEY_HOME },
1215         { "End",        KEY_END },
1216         { "PageUp",     KEY_PPAGE },
1217         { "PageDown",   KEY_NPAGE },
1218         { "F1",         KEY_F(1) },
1219         { "F2",         KEY_F(2) },
1220         { "F3",         KEY_F(3) },
1221         { "F4",         KEY_F(4) },
1222         { "F5",         KEY_F(5) },
1223         { "F6",         KEY_F(6) },
1224         { "F7",         KEY_F(7) },
1225         { "F8",         KEY_F(8) },
1226         { "F9",         KEY_F(9) },
1227         { "F10",        KEY_F(10) },
1228         { "F11",        KEY_F(11) },
1229         { "F12",        KEY_F(12) },
1230 };
1231
1232 static int
1233 get_key_value(const char *name)
1234 {
1235         int i;
1236
1237         for (i = 0; i < ARRAY_SIZE(key_table); i++)
1238                 if (!strcasecmp(key_table[i].name, name))
1239                         return key_table[i].value;
1240
1241         if (strlen(name) == 1 && isprint(*name))
1242                 return (int) *name;
1243
1244         return ERR;
1245 }
1246
1247 static const char *
1248 get_key_name(int key_value)
1249 {
1250         static char key_char[] = "'X'";
1251         const char *seq = NULL;
1252         int key;
1253
1254         for (key = 0; key < ARRAY_SIZE(key_table); key++)
1255                 if (key_table[key].value == key_value)
1256                         seq = key_table[key].name;
1257
1258         if (seq == NULL &&
1259             key_value < 127 &&
1260             isprint(key_value)) {
1261                 key_char[1] = (char) key_value;
1262                 seq = key_char;
1263         }
1264
1265         return seq ? seq : "(no key)";
1266 }
1267
1268 static const char *
1269 get_key(enum request request)
1270 {
1271         static char buf[BUFSIZ];
1272         size_t pos = 0;
1273         char *sep = "";
1274         int i;
1275
1276         buf[pos] = 0;
1277
1278         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
1279                 struct keybinding *keybinding = &default_keybindings[i];
1280
1281                 if (keybinding->request != request)
1282                         continue;
1283
1284                 if (!string_format_from(buf, &pos, "%s%s", sep,
1285                                         get_key_name(keybinding->alias)))
1286                         return "Too many keybindings!";
1287                 sep = ", ";
1288         }
1289
1290         return buf;
1291 }
1292
1293 struct run_request {
1294         enum keymap keymap;
1295         int key;
1296         const char *argv[SIZEOF_ARG];
1297 };
1298
1299 static struct run_request *run_request;
1300 static size_t run_requests;
1301
1302 static enum request
1303 add_run_request(enum keymap keymap, int key, int argc, const char **argv)
1304 {
1305         struct run_request *req;
1306
1307         if (argc >= ARRAY_SIZE(req->argv) - 1)
1308                 return REQ_NONE;
1309
1310         req = realloc(run_request, (run_requests + 1) * sizeof(*run_request));
1311         if (!req)
1312                 return REQ_NONE;
1313
1314         run_request = req;
1315         req = &run_request[run_requests];
1316         req->keymap = keymap;
1317         req->key = key;
1318         req->argv[0] = NULL;
1319
1320         if (!format_argv(req->argv, argv, FORMAT_NONE))
1321                 return REQ_NONE;
1322
1323         return REQ_NONE + ++run_requests;
1324 }
1325
1326 static struct run_request *
1327 get_run_request(enum request request)
1328 {
1329         if (request <= REQ_NONE)
1330                 return NULL;
1331         return &run_request[request - REQ_NONE - 1];
1332 }
1333
1334 static void
1335 add_builtin_run_requests(void)
1336 {
1337         const char *cherry_pick[] = { "git", "cherry-pick", "%(commit)", NULL };
1338         const char *gc[] = { "git", "gc", NULL };
1339         struct {
1340                 enum keymap keymap;
1341                 int key;
1342                 int argc;
1343                 const char **argv;
1344         } reqs[] = {
1345                 { KEYMAP_MAIN,    'C', ARRAY_SIZE(cherry_pick) - 1, cherry_pick },
1346                 { KEYMAP_GENERIC, 'G', ARRAY_SIZE(gc) - 1, gc },
1347         };
1348         int i;
1349
1350         for (i = 0; i < ARRAY_SIZE(reqs); i++) {
1351                 enum request req;
1352
1353                 req = add_run_request(reqs[i].keymap, reqs[i].key, reqs[i].argc, reqs[i].argv);
1354                 if (req != REQ_NONE)
1355                         add_keybinding(reqs[i].keymap, req, reqs[i].key);
1356         }
1357 }
1358
1359 /*
1360  * User config file handling.
1361  */
1362
1363 static struct enum_map color_map[] = {
1364 #define COLOR_MAP(name) ENUM_MAP(#name, COLOR_##name)
1365         COLOR_MAP(DEFAULT),
1366         COLOR_MAP(BLACK),
1367         COLOR_MAP(BLUE),
1368         COLOR_MAP(CYAN),
1369         COLOR_MAP(GREEN),
1370         COLOR_MAP(MAGENTA),
1371         COLOR_MAP(RED),
1372         COLOR_MAP(WHITE),
1373         COLOR_MAP(YELLOW),
1374 };
1375
1376 static struct enum_map attr_map[] = {
1377 #define ATTR_MAP(name) ENUM_MAP(#name, A_##name)
1378         ATTR_MAP(NORMAL),
1379         ATTR_MAP(BLINK),
1380         ATTR_MAP(BOLD),
1381         ATTR_MAP(DIM),
1382         ATTR_MAP(REVERSE),
1383         ATTR_MAP(STANDOUT),
1384         ATTR_MAP(UNDERLINE),
1385 };
1386
1387 #define set_color(color, name)          map_enum(color, color_map, name)
1388 #define set_attribute(attr, name)       map_enum(attr, attr_map, name)
1389
1390 static int   config_lineno;
1391 static bool  config_errors;
1392 static const char *config_msg;
1393
1394 /* Wants: object fgcolor bgcolor [attribute] */
1395 static int
1396 option_color_command(int argc, const char *argv[])
1397 {
1398         struct line_info *info;
1399
1400         if (argc != 3 && argc != 4) {
1401                 config_msg = "Wrong number of arguments given to color command";
1402                 return ERR;
1403         }
1404
1405         info = get_line_info(argv[0]);
1406         if (!info) {
1407                 if (!string_enum_compare(argv[0], "main-delim", strlen("main-delim"))) {
1408                         info = get_line_info("delimiter");
1409
1410                 } else if (!string_enum_compare(argv[0], "main-date", strlen("main-date"))) {
1411                         info = get_line_info("date");
1412
1413                 } else if (!string_enum_compare(argv[0], "main-author", strlen("main-author"))) {
1414                         info = get_line_info("author");
1415
1416                 } else {
1417                         config_msg = "Unknown color name";
1418                         return ERR;
1419                 }
1420         }
1421
1422         if (!set_color(&info->fg, argv[1]) ||
1423             !set_color(&info->bg, argv[2])) {
1424                 config_msg = "Unknown color";
1425                 return ERR;
1426         }
1427
1428         if (argc == 4 && !set_attribute(&info->attr, argv[3])) {
1429                 config_msg = "Unknown attribute";
1430                 return ERR;
1431         }
1432
1433         return OK;
1434 }
1435
1436 static int parse_bool(bool *opt, const char *arg)
1437 {
1438         *opt = (!strcmp(arg, "1") || !strcmp(arg, "true") || !strcmp(arg, "yes"))
1439                 ? TRUE : FALSE;
1440         return OK;
1441 }
1442
1443 static int
1444 parse_int(int *opt, const char *arg, int min, int max)
1445 {
1446         int value = atoi(arg);
1447
1448         if (min <= value && value <= max)
1449                 *opt = value;
1450         return OK;
1451 }
1452
1453 static int
1454 parse_string(char *opt, const char *arg, size_t optsize)
1455 {
1456         int arglen = strlen(arg);
1457
1458         switch (arg[0]) {
1459         case '\"':
1460         case '\'':
1461                 if (arglen == 1 || arg[arglen - 1] != arg[0]) {
1462                         config_msg = "Unmatched quotation";
1463                         return ERR;
1464                 }
1465                 arg += 1; arglen -= 2;
1466         default:
1467                 string_ncopy_do(opt, optsize, arg, strlen(arg));
1468                 return OK;
1469         }
1470 }
1471
1472 /* Wants: name = value */
1473 static int
1474 option_set_command(int argc, const char *argv[])
1475 {
1476         if (argc != 3) {
1477                 config_msg = "Wrong number of arguments given to set command";
1478                 return ERR;
1479         }
1480
1481         if (strcmp(argv[1], "=")) {
1482                 config_msg = "No value assigned";
1483                 return ERR;
1484         }
1485
1486         if (!strcmp(argv[0], "show-author"))
1487                 return parse_bool(&opt_author, argv[2]);
1488
1489         if (!strcmp(argv[0], "show-date"))
1490                 return parse_bool(&opt_date, argv[2]);
1491
1492         if (!strcmp(argv[0], "show-rev-graph"))
1493                 return parse_bool(&opt_rev_graph, argv[2]);
1494
1495         if (!strcmp(argv[0], "show-refs"))
1496                 return parse_bool(&opt_show_refs, argv[2]);
1497
1498         if (!strcmp(argv[0], "show-line-numbers"))
1499                 return parse_bool(&opt_line_number, argv[2]);
1500
1501         if (!strcmp(argv[0], "line-graphics"))
1502                 return parse_bool(&opt_line_graphics, argv[2]);
1503
1504         if (!strcmp(argv[0], "line-number-interval"))
1505                 return parse_int(&opt_num_interval, argv[2], 1, 1024);
1506
1507         if (!strcmp(argv[0], "author-width"))
1508                 return parse_int(&opt_author_cols, argv[2], 0, 1024);
1509
1510         if (!strcmp(argv[0], "tab-size"))
1511                 return parse_int(&opt_tab_size, argv[2], 1, 1024);
1512
1513         if (!strcmp(argv[0], "commit-encoding"))
1514                 return parse_string(opt_encoding, argv[2], sizeof(opt_encoding));
1515
1516         config_msg = "Unknown variable name";
1517         return ERR;
1518 }
1519
1520 /* Wants: mode request key */
1521 static int
1522 option_bind_command(int argc, const char *argv[])
1523 {
1524         enum request request;
1525         int keymap;
1526         int key;
1527
1528         if (argc < 3) {
1529                 config_msg = "Wrong number of arguments given to bind command";
1530                 return ERR;
1531         }
1532
1533         if (set_keymap(&keymap, argv[0]) == ERR) {
1534                 config_msg = "Unknown key map";
1535                 return ERR;
1536         }
1537
1538         key = get_key_value(argv[1]);
1539         if (key == ERR) {
1540                 config_msg = "Unknown key";
1541                 return ERR;
1542         }
1543
1544         request = get_request(argv[2]);
1545         if (request == REQ_NONE) {
1546                 static struct enum_map obsolete[] = {
1547                         ENUM_MAP("cherry-pick",         REQ_NONE),
1548                         ENUM_MAP("screen-resize",       REQ_NONE),
1549                         ENUM_MAP("tree-parent",         REQ_PARENT),
1550                 };
1551                 int alias;
1552
1553                 if (map_enum(&alias, obsolete, argv[2])) {
1554                         if (alias != REQ_NONE)
1555                                 add_keybinding(keymap, alias, key);
1556                         config_msg = "Obsolete request name";
1557                         return ERR;
1558                 }
1559         }
1560         if (request == REQ_NONE && *argv[2]++ == '!')
1561                 request = add_run_request(keymap, key, argc - 2, argv + 2);
1562         if (request == REQ_NONE) {
1563                 config_msg = "Unknown request name";
1564                 return ERR;
1565         }
1566
1567         add_keybinding(keymap, request, key);
1568
1569         return OK;
1570 }
1571
1572 static int
1573 set_option(const char *opt, char *value)
1574 {
1575         const char *argv[SIZEOF_ARG];
1576         int argc = 0;
1577
1578         if (!argv_from_string(argv, &argc, value)) {
1579                 config_msg = "Too many option arguments";
1580                 return ERR;
1581         }
1582
1583         if (!strcmp(opt, "color"))
1584                 return option_color_command(argc, argv);
1585
1586         if (!strcmp(opt, "set"))
1587                 return option_set_command(argc, argv);
1588
1589         if (!strcmp(opt, "bind"))
1590                 return option_bind_command(argc, argv);
1591
1592         config_msg = "Unknown option command";
1593         return ERR;
1594 }
1595
1596 static int
1597 read_option(char *opt, size_t optlen, char *value, size_t valuelen)
1598 {
1599         int status = OK;
1600
1601         config_lineno++;
1602         config_msg = "Internal error";
1603
1604         /* Check for comment markers, since read_properties() will
1605          * only ensure opt and value are split at first " \t". */
1606         optlen = strcspn(opt, "#");
1607         if (optlen == 0)
1608                 return OK;
1609
1610         if (opt[optlen] != 0) {
1611                 config_msg = "No option value";
1612                 status = ERR;
1613
1614         }  else {
1615                 /* Look for comment endings in the value. */
1616                 size_t len = strcspn(value, "#");
1617
1618                 if (len < valuelen) {
1619                         valuelen = len;
1620                         value[valuelen] = 0;
1621                 }
1622
1623                 status = set_option(opt, value);
1624         }
1625
1626         if (status == ERR) {
1627                 warn("Error on line %d, near '%.*s': %s",
1628                      config_lineno, (int) optlen, opt, config_msg);
1629                 config_errors = TRUE;
1630         }
1631
1632         /* Always keep going if errors are encountered. */
1633         return OK;
1634 }
1635
1636 static void
1637 load_option_file(const char *path)
1638 {
1639         struct io io = {};
1640
1641         /* It's OK that the file doesn't exist. */
1642         if (!io_open(&io, path))
1643                 return;
1644
1645         config_lineno = 0;
1646         config_errors = FALSE;
1647
1648         if (io_load(&io, " \t", read_option) == ERR ||
1649             config_errors == TRUE)
1650                 warn("Errors while loading %s.", path);
1651 }
1652
1653 static int
1654 load_options(void)
1655 {
1656         const char *home = getenv("HOME");
1657         const char *tigrc_user = getenv("TIGRC_USER");
1658         const char *tigrc_system = getenv("TIGRC_SYSTEM");
1659         char buf[SIZEOF_STR];
1660
1661         add_builtin_run_requests();
1662
1663         if (!tigrc_system) {
1664                 if (!string_format(buf, "%s/tigrc", SYSCONFDIR))
1665                         return ERR;
1666                 tigrc_system = buf;
1667         }
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_BACKGROUNDED = 2,  /* Backgrounded. */
2928         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2929         OPEN_NOMAXIMIZE = 8,    /* Do not maximize the current view. */
2930         OPEN_REFRESH = 16,      /* Refresh view using previous command. */
2931         OPEN_PREPARED = 32,     /* Open already prepared command. */
2932 };
2933
2934 static void
2935 open_view(struct view *prev, enum request request, enum open_flags flags)
2936 {
2937         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
2938         bool split = !!(flags & OPEN_SPLIT);
2939         bool reload = !!(flags & (OPEN_RELOAD | OPEN_REFRESH | OPEN_PREPARED));
2940         bool nomaximize = !!(flags & (OPEN_NOMAXIMIZE | OPEN_REFRESH));
2941         struct view *view = VIEW(request);
2942         int nviews = displayed_views();
2943         struct view *base_view = display[0];
2944
2945         if (view == prev && nviews == 1 && !reload) {
2946                 report("Already in %s view", view->name);
2947                 return;
2948         }
2949
2950         if (view->git_dir && !opt_git_dir[0]) {
2951                 report("The %s view is disabled in pager view", view->name);
2952                 return;
2953         }
2954
2955         if (split) {
2956                 display[1] = view;
2957                 if (!backgrounded)
2958                         current_view = 1;
2959         } else if (!nomaximize) {
2960                 /* Maximize the current view. */
2961                 memset(display, 0, sizeof(display));
2962                 current_view = 0;
2963                 display[current_view] = view;
2964         }
2965
2966         /* Resize the view when switching between split- and full-screen,
2967          * or when switching between two different full-screen views. */
2968         if (nviews != displayed_views() ||
2969             (nviews == 1 && base_view != display[0]))
2970                 resize_display();
2971
2972         if (view->ops->open) {
2973                 if (view->pipe)
2974                         end_update(view, TRUE);
2975                 if (!view->ops->open(view)) {
2976                         report("Failed to load %s view", view->name);
2977                         return;
2978                 }
2979                 restore_view_position(view);
2980
2981         } else if ((reload || strcmp(view->vid, view->id)) &&
2982                    !begin_update(view, flags & (OPEN_REFRESH | OPEN_PREPARED))) {
2983                 report("Failed to load %s view", view->name);
2984                 return;
2985         }
2986
2987         if (split && prev->lineno - prev->offset >= prev->height) {
2988                 /* Take the title line into account. */
2989                 int lines = prev->lineno - prev->offset - prev->height + 1;
2990
2991                 /* Scroll the view that was split if the current line is
2992                  * outside the new limited view. */
2993                 do_scroll_view(prev, lines);
2994         }
2995
2996         if (prev && view != prev) {
2997                 if (split && !backgrounded) {
2998                         /* "Blur" the previous view. */
2999                         update_view_title(prev);
3000                 }
3001
3002                 view->parent = prev;
3003         }
3004
3005         if (view->pipe && view->lines == 0) {
3006                 /* Clear the old view and let the incremental updating refill
3007                  * the screen. */
3008                 werase(view->win);
3009                 view->p_restore = flags & (OPEN_RELOAD | OPEN_REFRESH);
3010                 report("");
3011         } else if (view_is_displayed(view)) {
3012                 redraw_view(view);
3013                 report("");
3014         }
3015
3016         /* If the view is backgrounded the above calls to report()
3017          * won't redraw the view title. */
3018         if (backgrounded)
3019                 update_view_title(view);
3020 }
3021
3022 static void
3023 open_external_viewer(const char *argv[], const char *dir)
3024 {
3025         def_prog_mode();           /* save current tty modes */
3026         endwin();                  /* restore original tty modes */
3027         run_io_fg(argv, dir);
3028         fprintf(stderr, "Press Enter to continue");
3029         getc(opt_tty);
3030         reset_prog_mode();
3031         redraw_display(TRUE);
3032 }
3033
3034 static void
3035 open_mergetool(const char *file)
3036 {
3037         const char *mergetool_argv[] = { "git", "mergetool", file, NULL };
3038
3039         open_external_viewer(mergetool_argv, opt_cdup);
3040 }
3041
3042 static void
3043 open_editor(bool from_root, const char *file)
3044 {
3045         const char *editor_argv[] = { "vi", file, NULL };
3046         const char *editor;
3047
3048         editor = getenv("GIT_EDITOR");
3049         if (!editor && *opt_editor)
3050                 editor = opt_editor;
3051         if (!editor)
3052                 editor = getenv("VISUAL");
3053         if (!editor)
3054                 editor = getenv("EDITOR");
3055         if (!editor)
3056                 editor = "vi";
3057
3058         editor_argv[0] = editor;
3059         open_external_viewer(editor_argv, from_root ? opt_cdup : NULL);
3060 }
3061
3062 static void
3063 open_run_request(enum request request)
3064 {
3065         struct run_request *req = get_run_request(request);
3066         const char *argv[ARRAY_SIZE(req->argv)] = { NULL };
3067
3068         if (!req) {
3069                 report("Unknown run request");
3070                 return;
3071         }
3072
3073         if (format_argv(argv, req->argv, FORMAT_ALL))
3074                 open_external_viewer(argv, NULL);
3075         free_argv(argv);
3076 }
3077
3078 /*
3079  * User request switch noodle
3080  */
3081
3082 static int
3083 view_driver(struct view *view, enum request request)
3084 {
3085         int i;
3086
3087         if (request == REQ_NONE) {
3088                 doupdate();
3089                 return TRUE;
3090         }
3091
3092         if (request > REQ_NONE) {
3093                 open_run_request(request);
3094                 /* FIXME: When all views can refresh always do this. */
3095                 if (view == VIEW(REQ_VIEW_STATUS) ||
3096                     view == VIEW(REQ_VIEW_MAIN) ||
3097                     view == VIEW(REQ_VIEW_LOG) ||
3098                     view == VIEW(REQ_VIEW_STAGE))
3099                         request = REQ_REFRESH;
3100                 else
3101                         return TRUE;
3102         }
3103
3104         if (view && view->lines) {
3105                 request = view->ops->request(view, request, &view->line[view->lineno]);
3106                 if (request == REQ_NONE)
3107                         return TRUE;
3108         }
3109
3110         switch (request) {
3111         case REQ_MOVE_UP:
3112         case REQ_MOVE_DOWN:
3113         case REQ_MOVE_PAGE_UP:
3114         case REQ_MOVE_PAGE_DOWN:
3115         case REQ_MOVE_FIRST_LINE:
3116         case REQ_MOVE_LAST_LINE:
3117                 move_view(view, request);
3118                 break;
3119
3120         case REQ_SCROLL_LEFT:
3121         case REQ_SCROLL_RIGHT:
3122         case REQ_SCROLL_LINE_DOWN:
3123         case REQ_SCROLL_LINE_UP:
3124         case REQ_SCROLL_PAGE_DOWN:
3125         case REQ_SCROLL_PAGE_UP:
3126                 scroll_view(view, request);
3127                 break;
3128
3129         case REQ_VIEW_BLAME:
3130                 if (!opt_file[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_BLOB:
3139                 if (!ref_blob[0]) {
3140                         report("No file chosen, press %s to open tree view",
3141                                get_key(REQ_VIEW_TREE));
3142                         break;
3143                 }
3144                 open_view(view, request, OPEN_DEFAULT);
3145                 break;
3146
3147         case REQ_VIEW_PAGER:
3148                 if (!VIEW(REQ_VIEW_PAGER)->pipe && !VIEW(REQ_VIEW_PAGER)->lines) {
3149                         report("No pager content, press %s to run command from prompt",
3150                                get_key(REQ_PROMPT));
3151                         break;
3152                 }
3153                 open_view(view, request, OPEN_DEFAULT);
3154                 break;
3155
3156         case REQ_VIEW_STAGE:
3157                 if (!VIEW(REQ_VIEW_STAGE)->lines) {
3158                         report("No stage content, press %s to open the status view and choose file",
3159                                get_key(REQ_VIEW_STATUS));
3160                         break;
3161                 }
3162                 open_view(view, request, OPEN_DEFAULT);
3163                 break;
3164
3165         case REQ_VIEW_STATUS:
3166                 if (opt_is_inside_work_tree == FALSE) {
3167                         report("The status view requires a working tree");
3168                         break;
3169                 }
3170                 open_view(view, request, OPEN_DEFAULT);
3171                 break;
3172
3173         case REQ_VIEW_MAIN:
3174         case REQ_VIEW_DIFF:
3175         case REQ_VIEW_LOG:
3176         case REQ_VIEW_TREE:
3177         case REQ_VIEW_HELP:
3178                 open_view(view, request, OPEN_DEFAULT);
3179                 break;
3180
3181         case REQ_NEXT:
3182         case REQ_PREVIOUS:
3183                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
3184
3185                 if ((view == VIEW(REQ_VIEW_DIFF) &&
3186                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
3187                    (view == VIEW(REQ_VIEW_DIFF) &&
3188                      view->parent == VIEW(REQ_VIEW_BLAME)) ||
3189                    (view == VIEW(REQ_VIEW_STAGE) &&
3190                      view->parent == VIEW(REQ_VIEW_STATUS)) ||
3191                    (view == VIEW(REQ_VIEW_BLOB) &&
3192                      view->parent == VIEW(REQ_VIEW_TREE))) {
3193                         int line;
3194
3195                         view = view->parent;
3196                         line = view->lineno;
3197                         move_view(view, request);
3198                         if (view_is_displayed(view))
3199                                 update_view_title(view);
3200                         if (line != view->lineno)
3201                                 view->ops->request(view, REQ_ENTER,
3202                                                    &view->line[view->lineno]);
3203
3204                 } else {
3205                         move_view(view, request);
3206                 }
3207                 break;
3208
3209         case REQ_VIEW_NEXT:
3210         {
3211                 int nviews = displayed_views();
3212                 int next_view = (current_view + 1) % nviews;
3213
3214                 if (next_view == current_view) {
3215                         report("Only one view is displayed");
3216                         break;
3217                 }
3218
3219                 current_view = next_view;
3220                 /* Blur out the title of the previous view. */
3221                 update_view_title(view);
3222                 report("");
3223                 break;
3224         }
3225         case REQ_REFRESH:
3226                 report("Refreshing is not yet supported for the %s view", view->name);
3227                 break;
3228
3229         case REQ_MAXIMIZE:
3230                 if (displayed_views() == 2)
3231                         open_view(view, VIEW_REQ(view), OPEN_DEFAULT);
3232                 break;
3233
3234         case REQ_TOGGLE_LINENO:
3235                 toggle_view_option(&opt_line_number, "line numbers");
3236                 break;
3237
3238         case REQ_TOGGLE_DATE:
3239                 toggle_view_option(&opt_date, "date display");
3240                 break;
3241
3242         case REQ_TOGGLE_AUTHOR:
3243                 toggle_view_option(&opt_author, "author display");
3244                 break;
3245
3246         case REQ_TOGGLE_REV_GRAPH:
3247                 toggle_view_option(&opt_rev_graph, "revision graph display");
3248                 break;
3249
3250         case REQ_TOGGLE_REFS:
3251                 toggle_view_option(&opt_show_refs, "reference display");
3252                 break;
3253
3254         case REQ_SEARCH:
3255         case REQ_SEARCH_BACK:
3256                 search_view(view, request);
3257                 break;
3258
3259         case REQ_FIND_NEXT:
3260         case REQ_FIND_PREV:
3261                 find_next(view, request);
3262                 break;
3263
3264         case REQ_STOP_LOADING:
3265                 for (i = 0; i < ARRAY_SIZE(views); i++) {
3266                         view = &views[i];
3267                         if (view->pipe)
3268                                 report("Stopped loading the %s view", view->name),
3269                         end_update(view, TRUE);
3270                 }
3271                 break;
3272
3273         case REQ_SHOW_VERSION:
3274                 report("tig-%s (built %s)", TIG_VERSION, __DATE__);
3275                 return TRUE;
3276
3277         case REQ_SCREEN_REDRAW:
3278                 redraw_display(TRUE);
3279                 break;
3280
3281         case REQ_EDIT:
3282                 report("Nothing to edit");
3283                 break;
3284
3285         case REQ_ENTER:
3286                 report("Nothing to enter");
3287                 break;
3288
3289         case REQ_VIEW_CLOSE:
3290                 /* XXX: Mark closed views by letting view->parent point to the
3291                  * view itself. Parents to closed view should never be
3292                  * followed. */
3293                 if (view->parent &&
3294                     view->parent->parent != view->parent) {
3295                         memset(display, 0, sizeof(display));
3296                         current_view = 0;
3297                         display[current_view] = view->parent;
3298                         view->parent = view;
3299                         resize_display();
3300                         redraw_display(FALSE);
3301                         report("");
3302                         break;
3303                 }
3304                 /* Fall-through */
3305         case REQ_QUIT:
3306                 return FALSE;
3307
3308         default:
3309                 report("Unknown key, press 'h' for help");
3310                 return TRUE;
3311         }
3312
3313         return TRUE;
3314 }
3315
3316
3317 /*
3318  * View backend utilities
3319  */
3320
3321 /* Parse author lines where the name may be empty:
3322  *      author  <email@address.tld> 1138474660 +0100
3323  */
3324 static void
3325 parse_author_line(char *ident, char *author, size_t authorsize, struct tm *tm)
3326 {
3327         char *nameend = strchr(ident, '<');
3328         char *emailend = strchr(ident, '>');
3329
3330         if (nameend && emailend)
3331                 *nameend = *emailend = 0;
3332         ident = chomp_string(ident);
3333         if (!*ident) {
3334                 if (nameend)
3335                         ident = chomp_string(nameend + 1);
3336                 if (!*ident)
3337                         ident = "Unknown";
3338         }
3339
3340         string_ncopy_do(author, authorsize, ident, strlen(ident));
3341
3342         /* Parse epoch and timezone */
3343         if (emailend && emailend[1] == ' ') {
3344                 char *secs = emailend + 2;
3345                 char *zone = strchr(secs, ' ');
3346                 time_t time = (time_t) atol(secs);
3347
3348                 if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
3349                         long tz;
3350
3351                         zone++;
3352                         tz  = ('0' - zone[1]) * 60 * 60 * 10;
3353                         tz += ('0' - zone[2]) * 60 * 60;
3354                         tz += ('0' - zone[3]) * 60;
3355                         tz += ('0' - zone[4]) * 60;
3356
3357                         if (zone[0] == '-')
3358                                 tz = -tz;
3359
3360                         time -= tz;
3361                 }
3362
3363                 gmtime_r(&time, tm);
3364         }
3365 }
3366
3367 static enum input_status
3368 select_commit_parent_handler(void *data, char *buf, int c)
3369 {
3370         size_t parents = *(size_t *) data;
3371         int parent = 0;
3372
3373         if (!isdigit(c))
3374                 return INPUT_SKIP;
3375
3376         if (*buf)
3377                 parent = atoi(buf) * 10;
3378         parent += c - '0';
3379
3380         if (parent > parents)
3381                 return INPUT_SKIP;
3382         return INPUT_OK;
3383 }
3384
3385 static bool
3386 select_commit_parent(const char *id, char rev[SIZEOF_REV])
3387 {
3388         char buf[SIZEOF_STR * 4];
3389         const char *revlist_argv[] = {
3390                 "git", "rev-list", "-1", "--parents", id, NULL
3391         };
3392         int parents;
3393
3394         if (!run_io_buf(revlist_argv, buf, sizeof(buf)) ||
3395             !*chomp_string(buf) ||
3396             (parents = (strlen(buf) / 40) - 1) < 0) {
3397                 report("Failed to get parent information");
3398                 return FALSE;
3399
3400         } else if (parents == 0) {
3401                 report("The selected commit has no parents");
3402                 return FALSE;
3403         }
3404
3405         if (parents > 1) {
3406                 char prompt[SIZEOF_STR];
3407                 char *result;
3408
3409                 if (!string_format(prompt, "Which parent? [1..%d] ", parents))
3410                         return FALSE;
3411                 result = prompt_input(prompt, select_commit_parent_handler, &parents);
3412                 if (!result)
3413                         return FALSE;
3414                 parents = atoi(result);
3415         }
3416
3417         string_copy_rev(rev, &buf[41 * parents]);
3418         return TRUE;
3419 }
3420
3421 /*
3422  * Pager backend
3423  */
3424
3425 static bool
3426 pager_draw(struct view *view, struct line *line, unsigned int lineno)
3427 {
3428         char text[SIZEOF_STR];
3429
3430         if (opt_line_number && draw_lineno(view, lineno))
3431                 return TRUE;
3432
3433         string_expand(text, sizeof(text), line->data, opt_tab_size);
3434         draw_text(view, line->type, text, TRUE);
3435         return TRUE;
3436 }
3437
3438 static bool
3439 add_describe_ref(char *buf, size_t *bufpos, const char *commit_id, const char *sep)
3440 {
3441         const char *describe_argv[] = { "git", "describe", commit_id, NULL };
3442         char refbuf[SIZEOF_STR];
3443         char *ref = NULL;
3444
3445         if (run_io_buf(describe_argv, refbuf, sizeof(refbuf)))
3446                 ref = chomp_string(refbuf);
3447
3448         if (!ref || !*ref)
3449                 return TRUE;
3450
3451         /* This is the only fatal call, since it can "corrupt" the buffer. */
3452         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
3453                 return FALSE;
3454
3455         return TRUE;
3456 }
3457
3458 static void
3459 add_pager_refs(struct view *view, struct line *line)
3460 {
3461         char buf[SIZEOF_STR];
3462         char *commit_id = (char *)line->data + STRING_SIZE("commit ");
3463         struct ref **refs;
3464         size_t bufpos = 0, refpos = 0;
3465         const char *sep = "Refs: ";
3466         bool is_tag = FALSE;
3467
3468         assert(line->type == LINE_COMMIT);
3469
3470         refs = get_refs(commit_id);
3471         if (!refs) {
3472                 if (view == VIEW(REQ_VIEW_DIFF))
3473                         goto try_add_describe_ref;
3474                 return;
3475         }
3476
3477         do {
3478                 struct ref *ref = refs[refpos];
3479                 const char *fmt = ref->tag    ? "%s[%s]" :
3480                                   ref->remote ? "%s<%s>" : "%s%s";
3481
3482                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
3483                         return;
3484                 sep = ", ";
3485                 if (ref->tag)
3486                         is_tag = TRUE;
3487         } while (refs[refpos++]->next);
3488
3489         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
3490 try_add_describe_ref:
3491                 /* Add <tag>-g<commit_id> "fake" reference. */
3492                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
3493                         return;
3494         }
3495
3496         if (bufpos == 0)
3497                 return;
3498
3499         add_line_text(view, buf, LINE_PP_REFS);
3500 }
3501
3502 static bool
3503 pager_read(struct view *view, char *data)
3504 {
3505         struct line *line;
3506
3507         if (!data)
3508                 return TRUE;
3509
3510         line = add_line_text(view, data, get_line_type(data));
3511         if (!line)
3512                 return FALSE;
3513
3514         if (line->type == LINE_COMMIT &&
3515             (view == VIEW(REQ_VIEW_DIFF) ||
3516              view == VIEW(REQ_VIEW_LOG)))
3517                 add_pager_refs(view, line);
3518
3519         return TRUE;
3520 }
3521
3522 static enum request
3523 pager_request(struct view *view, enum request request, struct line *line)
3524 {
3525         int split = 0;
3526
3527         if (request != REQ_ENTER)
3528                 return request;
3529
3530         if (line->type == LINE_COMMIT &&
3531            (view == VIEW(REQ_VIEW_LOG) ||
3532             view == VIEW(REQ_VIEW_PAGER))) {
3533                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
3534                 split = 1;
3535         }
3536
3537         /* Always scroll the view even if it was split. That way
3538          * you can use Enter to scroll through the log view and
3539          * split open each commit diff. */
3540         scroll_view(view, REQ_SCROLL_LINE_DOWN);
3541
3542         /* FIXME: A minor workaround. Scrolling the view will call report("")
3543          * but if we are scrolling a non-current view this won't properly
3544          * update the view title. */
3545         if (split)
3546                 update_view_title(view);
3547
3548         return REQ_NONE;
3549 }
3550
3551 static bool
3552 pager_grep(struct view *view, struct line *line)
3553 {
3554         regmatch_t pmatch;
3555         char *text = line->data;
3556
3557         if (!*text)
3558                 return FALSE;
3559
3560         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
3561                 return FALSE;
3562
3563         return TRUE;
3564 }
3565
3566 static void
3567 pager_select(struct view *view, struct line *line)
3568 {
3569         if (line->type == LINE_COMMIT) {
3570                 char *text = (char *)line->data + STRING_SIZE("commit ");
3571
3572                 if (view != VIEW(REQ_VIEW_PAGER))
3573                         string_copy_rev(view->ref, text);
3574                 string_copy_rev(ref_commit, text);
3575         }
3576 }
3577
3578 static struct view_ops pager_ops = {
3579         "line",
3580         NULL,
3581         NULL,
3582         pager_read,
3583         pager_draw,
3584         pager_request,
3585         pager_grep,
3586         pager_select,
3587 };
3588
3589 static const char *log_argv[SIZEOF_ARG] = {
3590         "git", "log", "--no-color", "--cc", "--stat", "-n100", "%(head)", NULL
3591 };
3592
3593 static enum request
3594 log_request(struct view *view, enum request request, struct line *line)
3595 {
3596         switch (request) {
3597         case REQ_REFRESH:
3598                 load_refs();
3599                 open_view(view, REQ_VIEW_LOG, OPEN_REFRESH);
3600                 return REQ_NONE;
3601         default:
3602                 return pager_request(view, request, line);
3603         }
3604 }
3605
3606 static struct view_ops log_ops = {
3607         "line",
3608         log_argv,
3609         NULL,
3610         pager_read,
3611         pager_draw,
3612         log_request,
3613         pager_grep,
3614         pager_select,
3615 };
3616
3617 static const char *diff_argv[SIZEOF_ARG] = {
3618         "git", "show", "--pretty=fuller", "--no-color", "--root",
3619                 "--patch-with-stat", "--find-copies-harder", "-C", "%(commit)", NULL
3620 };
3621
3622 static struct view_ops diff_ops = {
3623         "line",
3624         diff_argv,
3625         NULL,
3626         pager_read,
3627         pager_draw,
3628         pager_request,
3629         pager_grep,
3630         pager_select,
3631 };
3632
3633 /*
3634  * Help backend
3635  */
3636
3637 static bool
3638 help_open(struct view *view)
3639 {
3640         char buf[SIZEOF_STR];
3641         size_t bufpos;
3642         int i;
3643
3644         if (view->lines > 0)
3645                 return TRUE;
3646
3647         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
3648
3649         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
3650                 const char *key;
3651
3652                 if (req_info[i].request == REQ_NONE)
3653                         continue;
3654
3655                 if (!req_info[i].request) {
3656                         add_line_text(view, "", LINE_DEFAULT);
3657                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
3658                         continue;
3659                 }
3660
3661                 key = get_key(req_info[i].request);
3662                 if (!*key)
3663                         key = "(no key defined)";
3664
3665                 for (bufpos = 0; bufpos <= req_info[i].namelen; bufpos++) {
3666                         buf[bufpos] = tolower(req_info[i].name[bufpos]);
3667                         if (buf[bufpos] == '_')
3668                                 buf[bufpos] = '-';
3669                 }
3670
3671                 add_line_format(view, LINE_DEFAULT, "    %-25s %-20s %s",
3672                                 key, buf, req_info[i].help);
3673         }
3674
3675         if (run_requests) {
3676                 add_line_text(view, "", LINE_DEFAULT);
3677                 add_line_text(view, "External commands:", LINE_DEFAULT);
3678         }
3679
3680         for (i = 0; i < run_requests; i++) {
3681                 struct run_request *req = get_run_request(REQ_NONE + i + 1);
3682                 const char *key;
3683                 int argc;
3684
3685                 if (!req)
3686                         continue;
3687
3688                 key = get_key_name(req->key);
3689                 if (!*key)
3690                         key = "(no key defined)";
3691
3692                 for (bufpos = 0, argc = 0; req->argv[argc]; argc++)
3693                         if (!string_format_from(buf, &bufpos, "%s%s",
3694                                                 argc ? " " : "", req->argv[argc]))
3695                                 return REQ_NONE;
3696
3697                 add_line_format(view, LINE_DEFAULT, "    %-10s %-14s `%s`",
3698                                 keymap_table[req->keymap].name, key, buf);
3699         }
3700
3701         return TRUE;
3702 }
3703
3704 static struct view_ops help_ops = {
3705         "line",
3706         NULL,
3707         help_open,
3708         NULL,
3709         pager_draw,
3710         pager_request,
3711         pager_grep,
3712         pager_select,
3713 };
3714
3715
3716 /*
3717  * Tree backend
3718  */
3719
3720 struct tree_stack_entry {
3721         struct tree_stack_entry *prev;  /* Entry below this in the stack */
3722         unsigned long lineno;           /* Line number to restore */
3723         char *name;                     /* Position of name in opt_path */
3724 };
3725
3726 /* The top of the path stack. */
3727 static struct tree_stack_entry *tree_stack = NULL;
3728 unsigned long tree_lineno = 0;
3729
3730 static void
3731 pop_tree_stack_entry(void)
3732 {
3733         struct tree_stack_entry *entry = tree_stack;
3734
3735         tree_lineno = entry->lineno;
3736         entry->name[0] = 0;
3737         tree_stack = entry->prev;
3738         free(entry);
3739 }
3740
3741 static void
3742 push_tree_stack_entry(const char *name, unsigned long lineno)
3743 {
3744         struct tree_stack_entry *entry = calloc(1, sizeof(*entry));
3745         size_t pathlen = strlen(opt_path);
3746
3747         if (!entry)
3748                 return;
3749
3750         entry->prev = tree_stack;
3751         entry->name = opt_path + pathlen;
3752         tree_stack = entry;
3753
3754         if (!string_format_from(opt_path, &pathlen, "%s/", name)) {
3755                 pop_tree_stack_entry();
3756                 return;
3757         }
3758
3759         /* Move the current line to the first tree entry. */
3760         tree_lineno = 1;
3761         entry->lineno = lineno;
3762 }
3763
3764 /* Parse output from git-ls-tree(1):
3765  *
3766  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
3767  */
3768
3769 #define SIZEOF_TREE_ATTR \
3770         STRING_SIZE("100644 blob f931e1d229c3e185caad4449bf5b66ed72462657\t")
3771
3772 #define SIZEOF_TREE_MODE \
3773         STRING_SIZE("100644 ")
3774
3775 #define TREE_ID_OFFSET \
3776         STRING_SIZE("100644 blob ")
3777
3778 struct tree_entry {
3779         char id[SIZEOF_REV];
3780         mode_t mode;
3781         struct tm time;                 /* Date from the author ident. */
3782         char author[75];                /* Author of the commit. */
3783         char name[1];
3784 };
3785
3786 static const char *
3787 tree_path(struct line *line)
3788 {
3789         return ((struct tree_entry *) line->data)->name;
3790 }
3791
3792
3793 static int
3794 tree_compare_entry(struct line *line1, struct line *line2)
3795 {
3796         if (line1->type != line2->type)
3797                 return line1->type == LINE_TREE_DIR ? -1 : 1;
3798         return strcmp(tree_path(line1), tree_path(line2));
3799 }
3800
3801 static struct line *
3802 tree_entry(struct view *view, enum line_type type, const char *path,
3803            const char *mode, const char *id)
3804 {
3805         struct tree_entry *entry = calloc(1, sizeof(*entry) + strlen(path));
3806         struct line *line = entry ? add_line_data(view, entry, type) : NULL;
3807
3808         if (!entry || !line) {
3809                 free(entry);
3810                 return NULL;
3811         }
3812
3813         strncpy(entry->name, path, strlen(path));
3814         if (mode)
3815                 entry->mode = strtoul(mode, NULL, 8);
3816         if (id)
3817                 string_copy_rev(entry->id, id);
3818
3819         return line;
3820 }
3821
3822 static bool
3823 tree_read_date(struct view *view, char *text, bool *read_date)
3824 {
3825         static char author_name[SIZEOF_STR];
3826         static struct tm author_time;
3827
3828         if (!text && *read_date) {
3829                 *read_date = FALSE;
3830                 return TRUE;
3831
3832         } else if (!text) {
3833                 char *path = *opt_path ? opt_path : ".";
3834                 /* Find next entry to process */
3835                 const char *log_file[] = {
3836                         "git", "log", "--no-color", "--pretty=raw",
3837                                 "--cc", "--raw", view->id, "--", path, NULL
3838                 };
3839                 struct io io = {};
3840
3841                 if (!view->lines) {
3842                         tree_entry(view, LINE_TREE_HEAD, opt_path, NULL, NULL);
3843                         report("Tree is empty");
3844                         return TRUE;
3845                 }
3846
3847                 if (!run_io_rd(&io, log_file, FORMAT_NONE)) {
3848                         report("Failed to load tree data");
3849                         return TRUE;
3850                 }
3851
3852                 done_io(view->pipe);
3853                 view->io = io;
3854                 *read_date = TRUE;
3855                 return FALSE;
3856
3857         } else if (*text == 'a' && get_line_type(text) == LINE_AUTHOR) {
3858                 parse_author_line(text + STRING_SIZE("author "),
3859                                   author_name, sizeof(author_name), &author_time);
3860
3861         } else if (*text == ':') {
3862                 char *pos;
3863                 size_t annotated = 1;
3864                 size_t i;
3865
3866                 pos = strchr(text, '\t');
3867                 if (!pos)
3868                         return TRUE;
3869                 text = pos + 1;
3870                 if (*opt_prefix && !strncmp(text, opt_prefix, strlen(opt_prefix)))
3871                         text += strlen(opt_prefix);
3872                 if (*opt_path && !strncmp(text, opt_path, strlen(opt_path)))
3873                         text += strlen(opt_path);
3874                 pos = strchr(text, '/');
3875                 if (pos)
3876                         *pos = 0;
3877
3878                 for (i = 1; i < view->lines; i++) {
3879                         struct line *line = &view->line[i];
3880                         struct tree_entry *entry = line->data;
3881
3882                         annotated += !!*entry->author;
3883                         if (*entry->author || strcmp(entry->name, text))
3884                                 continue;
3885
3886                         string_copy(entry->author, author_name);
3887                         memcpy(&entry->time, &author_time, sizeof(entry->time));
3888                         line->dirty = 1;
3889                         break;
3890                 }
3891
3892                 if (annotated == view->lines)
3893                         kill_io(view->pipe);
3894         }
3895         return TRUE;
3896 }
3897
3898 static bool
3899 tree_read(struct view *view, char *text)
3900 {
3901         static bool read_date = FALSE;
3902         struct tree_entry *data;
3903         struct line *entry, *line;
3904         enum line_type type;
3905         size_t textlen = text ? strlen(text) : 0;
3906         char *path = text + SIZEOF_TREE_ATTR;
3907
3908         if (read_date || !text)
3909                 return tree_read_date(view, text, &read_date);
3910
3911         if (textlen <= SIZEOF_TREE_ATTR)
3912                 return FALSE;
3913         if (view->lines == 0 &&
3914             !tree_entry(view, LINE_TREE_HEAD, opt_path, NULL, NULL))
3915                 return FALSE;
3916
3917         /* Strip the path part ... */
3918         if (*opt_path) {
3919                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
3920                 size_t striplen = strlen(opt_path);
3921
3922                 if (pathlen > striplen)
3923                         memmove(path, path + striplen,
3924                                 pathlen - striplen + 1);
3925
3926                 /* Insert "link" to parent directory. */
3927                 if (view->lines == 1 &&
3928                     !tree_entry(view, LINE_TREE_DIR, "..", "040000", view->ref))
3929                         return FALSE;
3930         }
3931
3932         type = text[SIZEOF_TREE_MODE] == 't' ? LINE_TREE_DIR : LINE_TREE_FILE;
3933         entry = tree_entry(view, type, path, text, text + TREE_ID_OFFSET);
3934         if (!entry)
3935                 return FALSE;
3936         data = entry->data;
3937
3938         /* Skip "Directory ..." and ".." line. */
3939         for (line = &view->line[1 + !!*opt_path]; line < entry; line++) {
3940                 if (tree_compare_entry(line, entry) <= 0)
3941                         continue;
3942
3943                 memmove(line + 1, line, (entry - line) * sizeof(*entry));
3944
3945                 line->data = data;
3946                 line->type = type;
3947                 for (; line <= entry; line++)
3948                         line->dirty = line->cleareol = 1;
3949                 return TRUE;
3950         }
3951
3952         if (tree_lineno > view->lineno) {
3953                 view->lineno = tree_lineno;
3954                 tree_lineno = 0;
3955         }
3956
3957         return TRUE;
3958 }
3959
3960 static bool
3961 tree_draw(struct view *view, struct line *line, unsigned int lineno)
3962 {
3963         struct tree_entry *entry = line->data;
3964
3965         if (line->type == LINE_TREE_HEAD) {
3966                 if (draw_text(view, line->type, "Directory path /", TRUE))
3967                         return TRUE;
3968         } else {
3969                 if (draw_mode(view, entry->mode))
3970                         return TRUE;
3971
3972                 if (opt_author && draw_author(view, entry->author))
3973                         return TRUE;
3974
3975                 if (opt_date && draw_date(view, *entry->author ? &entry->time : NULL))
3976                         return TRUE;
3977         }
3978         if (draw_text(view, line->type, entry->name, TRUE))
3979                 return TRUE;
3980         return TRUE;
3981 }
3982
3983 static void
3984 open_blob_editor()
3985 {
3986         char file[SIZEOF_STR] = "/tmp/tigblob.XXXXXX";
3987         int fd = mkstemp(file);
3988
3989         if (fd == -1)
3990                 report("Failed to create temporary file");
3991         else if (!run_io_append(blob_ops.argv, FORMAT_ALL, fd))
3992                 report("Failed to save blob data to file");
3993         else
3994                 open_editor(FALSE, file);
3995         if (fd != -1)
3996                 unlink(file);
3997 }
3998
3999 static enum request
4000 tree_request(struct view *view, enum request request, struct line *line)
4001 {
4002         enum open_flags flags;
4003
4004         switch (request) {
4005         case REQ_VIEW_BLAME:
4006                 if (line->type != LINE_TREE_FILE) {
4007                         report("Blame only supported for files");
4008                         return REQ_NONE;
4009                 }
4010
4011                 string_copy(opt_ref, view->vid);
4012                 return request;
4013
4014         case REQ_EDIT:
4015                 if (line->type != LINE_TREE_FILE) {
4016                         report("Edit only supported for files");
4017                 } else if (!is_head_commit(view->vid)) {
4018                         open_blob_editor();
4019                 } else {
4020                         open_editor(TRUE, opt_file);
4021                 }
4022                 return REQ_NONE;
4023
4024         case REQ_PARENT:
4025                 if (!*opt_path) {
4026                         /* quit view if at top of tree */
4027                         return REQ_VIEW_CLOSE;
4028                 }
4029                 /* fake 'cd  ..' */
4030                 line = &view->line[1];
4031                 break;
4032
4033         case REQ_ENTER:
4034                 break;
4035
4036         default:
4037                 return request;
4038         }
4039
4040         /* Cleanup the stack if the tree view is at a different tree. */
4041         while (!*opt_path && tree_stack)
4042                 pop_tree_stack_entry();
4043
4044         switch (line->type) {
4045         case LINE_TREE_DIR:
4046                 /* Depending on whether it is a subdirectory or parent link
4047                  * mangle the path buffer. */
4048                 if (line == &view->line[1] && *opt_path) {
4049                         pop_tree_stack_entry();
4050
4051                 } else {
4052                         const char *basename = tree_path(line);
4053
4054                         push_tree_stack_entry(basename, view->lineno);
4055                 }
4056
4057                 /* Trees and subtrees share the same ID, so they are not not
4058                  * unique like blobs. */
4059                 flags = OPEN_RELOAD;
4060                 request = REQ_VIEW_TREE;
4061                 break;
4062
4063         case LINE_TREE_FILE:
4064                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4065                 request = REQ_VIEW_BLOB;
4066                 break;
4067
4068         default:
4069                 return REQ_NONE;
4070         }
4071
4072         open_view(view, request, flags);
4073         if (request == REQ_VIEW_TREE)
4074                 view->lineno = tree_lineno;
4075
4076         return REQ_NONE;
4077 }
4078
4079 static void
4080 tree_select(struct view *view, struct line *line)
4081 {
4082         struct tree_entry *entry = line->data;
4083
4084         if (line->type == LINE_TREE_FILE) {
4085                 string_copy_rev(ref_blob, entry->id);
4086                 string_format(opt_file, "%s%s", opt_path, tree_path(line));
4087
4088         } else if (line->type != LINE_TREE_DIR) {
4089                 return;
4090         }
4091
4092         string_copy_rev(view->ref, entry->id);
4093 }
4094
4095 static const char *tree_argv[SIZEOF_ARG] = {
4096         "git", "ls-tree", "%(commit)", "%(directory)", NULL
4097 };
4098
4099 static struct view_ops tree_ops = {
4100         "file",
4101         tree_argv,
4102         NULL,
4103         tree_read,
4104         tree_draw,
4105         tree_request,
4106         pager_grep,
4107         tree_select,
4108 };
4109
4110 static bool
4111 blob_read(struct view *view, char *line)
4112 {
4113         if (!line)
4114                 return TRUE;
4115         return add_line_text(view, line, LINE_DEFAULT) != NULL;
4116 }
4117
4118 static enum request
4119 blob_request(struct view *view, enum request request, struct line *line)
4120 {
4121         switch (request) {
4122         case REQ_EDIT:
4123                 open_blob_editor();
4124                 return REQ_NONE;
4125         default:
4126                 return pager_request(view, request, line);
4127         }
4128 }
4129
4130 static const char *blob_argv[SIZEOF_ARG] = {
4131         "git", "cat-file", "blob", "%(blob)", NULL
4132 };
4133
4134 static struct view_ops blob_ops = {
4135         "line",
4136         blob_argv,
4137         NULL,
4138         blob_read,
4139         pager_draw,
4140         blob_request,
4141         pager_grep,
4142         pager_select,
4143 };
4144
4145 /*
4146  * Blame backend
4147  *
4148  * Loading the blame view is a two phase job:
4149  *
4150  *  1. File content is read either using opt_file from the
4151  *     filesystem or using git-cat-file.
4152  *  2. Then blame information is incrementally added by
4153  *     reading output from git-blame.
4154  */
4155
4156 static const char *blame_head_argv[] = {
4157         "git", "blame", "--incremental", "--", "%(file)", NULL
4158 };
4159
4160 static const char *blame_ref_argv[] = {
4161         "git", "blame", "--incremental", "%(ref)", "--", "%(file)", NULL
4162 };
4163
4164 static const char *blame_cat_file_argv[] = {
4165         "git", "cat-file", "blob", "%(ref):%(file)", NULL
4166 };
4167
4168 struct blame_commit {
4169         char id[SIZEOF_REV];            /* SHA1 ID. */
4170         char title[128];                /* First line of the commit message. */
4171         char author[75];                /* Author of the commit. */
4172         struct tm time;                 /* Date from the author ident. */
4173         char filename[128];             /* Name of file. */
4174         bool has_previous;              /* Was a "previous" line detected. */
4175 };
4176
4177 struct blame {
4178         struct blame_commit *commit;
4179         char text[1];
4180 };
4181
4182 static bool
4183 blame_open(struct view *view)
4184 {
4185         if (*opt_ref || !io_open(&view->io, opt_file)) {
4186                 if (!run_io_rd(&view->io, blame_cat_file_argv, FORMAT_ALL))
4187                         return FALSE;
4188         }
4189
4190         setup_update(view, opt_file);
4191         string_format(view->ref, "%s ...", opt_file);
4192
4193         return TRUE;
4194 }
4195
4196 static struct blame_commit *
4197 get_blame_commit(struct view *view, const char *id)
4198 {
4199         size_t i;
4200
4201         for (i = 0; i < view->lines; i++) {
4202                 struct blame *blame = view->line[i].data;
4203
4204                 if (!blame->commit)
4205                         continue;
4206
4207                 if (!strncmp(blame->commit->id, id, SIZEOF_REV - 1))
4208                         return blame->commit;
4209         }
4210
4211         {
4212                 struct blame_commit *commit = calloc(1, sizeof(*commit));
4213
4214                 if (commit)
4215                         string_ncopy(commit->id, id, SIZEOF_REV);
4216                 return commit;
4217         }
4218 }
4219
4220 static bool
4221 parse_number(const char **posref, size_t *number, size_t min, size_t max)
4222 {
4223         const char *pos = *posref;
4224
4225         *posref = NULL;
4226         pos = strchr(pos + 1, ' ');
4227         if (!pos || !isdigit(pos[1]))
4228                 return FALSE;
4229         *number = atoi(pos + 1);
4230         if (*number < min || *number > max)
4231                 return FALSE;
4232
4233         *posref = pos;
4234         return TRUE;
4235 }
4236
4237 static struct blame_commit *
4238 parse_blame_commit(struct view *view, const char *text, int *blamed)
4239 {
4240         struct blame_commit *commit;
4241         struct blame *blame;
4242         const char *pos = text + SIZEOF_REV - 1;
4243         size_t lineno;
4244         size_t group;
4245
4246         if (strlen(text) <= SIZEOF_REV || *pos != ' ')
4247                 return NULL;
4248
4249         if (!parse_number(&pos, &lineno, 1, view->lines) ||
4250             !parse_number(&pos, &group, 1, view->lines - lineno + 1))
4251                 return NULL;
4252
4253         commit = get_blame_commit(view, text);
4254         if (!commit)
4255                 return NULL;
4256
4257         *blamed += group;
4258         while (group--) {
4259                 struct line *line = &view->line[lineno + group - 1];
4260
4261                 blame = line->data;
4262                 blame->commit = commit;
4263                 line->dirty = 1;
4264         }
4265
4266         return commit;
4267 }
4268
4269 static bool
4270 blame_read_file(struct view *view, const char *line, bool *read_file)
4271 {
4272         if (!line) {
4273                 const char **argv = *opt_ref ? blame_ref_argv : blame_head_argv;
4274                 struct io io = {};
4275
4276                 if (view->lines == 0 && !view->parent)
4277                         die("No blame exist for %s", view->vid);
4278
4279                 if (view->lines == 0 || !run_io_rd(&io, argv, FORMAT_ALL)) {
4280                         report("Failed to load blame data");
4281                         return TRUE;
4282                 }
4283
4284                 done_io(view->pipe);
4285                 view->io = io;
4286                 *read_file = FALSE;
4287                 return FALSE;
4288
4289         } else {
4290                 size_t linelen = string_expand_length(line, opt_tab_size);
4291                 struct blame *blame = malloc(sizeof(*blame) + linelen);
4292
4293                 if (!blame)
4294                         return FALSE;
4295
4296                 blame->commit = NULL;
4297                 string_expand(blame->text, linelen + 1, line, opt_tab_size);
4298                 return add_line_data(view, blame, LINE_BLAME_ID) != NULL;
4299         }
4300 }
4301
4302 static bool
4303 match_blame_header(const char *name, char **line)
4304 {
4305         size_t namelen = strlen(name);
4306         bool matched = !strncmp(name, *line, namelen);
4307
4308         if (matched)
4309                 *line += namelen;
4310
4311         return matched;
4312 }
4313
4314 static bool
4315 blame_read(struct view *view, char *line)
4316 {
4317         static struct blame_commit *commit = NULL;
4318         static int blamed = 0;
4319         static time_t author_time;
4320         static bool read_file = TRUE;
4321
4322         if (read_file)
4323                 return blame_read_file(view, line, &read_file);
4324
4325         if (!line) {
4326                 /* Reset all! */
4327                 commit = NULL;
4328                 blamed = 0;
4329                 read_file = TRUE;
4330                 string_format(view->ref, "%s", view->vid);
4331                 if (view_is_displayed(view)) {
4332                         update_view_title(view);
4333                         redraw_view_from(view, 0);
4334                 }
4335                 return TRUE;
4336         }
4337
4338         if (!commit) {
4339                 commit = parse_blame_commit(view, line, &blamed);
4340                 string_format(view->ref, "%s %2d%%", view->vid,
4341                               view->lines ? blamed * 100 / view->lines : 0);
4342
4343         } else if (match_blame_header("author ", &line)) {
4344                 string_ncopy(commit->author, line, strlen(line));
4345
4346         } else if (match_blame_header("author-time ", &line)) {
4347                 author_time = (time_t) atol(line);
4348
4349         } else if (match_blame_header("author-tz ", &line)) {
4350                 long tz;
4351
4352                 tz  = ('0' - line[1]) * 60 * 60 * 10;
4353                 tz += ('0' - line[2]) * 60 * 60;
4354                 tz += ('0' - line[3]) * 60;
4355                 tz += ('0' - line[4]) * 60;
4356
4357                 if (line[0] == '-')
4358                         tz = -tz;
4359
4360                 author_time -= tz;
4361                 gmtime_r(&author_time, &commit->time);
4362
4363         } else if (match_blame_header("summary ", &line)) {
4364                 string_ncopy(commit->title, line, strlen(line));
4365
4366         } else if (match_blame_header("previous ", &line)) {
4367                 commit->has_previous = TRUE;
4368
4369         } else if (match_blame_header("filename ", &line)) {
4370                 string_ncopy(commit->filename, line, strlen(line));
4371                 commit = NULL;
4372         }
4373
4374         return TRUE;
4375 }
4376
4377 static bool
4378 blame_draw(struct view *view, struct line *line, unsigned int lineno)
4379 {
4380         struct blame *blame = line->data;
4381         struct tm *time = NULL;
4382         const char *id = NULL, *author = NULL;
4383
4384         if (blame->commit && *blame->commit->filename) {
4385                 id = blame->commit->id;
4386                 author = blame->commit->author;
4387                 time = &blame->commit->time;
4388         }
4389
4390         if (opt_date && draw_date(view, time))
4391                 return TRUE;
4392
4393         if (opt_author && draw_author(view, author))
4394                 return TRUE;
4395
4396         if (draw_field(view, LINE_BLAME_ID, id, ID_COLS, FALSE))
4397                 return TRUE;
4398
4399         if (draw_lineno(view, lineno))
4400                 return TRUE;
4401
4402         draw_text(view, LINE_DEFAULT, blame->text, TRUE);
4403         return TRUE;
4404 }
4405
4406 static bool
4407 check_blame_commit(struct blame *blame)
4408 {
4409         if (!blame->commit)
4410                 report("Commit data not loaded yet");
4411         else if (!strcmp(blame->commit->id, NULL_ID))
4412                 report("No commit exist for the selected line");
4413         else
4414                 return TRUE;
4415         return FALSE;
4416 }
4417
4418 static enum request
4419 blame_request(struct view *view, enum request request, struct line *line)
4420 {
4421         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4422         struct blame *blame = line->data;
4423
4424         switch (request) {
4425         case REQ_VIEW_BLAME:
4426                 if (check_blame_commit(blame)) {
4427                         string_copy(opt_ref, blame->commit->id);
4428                         open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
4429                 }
4430                 break;
4431
4432         case REQ_PARENT:
4433                 if (check_blame_commit(blame) &&
4434                     select_commit_parent(blame->commit->id, opt_ref))
4435                         open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
4436                 break;
4437
4438         case REQ_ENTER:
4439                 if (!blame->commit) {
4440                         report("No commit loaded yet");
4441                         break;
4442                 }
4443
4444                 if (view_is_displayed(VIEW(REQ_VIEW_DIFF)) &&
4445                     !strcmp(blame->commit->id, VIEW(REQ_VIEW_DIFF)->ref))
4446                         break;
4447
4448                 if (!strcmp(blame->commit->id, NULL_ID)) {
4449                         struct view *diff = VIEW(REQ_VIEW_DIFF);
4450                         const char *diff_index_argv[] = {
4451                                 "git", "diff-index", "--root", "--patch-with-stat",
4452                                         "-C", "-M", "HEAD", "--", view->vid, NULL
4453                         };
4454
4455                         if (!blame->commit->has_previous) {
4456                                 diff_index_argv[1] = "diff";
4457                                 diff_index_argv[2] = "--no-color";
4458                                 diff_index_argv[6] = "--";
4459                                 diff_index_argv[7] = "/dev/null";
4460                         }
4461
4462                         if (!prepare_update(diff, diff_index_argv, NULL, FORMAT_DASH)) {
4463                                 report("Failed to allocate diff command");
4464                                 break;
4465                         }
4466                         flags |= OPEN_PREPARED;
4467                 }
4468
4469                 open_view(view, REQ_VIEW_DIFF, flags);
4470                 if (VIEW(REQ_VIEW_DIFF)->pipe && !strcmp(blame->commit->id, NULL_ID))
4471                         string_copy_rev(VIEW(REQ_VIEW_DIFF)->ref, NULL_ID);
4472                 break;
4473
4474         default:
4475                 return request;
4476         }
4477
4478         return REQ_NONE;
4479 }
4480
4481 static bool
4482 blame_grep(struct view *view, struct line *line)
4483 {
4484         struct blame *blame = line->data;
4485         struct blame_commit *commit = blame->commit;
4486         regmatch_t pmatch;
4487
4488 #define MATCH(text, on)                                                 \
4489         (on && *text && regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4490
4491         if (commit) {
4492                 char buf[DATE_COLS + 1];
4493
4494                 if (MATCH(commit->title, 1) ||
4495                     MATCH(commit->author, opt_author) ||
4496                     MATCH(commit->id, opt_date))
4497                         return TRUE;
4498
4499                 if (strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time) &&
4500                     MATCH(buf, 1))
4501                         return TRUE;
4502         }
4503
4504         return MATCH(blame->text, 1);
4505
4506 #undef MATCH
4507 }
4508
4509 static void
4510 blame_select(struct view *view, struct line *line)
4511 {
4512         struct blame *blame = line->data;
4513         struct blame_commit *commit = blame->commit;
4514
4515         if (!commit)
4516                 return;
4517
4518         if (!strcmp(commit->id, NULL_ID))
4519                 string_ncopy(ref_commit, "HEAD", 4);
4520         else
4521                 string_copy_rev(ref_commit, commit->id);
4522 }
4523
4524 static struct view_ops blame_ops = {
4525         "line",
4526         NULL,
4527         blame_open,
4528         blame_read,
4529         blame_draw,
4530         blame_request,
4531         blame_grep,
4532         blame_select,
4533 };
4534
4535 /*
4536  * Status backend
4537  */
4538
4539 struct status {
4540         char status;
4541         struct {
4542                 mode_t mode;
4543                 char rev[SIZEOF_REV];
4544                 char name[SIZEOF_STR];
4545         } old;
4546         struct {
4547                 mode_t mode;
4548                 char rev[SIZEOF_REV];
4549                 char name[SIZEOF_STR];
4550         } new;
4551 };
4552
4553 static char status_onbranch[SIZEOF_STR];
4554 static struct status stage_status;
4555 static enum line_type stage_line_type;
4556 static size_t stage_chunks;
4557 static int *stage_chunk;
4558
4559 /* This should work even for the "On branch" line. */
4560 static inline bool
4561 status_has_none(struct view *view, struct line *line)
4562 {
4563         return line < view->line + view->lines && !line[1].data;
4564 }
4565
4566 /* Get fields from the diff line:
4567  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
4568  */
4569 static inline bool
4570 status_get_diff(struct status *file, const char *buf, size_t bufsize)
4571 {
4572         const char *old_mode = buf +  1;
4573         const char *new_mode = buf +  8;
4574         const char *old_rev  = buf + 15;
4575         const char *new_rev  = buf + 56;
4576         const char *status   = buf + 97;
4577
4578         if (bufsize < 98 ||
4579             old_mode[-1] != ':' ||
4580             new_mode[-1] != ' ' ||
4581             old_rev[-1]  != ' ' ||
4582             new_rev[-1]  != ' ' ||
4583             status[-1]   != ' ')
4584                 return FALSE;
4585
4586         file->status = *status;
4587
4588         string_copy_rev(file->old.rev, old_rev);
4589         string_copy_rev(file->new.rev, new_rev);
4590
4591         file->old.mode = strtoul(old_mode, NULL, 8);
4592         file->new.mode = strtoul(new_mode, NULL, 8);
4593
4594         file->old.name[0] = file->new.name[0] = 0;
4595
4596         return TRUE;
4597 }
4598
4599 static bool
4600 status_run(struct view *view, const char *argv[], char status, enum line_type type)
4601 {
4602         struct status *unmerged = NULL;
4603         char *buf;
4604         struct io io = {};
4605
4606         if (!run_io(&io, argv, NULL, IO_RD))
4607                 return FALSE;
4608
4609         add_line_data(view, NULL, type);
4610
4611         while ((buf = io_get(&io, 0, TRUE))) {
4612                 struct status *file = unmerged;
4613
4614                 if (!file) {
4615                         file = calloc(1, sizeof(*file));
4616                         if (!file || !add_line_data(view, file, type))
4617                                 goto error_out;
4618                 }
4619
4620                 /* Parse diff info part. */
4621                 if (status) {
4622                         file->status = status;
4623                         if (status == 'A')
4624                                 string_copy(file->old.rev, NULL_ID);
4625
4626                 } else if (!file->status || file == unmerged) {
4627                         if (!status_get_diff(file, buf, strlen(buf)))
4628                                 goto error_out;
4629
4630                         buf = io_get(&io, 0, TRUE);
4631                         if (!buf)
4632                                 break;
4633
4634                         /* Collapse all modified entries that follow an
4635                          * associated unmerged entry. */
4636                         if (unmerged == file) {
4637                                 unmerged->status = 'U';
4638                                 unmerged = NULL;
4639                         } else if (file->status == 'U') {
4640                                 unmerged = file;
4641                         }
4642                 }
4643
4644                 /* Grab the old name for rename/copy. */
4645                 if (!*file->old.name &&
4646                     (file->status == 'R' || file->status == 'C')) {
4647                         string_ncopy(file->old.name, buf, strlen(buf));
4648
4649                         buf = io_get(&io, 0, TRUE);
4650                         if (!buf)
4651                                 break;
4652                 }
4653
4654                 /* git-ls-files just delivers a NUL separated list of
4655                  * file names similar to the second half of the
4656                  * git-diff-* output. */
4657                 string_ncopy(file->new.name, buf, strlen(buf));
4658                 if (!*file->old.name)
4659                         string_copy(file->old.name, file->new.name);
4660                 file = NULL;
4661         }
4662
4663         if (io_error(&io)) {
4664 error_out:
4665                 done_io(&io);
4666                 return FALSE;
4667         }
4668
4669         if (!view->line[view->lines - 1].data)
4670                 add_line_data(view, NULL, LINE_STAT_NONE);
4671
4672         done_io(&io);
4673         return TRUE;
4674 }
4675
4676 /* Don't show unmerged entries in the staged section. */
4677 static const char *status_diff_index_argv[] = {
4678         "git", "diff-index", "-z", "--diff-filter=ACDMRTXB",
4679                              "--cached", "-M", "HEAD", NULL
4680 };
4681
4682 static const char *status_diff_files_argv[] = {
4683         "git", "diff-files", "-z", NULL
4684 };
4685
4686 static const char *status_list_other_argv[] = {
4687         "git", "ls-files", "-z", "--others", "--exclude-standard", NULL
4688 };
4689
4690 static const char *status_list_no_head_argv[] = {
4691         "git", "ls-files", "-z", "--cached", "--exclude-standard", NULL
4692 };
4693
4694 static const char *update_index_argv[] = {
4695         "git", "update-index", "-q", "--unmerged", "--refresh", NULL
4696 };
4697
4698 /* Restore the previous line number to stay in the context or select a
4699  * line with something that can be updated. */
4700 static void
4701 status_restore(struct view *view)
4702 {
4703         if (view->p_lineno >= view->lines)
4704                 view->p_lineno = view->lines - 1;
4705         while (view->p_lineno < view->lines && !view->line[view->p_lineno].data)
4706                 view->p_lineno++;
4707         while (view->p_lineno > 0 && !view->line[view->p_lineno].data)
4708                 view->p_lineno--;
4709
4710         /* If the above fails, always skip the "On branch" line. */
4711         if (view->p_lineno < view->lines)
4712                 view->lineno = view->p_lineno;
4713         else
4714                 view->lineno = 1;
4715
4716         if (view->lineno < view->offset)
4717                 view->offset = view->lineno;
4718         else if (view->offset + view->height <= view->lineno)
4719                 view->offset = view->lineno - view->height + 1;
4720
4721         view->p_restore = FALSE;
4722 }
4723
4724 /* First parse staged info using git-diff-index(1), then parse unstaged
4725  * info using git-diff-files(1), and finally untracked files using
4726  * git-ls-files(1). */
4727 static bool
4728 status_open(struct view *view)
4729 {
4730         reset_view(view);
4731
4732         add_line_data(view, NULL, LINE_STAT_HEAD);
4733         if (is_initial_commit())
4734                 string_copy(status_onbranch, "Initial commit");
4735         else if (!*opt_head)
4736                 string_copy(status_onbranch, "Not currently on any branch");
4737         else if (!string_format(status_onbranch, "On branch %s", opt_head))
4738                 return FALSE;
4739
4740         run_io_bg(update_index_argv);
4741
4742         if (is_initial_commit()) {
4743                 if (!status_run(view, status_list_no_head_argv, 'A', LINE_STAT_STAGED))
4744                         return FALSE;
4745         } else if (!status_run(view, status_diff_index_argv, 0, LINE_STAT_STAGED)) {
4746                 return FALSE;
4747         }
4748
4749         if (!status_run(view, status_diff_files_argv, 0, LINE_STAT_UNSTAGED) ||
4750             !status_run(view, status_list_other_argv, '?', LINE_STAT_UNTRACKED))
4751                 return FALSE;
4752
4753         /* Restore the exact position or use the specialized restore
4754          * mode? */
4755         if (!view->p_restore)
4756                 status_restore(view);
4757         return TRUE;
4758 }
4759
4760 static bool
4761 status_draw(struct view *view, struct line *line, unsigned int lineno)
4762 {
4763         struct status *status = line->data;
4764         enum line_type type;
4765         const char *text;
4766
4767         if (!status) {
4768                 switch (line->type) {
4769                 case LINE_STAT_STAGED:
4770                         type = LINE_STAT_SECTION;
4771                         text = "Changes to be committed:";
4772                         break;
4773
4774                 case LINE_STAT_UNSTAGED:
4775                         type = LINE_STAT_SECTION;
4776                         text = "Changed but not updated:";
4777                         break;
4778
4779                 case LINE_STAT_UNTRACKED:
4780                         type = LINE_STAT_SECTION;
4781                         text = "Untracked files:";
4782                         break;
4783
4784                 case LINE_STAT_NONE:
4785                         type = LINE_DEFAULT;
4786                         text = "  (no files)";
4787                         break;
4788
4789                 case LINE_STAT_HEAD:
4790                         type = LINE_STAT_HEAD;
4791                         text = status_onbranch;
4792                         break;
4793
4794                 default:
4795                         return FALSE;
4796                 }
4797         } else {
4798                 static char buf[] = { '?', ' ', ' ', ' ', 0 };
4799
4800                 buf[0] = status->status;
4801                 if (draw_text(view, line->type, buf, TRUE))
4802                         return TRUE;
4803                 type = LINE_DEFAULT;
4804                 text = status->new.name;
4805         }
4806
4807         draw_text(view, type, text, TRUE);
4808         return TRUE;
4809 }
4810
4811 static enum request
4812 status_enter(struct view *view, struct line *line)
4813 {
4814         struct status *status = line->data;
4815         const char *oldpath = status ? status->old.name : NULL;
4816         /* Diffs for unmerged entries are empty when passing the new
4817          * path, so leave it empty. */
4818         const char *newpath = status && status->status != 'U' ? status->new.name : NULL;
4819         const char *info;
4820         enum open_flags split;
4821         struct view *stage = VIEW(REQ_VIEW_STAGE);
4822
4823         if (line->type == LINE_STAT_NONE ||
4824             (!status && line[1].type == LINE_STAT_NONE)) {
4825                 report("No file to diff");
4826                 return REQ_NONE;
4827         }
4828
4829         switch (line->type) {
4830         case LINE_STAT_STAGED:
4831                 if (is_initial_commit()) {
4832                         const char *no_head_diff_argv[] = {
4833                                 "git", "diff", "--no-color", "--patch-with-stat",
4834                                         "--", "/dev/null", newpath, NULL
4835                         };
4836
4837                         if (!prepare_update(stage, no_head_diff_argv, opt_cdup, FORMAT_DASH))
4838                                 return REQ_QUIT;
4839                 } else {
4840                         const char *index_show_argv[] = {
4841                                 "git", "diff-index", "--root", "--patch-with-stat",
4842                                         "-C", "-M", "--cached", "HEAD", "--",
4843                                         oldpath, newpath, NULL
4844                         };
4845
4846                         if (!prepare_update(stage, index_show_argv, opt_cdup, FORMAT_DASH))
4847                                 return REQ_QUIT;
4848                 }
4849
4850                 if (status)
4851                         info = "Staged changes to %s";
4852                 else
4853                         info = "Staged changes";
4854                 break;
4855
4856         case LINE_STAT_UNSTAGED:
4857         {
4858                 const char *files_show_argv[] = {
4859                         "git", "diff-files", "--root", "--patch-with-stat",
4860                                 "-C", "-M", "--", oldpath, newpath, NULL
4861                 };
4862
4863                 if (!prepare_update(stage, files_show_argv, opt_cdup, FORMAT_DASH))
4864                         return REQ_QUIT;
4865                 if (status)
4866                         info = "Unstaged changes to %s";
4867                 else
4868                         info = "Unstaged changes";
4869                 break;
4870         }
4871         case LINE_STAT_UNTRACKED:
4872                 if (!newpath) {
4873                         report("No file to show");
4874                         return REQ_NONE;
4875                 }
4876
4877                 if (!suffixcmp(status->new.name, -1, "/")) {
4878                         report("Cannot display a directory");
4879                         return REQ_NONE;
4880                 }
4881
4882                 if (!prepare_update_file(stage, newpath))
4883                         return REQ_QUIT;
4884                 info = "Untracked file %s";
4885                 break;
4886
4887         case LINE_STAT_HEAD:
4888                 return REQ_NONE;
4889
4890         default:
4891                 die("line type %d not handled in switch", line->type);
4892         }
4893
4894         split = view_is_displayed(view) ? OPEN_SPLIT : 0;
4895         open_view(view, REQ_VIEW_STAGE, OPEN_PREPARED | split);
4896         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
4897                 if (status) {
4898                         stage_status = *status;
4899                 } else {
4900                         memset(&stage_status, 0, sizeof(stage_status));
4901                 }
4902
4903                 stage_line_type = line->type;
4904                 stage_chunks = 0;
4905                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
4906         }
4907
4908         return REQ_NONE;
4909 }
4910
4911 static bool
4912 status_exists(struct status *status, enum line_type type)
4913 {
4914         struct view *view = VIEW(REQ_VIEW_STATUS);
4915         unsigned long lineno;
4916
4917         for (lineno = 0; lineno < view->lines; lineno++) {
4918                 struct line *line = &view->line[lineno];
4919                 struct status *pos = line->data;
4920
4921                 if (line->type != type)
4922                         continue;
4923                 if (!pos && (!status || !status->status) && line[1].data) {
4924                         select_view_line(view, lineno);
4925                         return TRUE;
4926                 }
4927                 if (pos && !strcmp(status->new.name, pos->new.name)) {
4928                         select_view_line(view, lineno);
4929                         return TRUE;
4930                 }
4931         }
4932
4933         return FALSE;
4934 }
4935
4936
4937 static bool
4938 status_update_prepare(struct io *io, enum line_type type)
4939 {
4940         const char *staged_argv[] = {
4941                 "git", "update-index", "-z", "--index-info", NULL
4942         };
4943         const char *others_argv[] = {
4944                 "git", "update-index", "-z", "--add", "--remove", "--stdin", NULL
4945         };
4946
4947         switch (type) {
4948         case LINE_STAT_STAGED:
4949                 return run_io(io, staged_argv, opt_cdup, IO_WR);
4950
4951         case LINE_STAT_UNSTAGED:
4952                 return run_io(io, others_argv, opt_cdup, IO_WR);
4953
4954         case LINE_STAT_UNTRACKED:
4955                 return run_io(io, others_argv, NULL, IO_WR);
4956
4957         default:
4958                 die("line type %d not handled in switch", type);
4959                 return FALSE;
4960         }
4961 }
4962
4963 static bool
4964 status_update_write(struct io *io, struct status *status, enum line_type type)
4965 {
4966         char buf[SIZEOF_STR];
4967         size_t bufsize = 0;
4968
4969         switch (type) {
4970         case LINE_STAT_STAGED:
4971                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
4972                                         status->old.mode,
4973                                         status->old.rev,
4974                                         status->old.name, 0))
4975                         return FALSE;
4976                 break;
4977
4978         case LINE_STAT_UNSTAGED:
4979         case LINE_STAT_UNTRACKED:
4980                 if (!string_format_from(buf, &bufsize, "%s%c", status->new.name, 0))
4981                         return FALSE;
4982                 break;
4983
4984         default:
4985                 die("line type %d not handled in switch", type);
4986         }
4987
4988         return io_write(io, buf, bufsize);
4989 }
4990
4991 static bool
4992 status_update_file(struct status *status, enum line_type type)
4993 {
4994         struct io io = {};
4995         bool result;
4996
4997         if (!status_update_prepare(&io, type))
4998                 return FALSE;
4999
5000         result = status_update_write(&io, status, type);
5001         done_io(&io);
5002         return result;
5003 }
5004
5005 static bool
5006 status_update_files(struct view *view, struct line *line)
5007 {
5008         struct io io = {};
5009         bool result = TRUE;
5010         struct line *pos = view->line + view->lines;
5011         int files = 0;
5012         int file, done;
5013
5014         if (!status_update_prepare(&io, line->type))
5015                 return FALSE;
5016
5017         for (pos = line; pos < view->line + view->lines && pos->data; pos++)
5018                 files++;
5019
5020         for (file = 0, done = 0; result && file < files; line++, file++) {
5021                 int almost_done = file * 100 / files;
5022
5023                 if (almost_done > done) {
5024                         done = almost_done;
5025                         string_format(view->ref, "updating file %u of %u (%d%% done)",
5026                                       file, files, done);
5027                         update_view_title(view);
5028                 }
5029                 result = status_update_write(&io, line->data, line->type);
5030         }
5031
5032         done_io(&io);
5033         return result;
5034 }
5035
5036 static bool
5037 status_update(struct view *view)
5038 {
5039         struct line *line = &view->line[view->lineno];
5040
5041         assert(view->lines);
5042
5043         if (!line->data) {
5044                 /* This should work even for the "On branch" line. */
5045                 if (line < view->line + view->lines && !line[1].data) {
5046                         report("Nothing to update");
5047                         return FALSE;
5048                 }
5049
5050                 if (!status_update_files(view, line + 1)) {
5051                         report("Failed to update file status");
5052                         return FALSE;
5053                 }
5054
5055         } else if (!status_update_file(line->data, line->type)) {
5056                 report("Failed to update file status");
5057                 return FALSE;
5058         }
5059
5060         return TRUE;
5061 }
5062
5063 static bool
5064 status_revert(struct status *status, enum line_type type, bool has_none)
5065 {
5066         if (!status || type != LINE_STAT_UNSTAGED) {
5067                 if (type == LINE_STAT_STAGED) {
5068                         report("Cannot revert changes to staged files");
5069                 } else if (type == LINE_STAT_UNTRACKED) {
5070                         report("Cannot revert changes to untracked files");
5071                 } else if (has_none) {
5072                         report("Nothing to revert");
5073                 } else {
5074                         report("Cannot revert changes to multiple files");
5075                 }
5076                 return FALSE;
5077
5078         } else {
5079                 char mode[10] = "100644";
5080                 const char *reset_argv[] = {
5081                         "git", "update-index", "--cacheinfo", mode,
5082                                 status->old.rev, status->old.name, NULL
5083                 };
5084                 const char *checkout_argv[] = {
5085                         "git", "checkout", "--", status->old.name, NULL
5086                 };
5087
5088                 if (!prompt_yesno("Are you sure you want to overwrite any changes?"))
5089                         return FALSE;
5090                 string_format(mode, "%o", status->old.mode);
5091                 return (status->status != 'U' || run_io_fg(reset_argv, opt_cdup)) &&
5092                         run_io_fg(checkout_argv, opt_cdup);
5093         }
5094 }
5095
5096 static enum request
5097 status_request(struct view *view, enum request request, struct line *line)
5098 {
5099         struct status *status = line->data;
5100
5101         switch (request) {
5102         case REQ_STATUS_UPDATE:
5103                 if (!status_update(view))
5104                         return REQ_NONE;
5105                 break;
5106
5107         case REQ_STATUS_REVERT:
5108                 if (!status_revert(status, line->type, status_has_none(view, line)))
5109                         return REQ_NONE;
5110                 break;
5111
5112         case REQ_STATUS_MERGE:
5113                 if (!status || status->status != 'U') {
5114                         report("Merging only possible for files with unmerged status ('U').");
5115                         return REQ_NONE;
5116                 }
5117                 open_mergetool(status->new.name);
5118                 break;
5119
5120         case REQ_EDIT:
5121                 if (!status)
5122                         return request;
5123                 if (status->status == 'D') {
5124                         report("File has been deleted.");
5125                         return REQ_NONE;
5126                 }
5127
5128                 open_editor(status->status != '?', status->new.name);
5129                 break;
5130
5131         case REQ_VIEW_BLAME:
5132                 if (status) {
5133                         string_copy(opt_file, status->new.name);
5134                         opt_ref[0] = 0;
5135                 }
5136                 return request;
5137
5138         case REQ_ENTER:
5139                 /* After returning the status view has been split to
5140                  * show the stage view. No further reloading is
5141                  * necessary. */
5142                 status_enter(view, line);
5143                 return REQ_NONE;
5144
5145         case REQ_REFRESH:
5146                 /* Simply reload the view. */
5147                 break;
5148
5149         default:
5150                 return request;
5151         }
5152
5153         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
5154
5155         return REQ_NONE;
5156 }
5157
5158 static void
5159 status_select(struct view *view, struct line *line)
5160 {
5161         struct status *status = line->data;
5162         char file[SIZEOF_STR] = "all files";
5163         const char *text;
5164         const char *key;
5165
5166         if (status && !string_format(file, "'%s'", status->new.name))
5167                 return;
5168
5169         if (!status && line[1].type == LINE_STAT_NONE)
5170                 line++;
5171
5172         switch (line->type) {
5173         case LINE_STAT_STAGED:
5174                 text = "Press %s to unstage %s for commit";
5175                 break;
5176
5177         case LINE_STAT_UNSTAGED:
5178                 text = "Press %s to stage %s for commit";
5179                 break;
5180
5181         case LINE_STAT_UNTRACKED:
5182                 text = "Press %s to stage %s for addition";
5183                 break;
5184
5185         case LINE_STAT_HEAD:
5186         case LINE_STAT_NONE:
5187                 text = "Nothing to update";
5188                 break;
5189
5190         default:
5191                 die("line type %d not handled in switch", line->type);
5192         }
5193
5194         if (status && status->status == 'U') {
5195                 text = "Press %s to resolve conflict in %s";
5196                 key = get_key(REQ_STATUS_MERGE);
5197
5198         } else {
5199                 key = get_key(REQ_STATUS_UPDATE);
5200         }
5201
5202         string_format(view->ref, text, key, file);
5203 }
5204
5205 static bool
5206 status_grep(struct view *view, struct line *line)
5207 {
5208         struct status *status = line->data;
5209         enum { S_STATUS, S_NAME, S_END } state;
5210         char buf[2] = "?";
5211         regmatch_t pmatch;
5212
5213         if (!status)
5214                 return FALSE;
5215
5216         for (state = S_STATUS; state < S_END; state++) {
5217                 const char *text;
5218
5219                 switch (state) {
5220                 case S_NAME:    text = status->new.name;        break;
5221                 case S_STATUS:
5222                         buf[0] = status->status;
5223                         text = buf;
5224                         break;
5225
5226                 default:
5227                         return FALSE;
5228                 }
5229
5230                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5231                         return TRUE;
5232         }
5233
5234         return FALSE;
5235 }
5236
5237 static struct view_ops status_ops = {
5238         "file",
5239         NULL,
5240         status_open,
5241         NULL,
5242         status_draw,
5243         status_request,
5244         status_grep,
5245         status_select,
5246 };
5247
5248
5249 static bool
5250 stage_diff_write(struct io *io, struct line *line, struct line *end)
5251 {
5252         while (line < end) {
5253                 if (!io_write(io, line->data, strlen(line->data)) ||
5254                     !io_write(io, "\n", 1))
5255                         return FALSE;
5256                 line++;
5257                 if (line->type == LINE_DIFF_CHUNK ||
5258                     line->type == LINE_DIFF_HEADER)
5259                         break;
5260         }
5261
5262         return TRUE;
5263 }
5264
5265 static struct line *
5266 stage_diff_find(struct view *view, struct line *line, enum line_type type)
5267 {
5268         for (; view->line < line; line--)
5269                 if (line->type == type)
5270                         return line;
5271
5272         return NULL;
5273 }
5274
5275 static bool
5276 stage_apply_chunk(struct view *view, struct line *chunk, bool revert)
5277 {
5278         const char *apply_argv[SIZEOF_ARG] = {
5279                 "git", "apply", "--whitespace=nowarn", NULL
5280         };
5281         struct line *diff_hdr;
5282         struct io io = {};
5283         int argc = 3;
5284
5285         diff_hdr = stage_diff_find(view, chunk, LINE_DIFF_HEADER);
5286         if (!diff_hdr)
5287                 return FALSE;
5288
5289         if (!revert)
5290                 apply_argv[argc++] = "--cached";
5291         if (revert || stage_line_type == LINE_STAT_STAGED)
5292                 apply_argv[argc++] = "-R";
5293         apply_argv[argc++] = "-";
5294         apply_argv[argc++] = NULL;
5295         if (!run_io(&io, apply_argv, opt_cdup, IO_WR))
5296                 return FALSE;
5297
5298         if (!stage_diff_write(&io, diff_hdr, chunk) ||
5299             !stage_diff_write(&io, chunk, view->line + view->lines))
5300                 chunk = NULL;
5301
5302         done_io(&io);
5303         run_io_bg(update_index_argv);
5304
5305         return chunk ? TRUE : FALSE;
5306 }
5307
5308 static bool
5309 stage_update(struct view *view, struct line *line)
5310 {
5311         struct line *chunk = NULL;
5312
5313         if (!is_initial_commit() && stage_line_type != LINE_STAT_UNTRACKED)
5314                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
5315
5316         if (chunk) {
5317                 if (!stage_apply_chunk(view, chunk, FALSE)) {
5318                         report("Failed to apply chunk");
5319                         return FALSE;
5320                 }
5321
5322         } else if (!stage_status.status) {
5323                 view = VIEW(REQ_VIEW_STATUS);
5324
5325                 for (line = view->line; line < view->line + view->lines; line++)
5326                         if (line->type == stage_line_type)
5327                                 break;
5328
5329                 if (!status_update_files(view, line + 1)) {
5330                         report("Failed to update files");
5331                         return FALSE;
5332                 }
5333
5334         } else if (!status_update_file(&stage_status, stage_line_type)) {
5335                 report("Failed to update file");
5336                 return FALSE;
5337         }
5338
5339         return TRUE;
5340 }
5341
5342 static bool
5343 stage_revert(struct view *view, struct line *line)
5344 {
5345         struct line *chunk = NULL;
5346
5347         if (!is_initial_commit() && stage_line_type == LINE_STAT_UNSTAGED)
5348                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
5349
5350         if (chunk) {
5351                 if (!prompt_yesno("Are you sure you want to revert changes?"))
5352                         return FALSE;
5353
5354                 if (!stage_apply_chunk(view, chunk, TRUE)) {
5355                         report("Failed to revert chunk");
5356                         return FALSE;
5357                 }
5358                 return TRUE;
5359
5360         } else {
5361                 return status_revert(stage_status.status ? &stage_status : NULL,
5362                                      stage_line_type, FALSE);
5363         }
5364 }
5365
5366
5367 static void
5368 stage_next(struct view *view, struct line *line)
5369 {
5370         int i;
5371
5372         if (!stage_chunks) {
5373                 static size_t alloc = 0;
5374                 int *tmp;
5375
5376                 for (line = view->line; line < view->line + view->lines; line++) {
5377                         if (line->type != LINE_DIFF_CHUNK)
5378                                 continue;
5379
5380                         tmp = realloc_items(stage_chunk, &alloc,
5381                                             stage_chunks, sizeof(*tmp));
5382                         if (!tmp) {
5383                                 report("Allocation failure");
5384                                 return;
5385                         }
5386
5387                         stage_chunk = tmp;
5388                         stage_chunk[stage_chunks++] = line - view->line;
5389                 }
5390         }
5391
5392         for (i = 0; i < stage_chunks; i++) {
5393                 if (stage_chunk[i] > view->lineno) {
5394                         do_scroll_view(view, stage_chunk[i] - view->lineno);
5395                         report("Chunk %d of %d", i + 1, stage_chunks);
5396                         return;
5397                 }
5398         }
5399
5400         report("No next chunk found");
5401 }
5402
5403 static enum request
5404 stage_request(struct view *view, enum request request, struct line *line)
5405 {
5406         switch (request) {
5407         case REQ_STATUS_UPDATE:
5408                 if (!stage_update(view, line))
5409                         return REQ_NONE;
5410                 break;
5411
5412         case REQ_STATUS_REVERT:
5413                 if (!stage_revert(view, line))
5414                         return REQ_NONE;
5415                 break;
5416
5417         case REQ_STAGE_NEXT:
5418                 if (stage_line_type == LINE_STAT_UNTRACKED) {
5419                         report("File is untracked; press %s to add",
5420                                get_key(REQ_STATUS_UPDATE));
5421                         return REQ_NONE;
5422                 }
5423                 stage_next(view, line);
5424                 return REQ_NONE;
5425
5426         case REQ_EDIT:
5427                 if (!stage_status.new.name[0])
5428                         return request;
5429                 if (stage_status.status == 'D') {
5430                         report("File has been deleted.");
5431                         return REQ_NONE;
5432                 }
5433
5434                 open_editor(stage_status.status != '?', stage_status.new.name);
5435                 break;
5436
5437         case REQ_REFRESH:
5438                 /* Reload everything ... */
5439                 break;
5440
5441         case REQ_VIEW_BLAME:
5442                 if (stage_status.new.name[0]) {
5443                         string_copy(opt_file, stage_status.new.name);
5444                         opt_ref[0] = 0;
5445                 }
5446                 return request;
5447
5448         case REQ_ENTER:
5449                 return pager_request(view, request, line);
5450
5451         default:
5452                 return request;
5453         }
5454
5455         VIEW(REQ_VIEW_STATUS)->p_restore = TRUE;
5456         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD | OPEN_NOMAXIMIZE);
5457
5458         /* Check whether the staged entry still exists, and close the
5459          * stage view if it doesn't. */
5460         if (!status_exists(&stage_status, stage_line_type)) {
5461                 status_restore(VIEW(REQ_VIEW_STATUS));
5462                 return REQ_VIEW_CLOSE;
5463         }
5464
5465         if (stage_line_type == LINE_STAT_UNTRACKED) {
5466                 if (!suffixcmp(stage_status.new.name, -1, "/")) {
5467                         report("Cannot display a directory");
5468                         return REQ_NONE;
5469                 }
5470
5471                 if (!prepare_update_file(view, stage_status.new.name)) {
5472                         report("Failed to open file: %s", strerror(errno));
5473                         return REQ_NONE;
5474                 }
5475         }
5476         open_view(view, REQ_VIEW_STAGE, OPEN_REFRESH);
5477
5478         return REQ_NONE;
5479 }
5480
5481 static struct view_ops stage_ops = {
5482         "line",
5483         NULL,
5484         NULL,
5485         pager_read,
5486         pager_draw,
5487         stage_request,
5488         pager_grep,
5489         pager_select,
5490 };
5491
5492
5493 /*
5494  * Revision graph
5495  */
5496
5497 struct commit {
5498         char id[SIZEOF_REV];            /* SHA1 ID. */
5499         char title[128];                /* First line of the commit message. */
5500         char author[75];                /* Author of the commit. */
5501         struct tm time;                 /* Date from the author ident. */
5502         struct ref **refs;              /* Repository references. */
5503         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
5504         size_t graph_size;              /* The width of the graph array. */
5505         bool has_parents;               /* Rewritten --parents seen. */
5506 };
5507
5508 /* Size of rev graph with no  "padding" columns */
5509 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
5510
5511 struct rev_graph {
5512         struct rev_graph *prev, *next, *parents;
5513         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
5514         size_t size;
5515         struct commit *commit;
5516         size_t pos;
5517         unsigned int boundary:1;
5518 };
5519
5520 /* Parents of the commit being visualized. */
5521 static struct rev_graph graph_parents[4];
5522
5523 /* The current stack of revisions on the graph. */
5524 static struct rev_graph graph_stacks[4] = {
5525         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
5526         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
5527         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
5528         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
5529 };
5530
5531 static inline bool
5532 graph_parent_is_merge(struct rev_graph *graph)
5533 {
5534         return graph->parents->size > 1;
5535 }
5536
5537 static inline void
5538 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
5539 {
5540         struct commit *commit = graph->commit;
5541
5542         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
5543                 commit->graph[commit->graph_size++] = symbol;
5544 }
5545
5546 static void
5547 clear_rev_graph(struct rev_graph *graph)
5548 {
5549         graph->boundary = 0;
5550         graph->size = graph->pos = 0;
5551         graph->commit = NULL;
5552         memset(graph->parents, 0, sizeof(*graph->parents));
5553 }
5554
5555 static void
5556 done_rev_graph(struct rev_graph *graph)
5557 {
5558         if (graph_parent_is_merge(graph) &&
5559             graph->pos < graph->size - 1 &&
5560             graph->next->size == graph->size + graph->parents->size - 1) {
5561                 size_t i = graph->pos + graph->parents->size - 1;
5562
5563                 graph->commit->graph_size = i * 2;
5564                 while (i < graph->next->size - 1) {
5565                         append_to_rev_graph(graph, ' ');
5566                         append_to_rev_graph(graph, '\\');
5567                         i++;
5568                 }
5569         }
5570
5571         clear_rev_graph(graph);
5572 }
5573
5574 static void
5575 push_rev_graph(struct rev_graph *graph, const char *parent)
5576 {
5577         int i;
5578
5579         /* "Collapse" duplicate parents lines.
5580          *
5581          * FIXME: This needs to also update update the drawn graph but
5582          * for now it just serves as a method for pruning graph lines. */
5583         for (i = 0; i < graph->size; i++)
5584                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
5585                         return;
5586
5587         if (graph->size < SIZEOF_REVITEMS) {
5588                 string_copy_rev(graph->rev[graph->size++], parent);
5589         }
5590 }
5591
5592 static chtype
5593 get_rev_graph_symbol(struct rev_graph *graph)
5594 {
5595         chtype symbol;
5596
5597         if (graph->boundary)
5598                 symbol = REVGRAPH_BOUND;
5599         else if (graph->parents->size == 0)
5600                 symbol = REVGRAPH_INIT;
5601         else if (graph_parent_is_merge(graph))
5602                 symbol = REVGRAPH_MERGE;
5603         else if (graph->pos >= graph->size)
5604                 symbol = REVGRAPH_BRANCH;
5605         else
5606                 symbol = REVGRAPH_COMMIT;
5607
5608         return symbol;
5609 }
5610
5611 static void
5612 draw_rev_graph(struct rev_graph *graph)
5613 {
5614         struct rev_filler {
5615                 chtype separator, line;
5616         };
5617         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
5618         static struct rev_filler fillers[] = {
5619                 { ' ',  '|' },
5620                 { '`',  '.' },
5621                 { '\'', ' ' },
5622                 { '/',  ' ' },
5623         };
5624         chtype symbol = get_rev_graph_symbol(graph);
5625         struct rev_filler *filler;
5626         size_t i;
5627
5628         if (opt_line_graphics)
5629                 fillers[DEFAULT].line = line_graphics[LINE_GRAPHIC_VLINE];
5630
5631         filler = &fillers[DEFAULT];
5632
5633         for (i = 0; i < graph->pos; i++) {
5634                 append_to_rev_graph(graph, filler->line);
5635                 if (graph_parent_is_merge(graph->prev) &&
5636                     graph->prev->pos == i)
5637                         filler = &fillers[RSHARP];
5638
5639                 append_to_rev_graph(graph, filler->separator);
5640         }
5641
5642         /* Place the symbol for this revision. */
5643         append_to_rev_graph(graph, symbol);
5644
5645         if (graph->prev->size > graph->size)
5646                 filler = &fillers[RDIAG];
5647         else
5648                 filler = &fillers[DEFAULT];
5649
5650         i++;
5651
5652         for (; i < graph->size; i++) {
5653                 append_to_rev_graph(graph, filler->separator);
5654                 append_to_rev_graph(graph, filler->line);
5655                 if (graph_parent_is_merge(graph->prev) &&
5656                     i < graph->prev->pos + graph->parents->size)
5657                         filler = &fillers[RSHARP];
5658                 if (graph->prev->size > graph->size)
5659                         filler = &fillers[LDIAG];
5660         }
5661
5662         if (graph->prev->size > graph->size) {
5663                 append_to_rev_graph(graph, filler->separator);
5664                 if (filler->line != ' ')
5665                         append_to_rev_graph(graph, filler->line);
5666         }
5667 }
5668
5669 /* Prepare the next rev graph */
5670 static void
5671 prepare_rev_graph(struct rev_graph *graph)
5672 {
5673         size_t i;
5674
5675         /* First, traverse all lines of revisions up to the active one. */
5676         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
5677                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
5678                         break;
5679
5680                 push_rev_graph(graph->next, graph->rev[graph->pos]);
5681         }
5682
5683         /* Interleave the new revision parent(s). */
5684         for (i = 0; !graph->boundary && i < graph->parents->size; i++)
5685                 push_rev_graph(graph->next, graph->parents->rev[i]);
5686
5687         /* Lastly, put any remaining revisions. */
5688         for (i = graph->pos + 1; i < graph->size; i++)
5689                 push_rev_graph(graph->next, graph->rev[i]);
5690 }
5691
5692 static void
5693 update_rev_graph(struct view *view, struct rev_graph *graph)
5694 {
5695         /* If this is the finalizing update ... */
5696         if (graph->commit)
5697                 prepare_rev_graph(graph);
5698
5699         /* Graph visualization needs a one rev look-ahead,
5700          * so the first update doesn't visualize anything. */
5701         if (!graph->prev->commit)
5702                 return;
5703
5704         if (view->lines > 2)
5705                 view->line[view->lines - 3].dirty = 1;
5706         if (view->lines > 1)
5707                 view->line[view->lines - 2].dirty = 1;
5708         draw_rev_graph(graph->prev);
5709         done_rev_graph(graph->prev->prev);
5710 }
5711
5712
5713 /*
5714  * Main view backend
5715  */
5716
5717 static const char *main_argv[SIZEOF_ARG] = {
5718         "git", "log", "--no-color", "--pretty=raw", "--parents",
5719                       "--topo-order", "%(head)", NULL
5720 };
5721
5722 static bool
5723 main_draw(struct view *view, struct line *line, unsigned int lineno)
5724 {
5725         struct commit *commit = line->data;
5726
5727         if (!*commit->author)
5728                 return FALSE;
5729
5730         if (opt_date && draw_date(view, &commit->time))
5731                 return TRUE;
5732
5733         if (opt_author && draw_author(view, commit->author))
5734                 return TRUE;
5735
5736         if (opt_rev_graph && commit->graph_size &&
5737             draw_graphic(view, LINE_MAIN_REVGRAPH, commit->graph, commit->graph_size))
5738                 return TRUE;
5739
5740         if (opt_show_refs && commit->refs) {
5741                 size_t i = 0;
5742
5743                 do {
5744                         enum line_type type;
5745
5746                         if (commit->refs[i]->head)
5747                                 type = LINE_MAIN_HEAD;
5748                         else if (commit->refs[i]->ltag)
5749                                 type = LINE_MAIN_LOCAL_TAG;
5750                         else if (commit->refs[i]->tag)
5751                                 type = LINE_MAIN_TAG;
5752                         else if (commit->refs[i]->tracked)
5753                                 type = LINE_MAIN_TRACKED;
5754                         else if (commit->refs[i]->remote)
5755                                 type = LINE_MAIN_REMOTE;
5756                         else
5757                                 type = LINE_MAIN_REF;
5758
5759                         if (draw_text(view, type, "[", TRUE) ||
5760                             draw_text(view, type, commit->refs[i]->name, TRUE) ||
5761                             draw_text(view, type, "]", TRUE))
5762                                 return TRUE;
5763
5764                         if (draw_text(view, LINE_DEFAULT, " ", TRUE))
5765                                 return TRUE;
5766                 } while (commit->refs[i++]->next);
5767         }
5768
5769         draw_text(view, LINE_DEFAULT, commit->title, TRUE);
5770         return TRUE;
5771 }
5772
5773 /* Reads git log --pretty=raw output and parses it into the commit struct. */
5774 static bool
5775 main_read(struct view *view, char *line)
5776 {
5777         static struct rev_graph *graph = graph_stacks;
5778         enum line_type type;
5779         struct commit *commit;
5780
5781         if (!line) {
5782                 int i;
5783
5784                 if (!view->lines && !view->parent)
5785                         die("No revisions match the given arguments.");
5786                 if (view->lines > 0) {
5787                         commit = view->line[view->lines - 1].data;
5788                         view->line[view->lines - 1].dirty = 1;
5789                         if (!*commit->author) {
5790                                 view->lines--;
5791                                 free(commit);
5792                                 graph->commit = NULL;
5793                         }
5794                 }
5795                 update_rev_graph(view, graph);
5796
5797                 for (i = 0; i < ARRAY_SIZE(graph_stacks); i++)
5798                         clear_rev_graph(&graph_stacks[i]);
5799                 return TRUE;
5800         }
5801
5802         type = get_line_type(line);
5803         if (type == LINE_COMMIT) {
5804                 commit = calloc(1, sizeof(struct commit));
5805                 if (!commit)
5806                         return FALSE;
5807
5808                 line += STRING_SIZE("commit ");
5809                 if (*line == '-') {
5810                         graph->boundary = 1;
5811                         line++;
5812                 }
5813
5814                 string_copy_rev(commit->id, line);
5815                 commit->refs = get_refs(commit->id);
5816                 graph->commit = commit;
5817                 add_line_data(view, commit, LINE_MAIN_COMMIT);
5818
5819                 while ((line = strchr(line, ' '))) {
5820                         line++;
5821                         push_rev_graph(graph->parents, line);
5822                         commit->has_parents = TRUE;
5823                 }
5824                 return TRUE;
5825         }
5826
5827         if (!view->lines)
5828                 return TRUE;
5829         commit = view->line[view->lines - 1].data;
5830
5831         switch (type) {
5832         case LINE_PARENT:
5833                 if (commit->has_parents)
5834                         break;
5835                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
5836                 break;
5837
5838         case LINE_AUTHOR:
5839                 parse_author_line(line + STRING_SIZE("author "),
5840                                   commit->author, sizeof(commit->author),
5841                                   &commit->time);
5842                 update_rev_graph(view, graph);
5843                 graph = graph->next;
5844                 break;
5845
5846         default:
5847                 /* Fill in the commit title if it has not already been set. */
5848                 if (commit->title[0])
5849                         break;
5850
5851                 /* Require titles to start with a non-space character at the
5852                  * offset used by git log. */
5853                 if (strncmp(line, "    ", 4))
5854                         break;
5855                 line += 4;
5856                 /* Well, if the title starts with a whitespace character,
5857                  * try to be forgiving.  Otherwise we end up with no title. */
5858                 while (isspace(*line))
5859                         line++;
5860                 if (*line == '\0')
5861                         break;
5862                 /* FIXME: More graceful handling of titles; append "..." to
5863                  * shortened titles, etc. */
5864
5865                 string_expand(commit->title, sizeof(commit->title), line, 1);
5866                 view->line[view->lines - 1].dirty = 1;
5867         }
5868
5869         return TRUE;
5870 }
5871
5872 static enum request
5873 main_request(struct view *view, enum request request, struct line *line)
5874 {
5875         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
5876
5877         switch (request) {
5878         case REQ_ENTER:
5879                 open_view(view, REQ_VIEW_DIFF, flags);
5880                 break;
5881         case REQ_REFRESH:
5882                 load_refs();
5883                 open_view(view, REQ_VIEW_MAIN, OPEN_REFRESH);
5884                 break;
5885         default:
5886                 return request;
5887         }
5888
5889         return REQ_NONE;
5890 }
5891
5892 static bool
5893 grep_refs(struct ref **refs, regex_t *regex)
5894 {
5895         regmatch_t pmatch;
5896         size_t i = 0;
5897
5898         if (!refs)
5899                 return FALSE;
5900         do {
5901                 if (regexec(regex, refs[i]->name, 1, &pmatch, 0) != REG_NOMATCH)
5902                         return TRUE;
5903         } while (refs[i++]->next);
5904
5905         return FALSE;
5906 }
5907
5908 static bool
5909 main_grep(struct view *view, struct line *line)
5910 {
5911         struct commit *commit = line->data;
5912         enum { S_TITLE, S_AUTHOR, S_DATE, S_REFS, S_END } state;
5913         char buf[DATE_COLS + 1];
5914         regmatch_t pmatch;
5915
5916         for (state = S_TITLE; state < S_END; state++) {
5917                 char *text;
5918
5919                 switch (state) {
5920                 case S_TITLE:   text = commit->title;   break;
5921                 case S_AUTHOR:
5922                         if (!opt_author)
5923                                 continue;
5924                         text = commit->author;
5925                         break;
5926                 case S_DATE:
5927                         if (!opt_date)
5928                                 continue;
5929                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
5930                                 continue;
5931                         text = buf;
5932                         break;
5933                 case S_REFS:
5934                         if (!opt_show_refs)
5935                                 continue;
5936                         if (grep_refs(commit->refs, view->regex) == TRUE)
5937                                 return TRUE;
5938                         continue;
5939                 default:
5940                         return FALSE;
5941                 }
5942
5943                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5944                         return TRUE;
5945         }
5946
5947         return FALSE;
5948 }
5949
5950 static void
5951 main_select(struct view *view, struct line *line)
5952 {
5953         struct commit *commit = line->data;
5954
5955         string_copy_rev(view->ref, commit->id);
5956         string_copy_rev(ref_commit, view->ref);
5957 }
5958
5959 static struct view_ops main_ops = {
5960         "commit",
5961         main_argv,
5962         NULL,
5963         main_read,
5964         main_draw,
5965         main_request,
5966         main_grep,
5967         main_select,
5968 };
5969
5970
5971 /*
5972  * Unicode / UTF-8 handling
5973  *
5974  * NOTE: Much of the following code for dealing with Unicode is derived from
5975  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
5976  * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
5977  */
5978
5979 static inline int
5980 unicode_width(unsigned long c)
5981 {
5982         if (c >= 0x1100 &&
5983            (c <= 0x115f                         /* Hangul Jamo */
5984             || c == 0x2329
5985             || c == 0x232a
5986             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
5987                                                 /* CJK ... Yi */
5988             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
5989             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
5990             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
5991             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
5992             || (c >= 0xffe0  && c <= 0xffe6)
5993             || (c >= 0x20000 && c <= 0x2fffd)
5994             || (c >= 0x30000 && c <= 0x3fffd)))
5995                 return 2;
5996
5997         if (c == '\t')
5998                 return opt_tab_size;
5999
6000         return 1;
6001 }
6002
6003 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
6004  * Illegal bytes are set one. */
6005 static const unsigned char utf8_bytes[256] = {
6006         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,
6007         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,
6008         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,
6009         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,
6010         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,
6011         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,
6012         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,
6013         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,
6014 };
6015
6016 /* Decode UTF-8 multi-byte representation into a Unicode character. */
6017 static inline unsigned long
6018 utf8_to_unicode(const char *string, size_t length)
6019 {
6020         unsigned long unicode;
6021
6022         switch (length) {
6023         case 1:
6024                 unicode  =   string[0];
6025                 break;
6026         case 2:
6027                 unicode  =  (string[0] & 0x1f) << 6;
6028                 unicode +=  (string[1] & 0x3f);
6029                 break;
6030         case 3:
6031                 unicode  =  (string[0] & 0x0f) << 12;
6032                 unicode += ((string[1] & 0x3f) << 6);
6033                 unicode +=  (string[2] & 0x3f);
6034                 break;
6035         case 4:
6036                 unicode  =  (string[0] & 0x0f) << 18;
6037                 unicode += ((string[1] & 0x3f) << 12);
6038                 unicode += ((string[2] & 0x3f) << 6);
6039                 unicode +=  (string[3] & 0x3f);
6040                 break;
6041         case 5:
6042                 unicode  =  (string[0] & 0x0f) << 24;
6043                 unicode += ((string[1] & 0x3f) << 18);
6044                 unicode += ((string[2] & 0x3f) << 12);
6045                 unicode += ((string[3] & 0x3f) << 6);
6046                 unicode +=  (string[4] & 0x3f);
6047                 break;
6048         case 6:
6049                 unicode  =  (string[0] & 0x01) << 30;
6050                 unicode += ((string[1] & 0x3f) << 24);
6051                 unicode += ((string[2] & 0x3f) << 18);
6052                 unicode += ((string[3] & 0x3f) << 12);
6053                 unicode += ((string[4] & 0x3f) << 6);
6054                 unicode +=  (string[5] & 0x3f);
6055                 break;
6056         default:
6057                 die("Invalid Unicode length");
6058         }
6059
6060         /* Invalid characters could return the special 0xfffd value but NUL
6061          * should be just as good. */
6062         return unicode > 0xffff ? 0 : unicode;
6063 }
6064
6065 /* Calculates how much of string can be shown within the given maximum width
6066  * and sets trimmed parameter to non-zero value if all of string could not be
6067  * shown. If the reserve flag is TRUE, it will reserve at least one
6068  * trailing character, which can be useful when drawing a delimiter.
6069  *
6070  * Returns the number of bytes to output from string to satisfy max_width. */
6071 static size_t
6072 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve)
6073 {
6074         const char *string = *start;
6075         const char *end = strchr(string, '\0');
6076         unsigned char last_bytes = 0;
6077         size_t last_ucwidth = 0;
6078
6079         *width = 0;
6080         *trimmed = 0;
6081
6082         while (string < end) {
6083                 int c = *(unsigned char *) string;
6084                 unsigned char bytes = utf8_bytes[c];
6085                 size_t ucwidth;
6086                 unsigned long unicode;
6087
6088                 if (string + bytes > end)
6089                         break;
6090
6091                 /* Change representation to figure out whether
6092                  * it is a single- or double-width character. */
6093
6094                 unicode = utf8_to_unicode(string, bytes);
6095                 /* FIXME: Graceful handling of invalid Unicode character. */
6096                 if (!unicode)
6097                         break;
6098
6099                 ucwidth = unicode_width(unicode);
6100                 if (skip > 0) {
6101                         skip -= ucwidth <= skip ? ucwidth : skip;
6102                         *start += bytes;
6103                 }
6104                 *width  += ucwidth;
6105                 if (*width > max_width) {
6106                         *trimmed = 1;
6107                         *width -= ucwidth;
6108                         if (reserve && *width == max_width) {
6109                                 string -= last_bytes;
6110                                 *width -= last_ucwidth;
6111                         }
6112                         break;
6113                 }
6114
6115                 string  += bytes;
6116                 last_bytes = ucwidth ? bytes : 0;
6117                 last_ucwidth = ucwidth;
6118         }
6119
6120         return string - *start;
6121 }
6122
6123
6124 /*
6125  * Status management
6126  */
6127
6128 /* Whether or not the curses interface has been initialized. */
6129 static bool cursed = FALSE;
6130
6131 /* Terminal hacks and workarounds. */
6132 static bool use_scroll_redrawwin;
6133 static bool use_scroll_status_wclear;
6134
6135 /* The status window is used for polling keystrokes. */
6136 static WINDOW *status_win;
6137
6138 /* Reading from the prompt? */
6139 static bool input_mode = FALSE;
6140
6141 static bool status_empty = FALSE;
6142
6143 /* Update status and title window. */
6144 static void
6145 report(const char *msg, ...)
6146 {
6147         struct view *view = display[current_view];
6148
6149         if (input_mode)
6150                 return;
6151
6152         if (!view) {
6153                 char buf[SIZEOF_STR];
6154                 va_list args;
6155
6156                 va_start(args, msg);
6157                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
6158                         buf[sizeof(buf) - 1] = 0;
6159                         buf[sizeof(buf) - 2] = '.';
6160                         buf[sizeof(buf) - 3] = '.';
6161                         buf[sizeof(buf) - 4] = '.';
6162                 }
6163                 va_end(args);
6164                 die("%s", buf);
6165         }
6166
6167         if (!status_empty || *msg) {
6168                 va_list args;
6169
6170                 va_start(args, msg);
6171
6172                 wmove(status_win, 0, 0);
6173                 if (view->has_scrolled && use_scroll_status_wclear)
6174                         wclear(status_win);
6175                 if (*msg) {
6176                         vwprintw(status_win, msg, args);
6177                         status_empty = FALSE;
6178                 } else {
6179                         status_empty = TRUE;
6180                 }
6181                 wclrtoeol(status_win);
6182                 wnoutrefresh(status_win);
6183
6184                 va_end(args);
6185         }
6186
6187         update_view_title(view);
6188 }
6189
6190 /* Controls when nodelay should be in effect when polling user input. */
6191 static void
6192 set_nonblocking_input(bool loading)
6193 {
6194         static unsigned int loading_views;
6195
6196         if ((loading == FALSE && loading_views-- == 1) ||
6197             (loading == TRUE  && loading_views++ == 0))
6198                 nodelay(status_win, loading);
6199 }
6200
6201 static void
6202 init_display(void)
6203 {
6204         const char *term;
6205         int x, y;
6206
6207         /* Initialize the curses library */
6208         if (isatty(STDIN_FILENO)) {
6209                 cursed = !!initscr();
6210                 opt_tty = stdin;
6211         } else {
6212                 /* Leave stdin and stdout alone when acting as a pager. */
6213                 opt_tty = fopen("/dev/tty", "r+");
6214                 if (!opt_tty)
6215                         die("Failed to open /dev/tty");
6216                 cursed = !!newterm(NULL, opt_tty, opt_tty);
6217         }
6218
6219         if (!cursed)
6220                 die("Failed to initialize curses");
6221
6222         nonl();         /* Disable conversion and detect newlines from input. */
6223         cbreak();       /* Take input chars one at a time, no wait for \n */
6224         noecho();       /* Don't echo input */
6225         leaveok(stdscr, FALSE);
6226
6227         if (has_colors())
6228                 init_colors();
6229
6230         getmaxyx(stdscr, y, x);
6231         status_win = newwin(1, 0, y - 1, 0);
6232         if (!status_win)
6233                 die("Failed to create status window");
6234
6235         /* Enable keyboard mapping */
6236         keypad(status_win, TRUE);
6237         wbkgdset(status_win, get_line_attr(LINE_STATUS));
6238
6239         TABSIZE = opt_tab_size;
6240         if (opt_line_graphics) {
6241                 line_graphics[LINE_GRAPHIC_VLINE] = ACS_VLINE;
6242         }
6243
6244         term = getenv("XTERM_VERSION") ? NULL : getenv("COLORTERM");
6245         if (term && !strcmp(term, "gnome-terminal")) {
6246                 /* In the gnome-terminal-emulator, the message from
6247                  * scrolling up one line when impossible followed by
6248                  * scrolling down one line causes corruption of the
6249                  * status line. This is fixed by calling wclear. */
6250                 use_scroll_status_wclear = TRUE;
6251                 use_scroll_redrawwin = FALSE;
6252
6253         } else if (term && !strcmp(term, "xrvt-xpm")) {
6254                 /* No problems with full optimizations in xrvt-(unicode)
6255                  * and aterm. */
6256                 use_scroll_status_wclear = use_scroll_redrawwin = FALSE;
6257
6258         } else {
6259                 /* When scrolling in (u)xterm the last line in the
6260                  * scrolling direction will update slowly. */
6261                 use_scroll_redrawwin = TRUE;
6262                 use_scroll_status_wclear = FALSE;
6263         }
6264 }
6265
6266 static int
6267 get_input(int prompt_position)
6268 {
6269         struct view *view;
6270         int i, key, cursor_y, cursor_x;
6271
6272         if (prompt_position)
6273                 input_mode = TRUE;
6274
6275         while (TRUE) {
6276                 foreach_view (view, i) {
6277                         update_view(view);
6278                         if (view_is_displayed(view) && view->has_scrolled &&
6279                             use_scroll_redrawwin)
6280                                 redrawwin(view->win);
6281                         view->has_scrolled = FALSE;
6282                 }
6283
6284                 /* Update the cursor position. */
6285                 if (prompt_position) {
6286                         getbegyx(status_win, cursor_y, cursor_x);
6287                         cursor_x = prompt_position;
6288                 } else {
6289                         view = display[current_view];
6290                         getbegyx(view->win, cursor_y, cursor_x);
6291                         cursor_x = view->width - 1;
6292                         cursor_y += view->lineno - view->offset;
6293                 }
6294                 setsyx(cursor_y, cursor_x);
6295
6296                 /* Refresh, accept single keystroke of input */
6297                 doupdate();
6298                 key = wgetch(status_win);
6299
6300                 /* wgetch() with nodelay() enabled returns ERR when
6301                  * there's no input. */
6302                 if (key == ERR) {
6303
6304                 } else if (key == KEY_RESIZE) {
6305                         int height, width;
6306
6307                         getmaxyx(stdscr, height, width);
6308
6309                         wresize(status_win, 1, width);
6310                         mvwin(status_win, height - 1, 0);
6311                         wnoutrefresh(status_win);
6312                         resize_display();
6313                         redraw_display(TRUE);
6314
6315                 } else {
6316                         input_mode = FALSE;
6317                         return key;
6318                 }
6319         }
6320 }
6321
6322 static char *
6323 prompt_input(const char *prompt, input_handler handler, void *data)
6324 {
6325         enum input_status status = INPUT_OK;
6326         static char buf[SIZEOF_STR];
6327         size_t pos = 0;
6328
6329         buf[pos] = 0;
6330
6331         while (status == INPUT_OK || status == INPUT_SKIP) {
6332                 int key;
6333
6334                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
6335                 wclrtoeol(status_win);
6336
6337                 key = get_input(pos + 1);
6338                 switch (key) {
6339                 case KEY_RETURN:
6340                 case KEY_ENTER:
6341                 case '\n':
6342                         status = pos ? INPUT_STOP : INPUT_CANCEL;
6343                         break;
6344
6345                 case KEY_BACKSPACE:
6346                         if (pos > 0)
6347                                 buf[--pos] = 0;
6348                         else
6349                                 status = INPUT_CANCEL;
6350                         break;
6351
6352                 case KEY_ESC:
6353                         status = INPUT_CANCEL;
6354                         break;
6355
6356                 default:
6357                         if (pos >= sizeof(buf)) {
6358                                 report("Input string too long");
6359                                 return NULL;
6360                         }
6361
6362                         status = handler(data, buf, key);
6363                         if (status == INPUT_OK)
6364                                 buf[pos++] = (char) key;
6365                 }
6366         }
6367
6368         /* Clear the status window */
6369         status_empty = FALSE;
6370         report("");
6371
6372         if (status == INPUT_CANCEL)
6373                 return NULL;
6374
6375         buf[pos++] = 0;
6376
6377         return buf;
6378 }
6379
6380 static enum input_status
6381 prompt_yesno_handler(void *data, char *buf, int c)
6382 {
6383         if (c == 'y' || c == 'Y')
6384                 return INPUT_STOP;
6385         if (c == 'n' || c == 'N')
6386                 return INPUT_CANCEL;
6387         return INPUT_SKIP;
6388 }
6389
6390 static bool
6391 prompt_yesno(const char *prompt)
6392 {
6393         char prompt2[SIZEOF_STR];
6394
6395         if (!string_format(prompt2, "%s [Yy/Nn]", prompt))
6396                 return FALSE;
6397
6398         return !!prompt_input(prompt2, prompt_yesno_handler, NULL);
6399 }
6400
6401 static enum input_status
6402 read_prompt_handler(void *data, char *buf, int c)
6403 {
6404         return isprint(c) ? INPUT_OK : INPUT_SKIP;
6405 }
6406
6407 static char *
6408 read_prompt(const char *prompt)
6409 {
6410         return prompt_input(prompt, read_prompt_handler, NULL);
6411 }
6412
6413 /*
6414  * Repository properties
6415  */
6416
6417 static struct ref *refs = NULL;
6418 static size_t refs_alloc = 0;
6419 static size_t refs_size = 0;
6420
6421 /* Id <-> ref store */
6422 static struct ref ***id_refs = NULL;
6423 static size_t id_refs_alloc = 0;
6424 static size_t id_refs_size = 0;
6425
6426 static int
6427 compare_refs(const void *ref1_, const void *ref2_)
6428 {
6429         const struct ref *ref1 = *(const struct ref **)ref1_;
6430         const struct ref *ref2 = *(const struct ref **)ref2_;
6431
6432         if (ref1->tag != ref2->tag)
6433                 return ref2->tag - ref1->tag;
6434         if (ref1->ltag != ref2->ltag)
6435                 return ref2->ltag - ref2->ltag;
6436         if (ref1->head != ref2->head)
6437                 return ref2->head - ref1->head;
6438         if (ref1->tracked != ref2->tracked)
6439                 return ref2->tracked - ref1->tracked;
6440         if (ref1->remote != ref2->remote)
6441                 return ref2->remote - ref1->remote;
6442         return strcmp(ref1->name, ref2->name);
6443 }
6444
6445 static struct ref **
6446 get_refs(const char *id)
6447 {
6448         struct ref ***tmp_id_refs;
6449         struct ref **ref_list = NULL;
6450         size_t ref_list_alloc = 0;
6451         size_t ref_list_size = 0;
6452         size_t i;
6453
6454         for (i = 0; i < id_refs_size; i++)
6455                 if (!strcmp(id, id_refs[i][0]->id))
6456                         return id_refs[i];
6457
6458         tmp_id_refs = realloc_items(id_refs, &id_refs_alloc, id_refs_size + 1,
6459                                     sizeof(*id_refs));
6460         if (!tmp_id_refs)
6461                 return NULL;
6462
6463         id_refs = tmp_id_refs;
6464
6465         for (i = 0; i < refs_size; i++) {
6466                 struct ref **tmp;
6467
6468                 if (strcmp(id, refs[i].id))
6469                         continue;
6470
6471                 tmp = realloc_items(ref_list, &ref_list_alloc,
6472                                     ref_list_size + 1, sizeof(*ref_list));
6473                 if (!tmp) {
6474                         if (ref_list)
6475                                 free(ref_list);
6476                         return NULL;
6477                 }
6478
6479                 ref_list = tmp;
6480                 ref_list[ref_list_size] = &refs[i];
6481                 /* XXX: The properties of the commit chains ensures that we can
6482                  * safely modify the shared ref. The repo references will
6483                  * always be similar for the same id. */
6484                 ref_list[ref_list_size]->next = 1;
6485
6486                 ref_list_size++;
6487         }
6488
6489         if (ref_list) {
6490                 qsort(ref_list, ref_list_size, sizeof(*ref_list), compare_refs);
6491                 ref_list[ref_list_size - 1]->next = 0;
6492                 id_refs[id_refs_size++] = ref_list;
6493         }
6494
6495         return ref_list;
6496 }
6497
6498 static int
6499 read_ref(char *id, size_t idlen, char *name, size_t namelen)
6500 {
6501         struct ref *ref;
6502         bool tag = FALSE;
6503         bool ltag = FALSE;
6504         bool remote = FALSE;
6505         bool tracked = FALSE;
6506         bool check_replace = FALSE;
6507         bool head = FALSE;
6508
6509         if (!prefixcmp(name, "refs/tags/")) {
6510                 if (!suffixcmp(name, namelen, "^{}")) {
6511                         namelen -= 3;
6512                         name[namelen] = 0;
6513                         if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
6514                                 check_replace = TRUE;
6515                 } else {
6516                         ltag = TRUE;
6517                 }
6518
6519                 tag = TRUE;
6520                 namelen -= STRING_SIZE("refs/tags/");
6521                 name    += STRING_SIZE("refs/tags/");
6522
6523         } else if (!prefixcmp(name, "refs/remotes/")) {
6524                 remote = TRUE;
6525                 namelen -= STRING_SIZE("refs/remotes/");
6526                 name    += STRING_SIZE("refs/remotes/");
6527                 tracked  = !strcmp(opt_remote, name);
6528
6529         } else if (!prefixcmp(name, "refs/heads/")) {
6530                 namelen -= STRING_SIZE("refs/heads/");
6531                 name    += STRING_SIZE("refs/heads/");
6532                 head     = !strncmp(opt_head, name, namelen);
6533
6534         } else if (!strcmp(name, "HEAD")) {
6535                 string_ncopy(opt_head_rev, id, idlen);
6536                 return OK;
6537         }
6538
6539         if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
6540                 /* it's an annotated tag, replace the previous SHA1 with the
6541                  * resolved commit id; relies on the fact git-ls-remote lists
6542                  * the commit id of an annotated tag right before the commit id
6543                  * it points to. */
6544                 refs[refs_size - 1].ltag = ltag;
6545                 string_copy_rev(refs[refs_size - 1].id, id);
6546
6547                 return OK;
6548         }
6549         refs = realloc_items(refs, &refs_alloc, refs_size + 1, sizeof(*refs));
6550         if (!refs)
6551                 return ERR;
6552
6553         ref = &refs[refs_size++];
6554         ref->name = malloc(namelen + 1);
6555         if (!ref->name)
6556                 return ERR;
6557
6558         strncpy(ref->name, name, namelen);
6559         ref->name[namelen] = 0;
6560         ref->head = head;
6561         ref->tag = tag;
6562         ref->ltag = ltag;
6563         ref->remote = remote;
6564         ref->tracked = tracked;
6565         string_copy_rev(ref->id, id);
6566
6567         return OK;
6568 }
6569
6570 static int
6571 load_refs(void)
6572 {
6573         static const char *ls_remote_argv[SIZEOF_ARG] = {
6574                 "git", "ls-remote", ".", NULL
6575         };
6576         static bool init = FALSE;
6577
6578         if (!init) {
6579                 argv_from_env(ls_remote_argv, "TIG_LS_REMOTE");
6580                 init = TRUE;
6581         }
6582
6583         if (!*opt_git_dir)
6584                 return OK;
6585
6586         while (refs_size > 0)
6587                 free(refs[--refs_size].name);
6588         while (id_refs_size > 0)
6589                 free(id_refs[--id_refs_size]);
6590
6591         return run_io_load(ls_remote_argv, "\t", read_ref);
6592 }
6593
6594 static void
6595 set_repo_config_option(char *name, char *value, int (*cmd)(int, const char **))
6596 {
6597         const char *argv[SIZEOF_ARG] = { name, "=" };
6598         int argc = 1 + (cmd == option_set_command);
6599         int error = ERR;
6600
6601         if (!argv_from_string(argv, &argc, value))
6602                 config_msg = "Too many option arguments";
6603         else
6604                 error = cmd(argc, argv);
6605
6606         if (error == ERR)
6607                 warn("Option 'tig.%s': %s", name, config_msg);
6608 }
6609
6610 static int
6611 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
6612 {
6613         if (!strcmp(name, "i18n.commitencoding"))
6614                 string_ncopy(opt_encoding, value, valuelen);
6615
6616         if (!strcmp(name, "core.editor"))
6617                 string_ncopy(opt_editor, value, valuelen);
6618
6619         if (!prefixcmp(name, "tig.color."))
6620                 set_repo_config_option(name + 10, value, option_color_command);
6621
6622         else if (!prefixcmp(name, "tig.bind."))
6623                 set_repo_config_option(name + 9, value, option_bind_command);
6624
6625         else if (!prefixcmp(name, "tig."))
6626                 set_repo_config_option(name + 4, value, option_set_command);
6627
6628         /* branch.<head>.remote */
6629         if (*opt_head &&
6630             !strncmp(name, "branch.", 7) &&
6631             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
6632             !strcmp(name + 7 + strlen(opt_head), ".remote"))
6633                 string_ncopy(opt_remote, value, valuelen);
6634
6635         if (*opt_head && *opt_remote &&
6636             !strncmp(name, "branch.", 7) &&
6637             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
6638             !strcmp(name + 7 + strlen(opt_head), ".merge")) {
6639                 size_t from = strlen(opt_remote);
6640
6641                 if (!prefixcmp(value, "refs/heads/")) {
6642                         value += STRING_SIZE("refs/heads/");
6643                         valuelen -= STRING_SIZE("refs/heads/");
6644                 }
6645
6646                 if (!string_format_from(opt_remote, &from, "/%s", value))
6647                         opt_remote[0] = 0;
6648         }
6649
6650         return OK;
6651 }
6652
6653 static int
6654 load_git_config(void)
6655 {
6656         const char *config_list_argv[] = { "git", GIT_CONFIG, "--list", NULL };
6657
6658         return run_io_load(config_list_argv, "=", read_repo_config_option);
6659 }
6660
6661 static int
6662 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
6663 {
6664         if (!opt_git_dir[0]) {
6665                 string_ncopy(opt_git_dir, name, namelen);
6666
6667         } else if (opt_is_inside_work_tree == -1) {
6668                 /* This can be 3 different values depending on the
6669                  * version of git being used. If git-rev-parse does not
6670                  * understand --is-inside-work-tree it will simply echo
6671                  * the option else either "true" or "false" is printed.
6672                  * Default to true for the unknown case. */
6673                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
6674
6675         } else if (*name == '.') {
6676                 string_ncopy(opt_cdup, name, namelen);
6677
6678         } else {
6679                 string_ncopy(opt_prefix, name, namelen);
6680         }
6681
6682         return OK;
6683 }
6684
6685 static int
6686 load_repo_info(void)
6687 {
6688         const char *head_argv[] = {
6689                 "git", "symbolic-ref", "HEAD", NULL
6690         };
6691         const char *rev_parse_argv[] = {
6692                 "git", "rev-parse", "--git-dir", "--is-inside-work-tree",
6693                         "--show-cdup", "--show-prefix", NULL
6694         };
6695
6696         if (run_io_buf(head_argv, opt_head, sizeof(opt_head))) {
6697                 chomp_string(opt_head);
6698                 if (!prefixcmp(opt_head, "refs/heads/")) {
6699                         char *offset = opt_head + STRING_SIZE("refs/heads/");
6700
6701                         memmove(opt_head, offset, strlen(offset) + 1);
6702                 }
6703         }
6704
6705         return run_io_load(rev_parse_argv, "=", read_repo_info);
6706 }
6707
6708
6709 /*
6710  * Main
6711  */
6712
6713 static const char usage[] =
6714 "tig " TIG_VERSION " (" __DATE__ ")\n"
6715 "\n"
6716 "Usage: tig        [options] [revs] [--] [paths]\n"
6717 "   or: tig show   [options] [revs] [--] [paths]\n"
6718 "   or: tig blame  [rev] path\n"
6719 "   or: tig status\n"
6720 "   or: tig <      [git command output]\n"
6721 "\n"
6722 "Options:\n"
6723 "  -v, --version   Show version and exit\n"
6724 "  -h, --help      Show help message and exit";
6725
6726 static void __NORETURN
6727 quit(int sig)
6728 {
6729         /* XXX: Restore tty modes and let the OS cleanup the rest! */
6730         if (cursed)
6731                 endwin();
6732         exit(0);
6733 }
6734
6735 static void __NORETURN
6736 die(const char *err, ...)
6737 {
6738         va_list args;
6739
6740         endwin();
6741
6742         va_start(args, err);
6743         fputs("tig: ", stderr);
6744         vfprintf(stderr, err, args);
6745         fputs("\n", stderr);
6746         va_end(args);
6747
6748         exit(1);
6749 }
6750
6751 static void
6752 warn(const char *msg, ...)
6753 {
6754         va_list args;
6755
6756         va_start(args, msg);
6757         fputs("tig warning: ", stderr);
6758         vfprintf(stderr, msg, args);
6759         fputs("\n", stderr);
6760         va_end(args);
6761 }
6762
6763 static enum request
6764 parse_options(int argc, const char *argv[])
6765 {
6766         enum request request = REQ_VIEW_MAIN;
6767         const char *subcommand;
6768         bool seen_dashdash = FALSE;
6769         /* XXX: This is vulnerable to the user overriding options
6770          * required for the main view parser. */
6771         const char *custom_argv[SIZEOF_ARG] = {
6772                 "git", "log", "--no-color", "--pretty=raw", "--parents",
6773                         "--topo-order", NULL
6774         };
6775         int i, j = 6;
6776
6777         if (!isatty(STDIN_FILENO)) {
6778                 io_open(&VIEW(REQ_VIEW_PAGER)->io, "");
6779                 return REQ_VIEW_PAGER;
6780         }
6781
6782         if (argc <= 1)
6783                 return REQ_NONE;
6784
6785         subcommand = argv[1];
6786         if (!strcmp(subcommand, "status")) {
6787                 if (argc > 2)
6788                         warn("ignoring arguments after `%s'", subcommand);
6789                 return REQ_VIEW_STATUS;
6790
6791         } else if (!strcmp(subcommand, "blame")) {
6792                 if (argc <= 2 || argc > 4)
6793                         die("invalid number of options to blame\n\n%s", usage);
6794
6795                 i = 2;
6796                 if (argc == 4) {
6797                         string_ncopy(opt_ref, argv[i], strlen(argv[i]));
6798                         i++;
6799                 }
6800
6801                 string_ncopy(opt_file, argv[i], strlen(argv[i]));
6802                 return REQ_VIEW_BLAME;
6803
6804         } else if (!strcmp(subcommand, "show")) {
6805                 request = REQ_VIEW_DIFF;
6806
6807         } else {
6808                 subcommand = NULL;
6809         }
6810
6811         if (subcommand) {
6812                 custom_argv[1] = subcommand;
6813                 j = 2;
6814         }
6815
6816         for (i = 1 + !!subcommand; i < argc; i++) {
6817                 const char *opt = argv[i];
6818
6819                 if (seen_dashdash || !strcmp(opt, "--")) {
6820                         seen_dashdash = TRUE;
6821
6822                 } else if (!strcmp(opt, "-v") || !strcmp(opt, "--version")) {
6823                         printf("tig version %s\n", TIG_VERSION);
6824                         quit(0);
6825
6826                 } else if (!strcmp(opt, "-h") || !strcmp(opt, "--help")) {
6827                         printf("%s\n", usage);
6828                         quit(0);
6829                 }
6830
6831                 custom_argv[j++] = opt;
6832                 if (j >= ARRAY_SIZE(custom_argv))
6833                         die("command too long");
6834         }
6835
6836         if (!prepare_update(VIEW(request), custom_argv, NULL, FORMAT_NONE))                                                                        
6837                 die("Failed to format arguments"); 
6838
6839         return request;
6840 }
6841
6842 int
6843 main(int argc, const char *argv[])
6844 {
6845         enum request request = parse_options(argc, argv);
6846         struct view *view;
6847         size_t i;
6848
6849         signal(SIGINT, quit);
6850
6851         if (setlocale(LC_ALL, "")) {
6852                 char *codeset = nl_langinfo(CODESET);
6853
6854                 string_ncopy(opt_codeset, codeset, strlen(codeset));
6855         }
6856
6857         if (load_repo_info() == ERR)
6858                 die("Failed to load repo info.");
6859
6860         if (load_options() == ERR)
6861                 die("Failed to load user config.");
6862
6863         if (load_git_config() == ERR)
6864                 die("Failed to load repo config.");
6865
6866         /* Require a git repository unless when running in pager mode. */
6867         if (!opt_git_dir[0] && request != REQ_VIEW_PAGER)
6868                 die("Not a git repository");
6869
6870         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
6871                 opt_utf8 = FALSE;
6872
6873         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
6874                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
6875                 if (opt_iconv == ICONV_NONE)
6876                         die("Failed to initialize character set conversion");
6877         }
6878
6879         if (load_refs() == ERR)
6880                 die("Failed to load refs.");
6881
6882         foreach_view (view, i)
6883                 argv_from_env(view->ops->argv, view->cmd_env);
6884
6885         init_display();
6886
6887         if (request != REQ_NONE)
6888                 open_view(NULL, request, OPEN_PREPARED);
6889         request = request == REQ_NONE ? REQ_VIEW_MAIN : REQ_NONE;
6890
6891         while (view_driver(display[current_view], request)) {
6892                 int key = get_input(0);
6893
6894                 view = display[current_view];
6895                 request = get_keybinding(view->keymap, key);
6896
6897                 /* Some low-level request handling. This keeps access to
6898                  * status_win restricted. */
6899                 switch (request) {
6900                 case REQ_PROMPT:
6901                 {
6902                         char *cmd = read_prompt(":");
6903
6904                         if (cmd) {
6905                                 struct view *next = VIEW(REQ_VIEW_PAGER);
6906                                 const char *argv[SIZEOF_ARG] = { "git" };
6907                                 int argc = 1;
6908
6909                                 /* When running random commands, initially show the
6910                                  * command in the title. However, it maybe later be
6911                                  * overwritten if a commit line is selected. */
6912                                 string_ncopy(next->ref, cmd, strlen(cmd));
6913
6914                                 if (!argv_from_string(argv, &argc, cmd)) {
6915                                         report("Too many arguments");
6916                                 } else if (!prepare_update(next, argv, NULL, FORMAT_DASH)) {
6917                                         report("Failed to format command");
6918                                 } else {
6919                                         open_view(view, REQ_VIEW_PAGER, OPEN_PREPARED);
6920                                 }
6921                         }
6922
6923                         request = REQ_NONE;
6924                         break;
6925                 }
6926                 case REQ_SEARCH:
6927                 case REQ_SEARCH_BACK:
6928                 {
6929                         const char *prompt = request == REQ_SEARCH ? "/" : "?";
6930                         char *search = read_prompt(prompt);
6931
6932                         if (search)
6933                                 string_ncopy(opt_search, search, strlen(search));
6934                         else if (*opt_search)
6935                                 request = request == REQ_SEARCH ?
6936                                         REQ_FIND_NEXT :
6937                                         REQ_FIND_PREV;
6938                         else
6939                                 request = REQ_NONE;
6940                         break;
6941                 }
6942                 default:
6943                         break;
6944                 }
6945         }
6946
6947         quit(0);
6948
6949         return 0;
6950 }