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