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