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