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