Handle blaming beyond the creation of file more gracefully
[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(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 bool
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 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 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 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 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                 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 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 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 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 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 /*
2245  * Navigation
2246  */
2247
2248 static bool
2249 goto_view_line(struct view *view, unsigned long offset, unsigned long lineno)
2250 {
2251         if (lineno >= view->lines)
2252                 lineno = view->lines > 0 ? view->lines - 1 : 0;
2253
2254         if (offset > lineno || offset + view->height <= lineno) {
2255                 unsigned long half = view->height / 2;
2256
2257                 if (lineno > half)
2258                         offset = lineno - half;
2259                 else
2260                         offset = 0;
2261         }
2262
2263         if (offset != view->offset || lineno != view->lineno) {
2264                 view->offset = offset;
2265                 view->lineno = lineno;
2266                 return TRUE;
2267         }
2268
2269         return FALSE;
2270 }
2271
2272 /* Scrolling backend */
2273 static void
2274 do_scroll_view(struct view *view, int lines)
2275 {
2276         bool redraw_current_line = FALSE;
2277
2278         /* The rendering expects the new offset. */
2279         view->offset += lines;
2280
2281         assert(0 <= view->offset && view->offset < view->lines);
2282         assert(lines);
2283
2284         /* Move current line into the view. */
2285         if (view->lineno < view->offset) {
2286                 view->lineno = view->offset;
2287                 redraw_current_line = TRUE;
2288         } else if (view->lineno >= view->offset + view->height) {
2289                 view->lineno = view->offset + view->height - 1;
2290                 redraw_current_line = TRUE;
2291         }
2292
2293         assert(view->offset <= view->lineno && view->lineno < view->lines);
2294
2295         /* Redraw the whole screen if scrolling is pointless. */
2296         if (view->height < ABS(lines)) {
2297                 redraw_view(view);
2298
2299         } else {
2300                 int line = lines > 0 ? view->height - lines : 0;
2301                 int end = line + ABS(lines);
2302
2303                 scrollok(view->win, TRUE);
2304                 wscrl(view->win, lines);
2305                 scrollok(view->win, FALSE);
2306
2307                 while (line < end && draw_view_line(view, line))
2308                         line++;
2309
2310                 if (redraw_current_line)
2311                         draw_view_line(view, view->lineno - view->offset);
2312                 wnoutrefresh(view->win);
2313         }
2314
2315         view->has_scrolled = TRUE;
2316         report("");
2317 }
2318
2319 /* Scroll frontend */
2320 static void
2321 scroll_view(struct view *view, enum request request)
2322 {
2323         int lines = 1;
2324
2325         assert(view_is_displayed(view));
2326
2327         switch (request) {
2328         case REQ_SCROLL_LEFT:
2329                 if (view->yoffset == 0) {
2330                         report("Cannot scroll beyond the first column");
2331                         return;
2332                 }
2333                 if (view->yoffset <= SCROLL_INTERVAL)
2334                         view->yoffset = 0;
2335                 else
2336                         view->yoffset -= SCROLL_INTERVAL;
2337                 redraw_view_from(view, 0);
2338                 report("");
2339                 return;
2340         case REQ_SCROLL_RIGHT:
2341                 if (!view->can_hscroll) {
2342                         report("Cannot scroll beyond the last column");
2343                         return;
2344                 }
2345                 view->yoffset += SCROLL_INTERVAL;
2346                 redraw_view(view);
2347                 report("");
2348                 return;
2349         case REQ_SCROLL_PAGE_DOWN:
2350                 lines = view->height;
2351         case REQ_SCROLL_LINE_DOWN:
2352                 if (view->offset + lines > view->lines)
2353                         lines = view->lines - view->offset;
2354
2355                 if (lines == 0 || view->offset + view->height >= view->lines) {
2356                         report("Cannot scroll beyond the last line");
2357                         return;
2358                 }
2359                 break;
2360
2361         case REQ_SCROLL_PAGE_UP:
2362                 lines = view->height;
2363         case REQ_SCROLL_LINE_UP:
2364                 if (lines > view->offset)
2365                         lines = view->offset;
2366
2367                 if (lines == 0) {
2368                         report("Cannot scroll beyond the first line");
2369                         return;
2370                 }
2371
2372                 lines = -lines;
2373                 break;
2374
2375         default:
2376                 die("request %d not handled in switch", request);
2377         }
2378
2379         do_scroll_view(view, lines);
2380 }
2381
2382 /* Cursor moving */
2383 static void
2384 move_view(struct view *view, enum request request)
2385 {
2386         int scroll_steps = 0;
2387         int steps;
2388
2389         switch (request) {
2390         case REQ_MOVE_FIRST_LINE:
2391                 steps = -view->lineno;
2392                 break;
2393
2394         case REQ_MOVE_LAST_LINE:
2395                 steps = view->lines - view->lineno - 1;
2396                 break;
2397
2398         case REQ_MOVE_PAGE_UP:
2399                 steps = view->height > view->lineno
2400                       ? -view->lineno : -view->height;
2401                 break;
2402
2403         case REQ_MOVE_PAGE_DOWN:
2404                 steps = view->lineno + view->height >= view->lines
2405                       ? view->lines - view->lineno - 1 : view->height;
2406                 break;
2407
2408         case REQ_MOVE_UP:
2409                 steps = -1;
2410                 break;
2411
2412         case REQ_MOVE_DOWN:
2413                 steps = 1;
2414                 break;
2415
2416         default:
2417                 die("request %d not handled in switch", request);
2418         }
2419
2420         if (steps <= 0 && view->lineno == 0) {
2421                 report("Cannot move beyond the first line");
2422                 return;
2423
2424         } else if (steps >= 0 && view->lineno + 1 >= view->lines) {
2425                 report("Cannot move beyond the last line");
2426                 return;
2427         }
2428
2429         /* Move the current line */
2430         view->lineno += steps;
2431         assert(0 <= view->lineno && view->lineno < view->lines);
2432
2433         /* Check whether the view needs to be scrolled */
2434         if (view->lineno < view->offset ||
2435             view->lineno >= view->offset + view->height) {
2436                 scroll_steps = steps;
2437                 if (steps < 0 && -steps > view->offset) {
2438                         scroll_steps = -view->offset;
2439
2440                 } else if (steps > 0) {
2441                         if (view->lineno == view->lines - 1 &&
2442                             view->lines > view->height) {
2443                                 scroll_steps = view->lines - view->offset - 1;
2444                                 if (scroll_steps >= view->height)
2445                                         scroll_steps -= view->height - 1;
2446                         }
2447                 }
2448         }
2449
2450         if (!view_is_displayed(view)) {
2451                 view->offset += scroll_steps;
2452                 assert(0 <= view->offset && view->offset < view->lines);
2453                 view->ops->select(view, &view->line[view->lineno]);
2454                 return;
2455         }
2456
2457         /* Repaint the old "current" line if we be scrolling */
2458         if (ABS(steps) < view->height)
2459                 draw_view_line(view, view->lineno - steps - view->offset);
2460
2461         if (scroll_steps) {
2462                 do_scroll_view(view, scroll_steps);
2463                 return;
2464         }
2465
2466         /* Draw the current line */
2467         draw_view_line(view, view->lineno - view->offset);
2468
2469         wnoutrefresh(view->win);
2470         report("");
2471 }
2472
2473
2474 /*
2475  * Searching
2476  */
2477
2478 static void search_view(struct view *view, enum request request);
2479
2480 static void
2481 select_view_line(struct view *view, unsigned long lineno)
2482 {
2483         unsigned long old_lineno = view->lineno;
2484         unsigned long old_offset = view->offset;
2485
2486         if (goto_view_line(view, view->offset, lineno)) {
2487                 if (view_is_displayed(view)) {
2488                         if (old_offset != view->offset) {
2489                                 redraw_view(view);
2490                         } else {
2491                                 draw_view_line(view, old_lineno - view->offset);
2492                                 draw_view_line(view, view->lineno - view->offset);
2493                                 wnoutrefresh(view->win);
2494                         }
2495                 } else {
2496                         view->ops->select(view, &view->line[view->lineno]);
2497                 }
2498         }
2499 }
2500
2501 static void
2502 find_next(struct view *view, enum request request)
2503 {
2504         unsigned long lineno = view->lineno;
2505         int direction;
2506
2507         if (!*view->grep) {
2508                 if (!*opt_search)
2509                         report("No previous search");
2510                 else
2511                         search_view(view, request);
2512                 return;
2513         }
2514
2515         switch (request) {
2516         case REQ_SEARCH:
2517         case REQ_FIND_NEXT:
2518                 direction = 1;
2519                 break;
2520
2521         case REQ_SEARCH_BACK:
2522         case REQ_FIND_PREV:
2523                 direction = -1;
2524                 break;
2525
2526         default:
2527                 return;
2528         }
2529
2530         if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
2531                 lineno += direction;
2532
2533         /* Note, lineno is unsigned long so will wrap around in which case it
2534          * will become bigger than view->lines. */
2535         for (; lineno < view->lines; lineno += direction) {
2536                 if (view->ops->grep(view, &view->line[lineno])) {
2537                         select_view_line(view, lineno);
2538                         report("Line %ld matches '%s'", lineno + 1, view->grep);
2539                         return;
2540                 }
2541         }
2542
2543         report("No match found for '%s'", view->grep);
2544 }
2545
2546 static void
2547 search_view(struct view *view, enum request request)
2548 {
2549         int regex_err;
2550
2551         if (view->regex) {
2552                 regfree(view->regex);
2553                 *view->grep = 0;
2554         } else {
2555                 view->regex = calloc(1, sizeof(*view->regex));
2556                 if (!view->regex)
2557                         return;
2558         }
2559
2560         regex_err = regcomp(view->regex, opt_search, REG_EXTENDED);
2561         if (regex_err != 0) {
2562                 char buf[SIZEOF_STR] = "unknown error";
2563
2564                 regerror(regex_err, view->regex, buf, sizeof(buf));
2565                 report("Search failed: %s", buf);
2566                 return;
2567         }
2568
2569         string_copy(view->grep, opt_search);
2570
2571         find_next(view, request);
2572 }
2573
2574 /*
2575  * Incremental updating
2576  */
2577
2578 static void
2579 reset_view(struct view *view)
2580 {
2581         int i;
2582
2583         for (i = 0; i < view->lines; i++)
2584                 free(view->line[i].data);
2585         free(view->line);
2586
2587         view->p_offset = view->offset;
2588         view->p_yoffset = view->yoffset;
2589         view->p_lineno = view->lineno;
2590
2591         view->line = NULL;
2592         view->offset = 0;
2593         view->yoffset = 0;
2594         view->lines  = 0;
2595         view->lineno = 0;
2596         view->line_alloc = 0;
2597         view->vid[0] = 0;
2598         view->update_secs = 0;
2599 }
2600
2601 static void
2602 free_argv(const char *argv[])
2603 {
2604         int argc;
2605
2606         for (argc = 0; argv[argc]; argc++)
2607                 free((void *) argv[argc]);
2608 }
2609
2610 static bool
2611 format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags flags)
2612 {
2613         char buf[SIZEOF_STR];
2614         int argc;
2615         bool noreplace = flags == FORMAT_NONE;
2616
2617         free_argv(dst_argv);
2618
2619         for (argc = 0; src_argv[argc]; argc++) {
2620                 const char *arg = src_argv[argc];
2621                 size_t bufpos = 0;
2622
2623                 while (arg) {
2624                         char *next = strstr(arg, "%(");
2625                         int len = next - arg;
2626                         const char *value;
2627
2628                         if (!next || noreplace) {
2629                                 if (flags == FORMAT_DASH && !strcmp(arg, "--"))
2630                                         noreplace = TRUE;
2631                                 len = strlen(arg);
2632                                 value = "";
2633
2634                         } else if (!prefixcmp(next, "%(directory)")) {
2635                                 value = opt_path;
2636
2637                         } else if (!prefixcmp(next, "%(file)")) {
2638                                 value = opt_file;
2639
2640                         } else if (!prefixcmp(next, "%(ref)")) {
2641                                 value = *opt_ref ? opt_ref : "HEAD";
2642
2643                         } else if (!prefixcmp(next, "%(head)")) {
2644                                 value = ref_head;
2645
2646                         } else if (!prefixcmp(next, "%(commit)")) {
2647                                 value = ref_commit;
2648
2649                         } else if (!prefixcmp(next, "%(blob)")) {
2650                                 value = ref_blob;
2651
2652                         } else {
2653                                 report("Unknown replacement: `%s`", next);
2654                                 return FALSE;
2655                         }
2656
2657                         if (!string_format_from(buf, &bufpos, "%.*s%s", len, arg, value))
2658                                 return FALSE;
2659
2660                         arg = next && !noreplace ? strchr(next, ')') + 1 : NULL;
2661                 }
2662
2663                 dst_argv[argc] = strdup(buf);
2664                 if (!dst_argv[argc])
2665                         break;
2666         }
2667
2668         dst_argv[argc] = NULL;
2669
2670         return src_argv[argc] == NULL;
2671 }
2672
2673 static bool
2674 restore_view_position(struct view *view)
2675 {
2676         if (!view->p_restore || (view->pipe && view->lines <= view->p_lineno))
2677                 return FALSE;
2678
2679         /* Changing the view position cancels the restoring. */
2680         /* FIXME: Changing back to the first line is not detected. */
2681         if (view->offset != 0 || view->lineno != 0) {
2682                 view->p_restore = FALSE;
2683                 return FALSE;
2684         }
2685
2686         if (goto_view_line(view, view->p_offset, view->p_lineno) &&
2687             view_is_displayed(view))
2688                 werase(view->win);
2689
2690         view->yoffset = view->p_yoffset;
2691         view->p_restore = FALSE;
2692
2693         return TRUE;
2694 }
2695
2696 static void
2697 end_update(struct view *view, bool force)
2698 {
2699         if (!view->pipe)
2700                 return;
2701         while (!view->ops->read(view, NULL))
2702                 if (!force)
2703                         return;
2704         set_nonblocking_input(FALSE);
2705         if (force)
2706                 kill_io(view->pipe);
2707         done_io(view->pipe);
2708         view->pipe = NULL;
2709 }
2710
2711 static void
2712 setup_update(struct view *view, const char *vid)
2713 {
2714         set_nonblocking_input(TRUE);
2715         reset_view(view);
2716         string_copy_rev(view->vid, vid);
2717         view->pipe = &view->io;
2718         view->start_time = time(NULL);
2719 }
2720
2721 static bool
2722 prepare_update(struct view *view, const char *argv[], const char *dir,
2723                enum format_flags flags)
2724 {
2725         if (view->pipe)
2726                 end_update(view, TRUE);
2727         return init_io_rd(&view->io, argv, dir, flags);
2728 }
2729
2730 static bool
2731 prepare_update_file(struct view *view, const char *name)
2732 {
2733         if (view->pipe)
2734                 end_update(view, TRUE);
2735         return io_open(&view->io, name);
2736 }
2737
2738 static bool
2739 begin_update(struct view *view, bool refresh)
2740 {
2741         if (view->pipe)
2742                 end_update(view, TRUE);
2743
2744         if (refresh) {
2745                 if (!start_io(&view->io))
2746                         return FALSE;
2747
2748         } else {
2749                 if (view == VIEW(REQ_VIEW_TREE) && strcmp(view->vid, view->id))
2750                         opt_path[0] = 0;
2751
2752                 if (!run_io_rd(&view->io, view->ops->argv, FORMAT_ALL))
2753                         return FALSE;
2754
2755                 /* Put the current ref_* value to the view title ref
2756                  * member. This is needed by the blob view. Most other
2757                  * views sets it automatically after loading because the
2758                  * first line is a commit line. */
2759                 string_copy_rev(view->ref, view->id);
2760         }
2761
2762         setup_update(view, view->id);
2763
2764         return TRUE;
2765 }
2766
2767 #define ITEM_CHUNK_SIZE 256
2768 static void *
2769 realloc_items(void *mem, size_t *size, size_t new_size, size_t item_size)
2770 {
2771         size_t num_chunks = *size / ITEM_CHUNK_SIZE;
2772         size_t num_chunks_new = (new_size + ITEM_CHUNK_SIZE - 1) / ITEM_CHUNK_SIZE;
2773
2774         if (mem == NULL || num_chunks != num_chunks_new) {
2775                 *size = num_chunks_new * ITEM_CHUNK_SIZE;
2776                 mem = realloc(mem, *size * item_size);
2777         }
2778
2779         return mem;
2780 }
2781
2782 static struct line *
2783 realloc_lines(struct view *view, size_t line_size)
2784 {
2785         size_t alloc = view->line_alloc;
2786         struct line *tmp = realloc_items(view->line, &alloc, line_size,
2787                                          sizeof(*view->line));
2788
2789         if (!tmp)
2790                 return NULL;
2791
2792         view->line = tmp;
2793         view->line_alloc = alloc;
2794         return view->line;
2795 }
2796
2797 static bool
2798 update_view(struct view *view)
2799 {
2800         char out_buffer[BUFSIZ * 2];
2801         char *line;
2802         /* Clear the view and redraw everything since the tree sorting
2803          * might have rearranged things. */
2804         bool redraw = view->lines == 0;
2805         bool can_read = TRUE;
2806
2807         if (!view->pipe)
2808                 return TRUE;
2809
2810         if (!io_can_read(view->pipe)) {
2811                 if (view->lines == 0) {
2812                         time_t secs = time(NULL) - view->start_time;
2813
2814                         if (secs > 1 && secs > view->update_secs) {
2815                                 if (view->update_secs == 0)
2816                                         redraw_view(view);
2817                                 update_view_title(view);
2818                                 view->update_secs = secs;
2819                         }
2820                 }
2821                 return TRUE;
2822         }
2823
2824         for (; (line = io_get(view->pipe, '\n', can_read)); can_read = FALSE) {
2825                 if (opt_iconv != ICONV_NONE) {
2826                         ICONV_CONST char *inbuf = line;
2827                         size_t inlen = strlen(line) + 1;
2828
2829                         char *outbuf = out_buffer;
2830                         size_t outlen = sizeof(out_buffer);
2831
2832                         size_t ret;
2833
2834                         ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
2835                         if (ret != (size_t) -1)
2836                                 line = out_buffer;
2837                 }
2838
2839                 if (!view->ops->read(view, line)) {
2840                         report("Allocation failure");
2841                         end_update(view, TRUE);
2842                         return FALSE;
2843                 }
2844         }
2845
2846         {
2847                 unsigned long lines = view->lines;
2848                 int digits;
2849
2850                 for (digits = 0; lines; digits++)
2851                         lines /= 10;
2852
2853                 /* Keep the displayed view in sync with line number scaling. */
2854                 if (digits != view->digits) {
2855                         view->digits = digits;
2856                         if (opt_line_number || view == VIEW(REQ_VIEW_BLAME))
2857                                 redraw = TRUE;
2858                 }
2859         }
2860
2861         if (io_error(view->pipe)) {
2862                 report("Failed to read: %s", io_strerror(view->pipe));
2863                 end_update(view, TRUE);
2864
2865         } else if (io_eof(view->pipe)) {
2866                 report("");
2867                 end_update(view, FALSE);
2868         }
2869
2870         if (restore_view_position(view))
2871                 redraw = TRUE;
2872
2873         if (!view_is_displayed(view))
2874                 return TRUE;
2875
2876         if (redraw)
2877                 redraw_view_from(view, 0);
2878         else
2879                 redraw_view_dirty(view);
2880
2881         /* Update the title _after_ the redraw so that if the redraw picks up a
2882          * commit reference in view->ref it'll be available here. */
2883         update_view_title(view);
2884         return TRUE;
2885 }
2886
2887 static struct line *
2888 add_line_data(struct view *view, void *data, enum line_type type)
2889 {
2890         struct line *line;
2891
2892         if (!realloc_lines(view, view->lines + 1))
2893                 return NULL;
2894
2895         line = &view->line[view->lines++];
2896         memset(line, 0, sizeof(*line));
2897         line->type = type;
2898         line->data = data;
2899         line->dirty = 1;
2900
2901         return line;
2902 }
2903
2904 static struct line *
2905 add_line_text(struct view *view, const char *text, enum line_type type)
2906 {
2907         char *data = text ? strdup(text) : NULL;
2908
2909         return data ? add_line_data(view, data, type) : NULL;
2910 }
2911
2912 static struct line *
2913 add_line_format(struct view *view, enum line_type type, const char *fmt, ...)
2914 {
2915         char buf[SIZEOF_STR];
2916         va_list args;
2917
2918         va_start(args, fmt);
2919         if (vsnprintf(buf, sizeof(buf), fmt, args) >= sizeof(buf))
2920                 buf[0] = 0;
2921         va_end(args);
2922
2923         return buf[0] ? add_line_text(view, buf, type) : NULL;
2924 }
2925
2926 /*
2927  * View opening
2928  */
2929
2930 enum open_flags {
2931         OPEN_DEFAULT = 0,       /* Use default view switching. */
2932         OPEN_SPLIT = 1,         /* Split current view. */
2933         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2934         OPEN_REFRESH = 16,      /* Refresh view using previous command. */
2935         OPEN_PREPARED = 32,     /* Open already prepared command. */
2936 };
2937
2938 static void
2939 open_view(struct view *prev, enum request request, enum open_flags flags)
2940 {
2941         bool split = !!(flags & OPEN_SPLIT);
2942         bool reload = !!(flags & (OPEN_RELOAD | OPEN_REFRESH | OPEN_PREPARED));
2943         bool nomaximize = !!(flags & OPEN_REFRESH);
2944         struct view *view = VIEW(request);
2945         int nviews = displayed_views();
2946         struct view *base_view = display[0];
2947
2948         if (view == prev && nviews == 1 && !reload) {
2949                 report("Already in %s view", view->name);
2950                 return;
2951         }
2952
2953         if (view->git_dir && !opt_git_dir[0]) {
2954                 report("The %s view is disabled in pager view", view->name);
2955                 return;
2956         }
2957
2958         if (split) {
2959                 display[1] = view;
2960                 current_view = 1;
2961         } else if (!nomaximize) {
2962                 /* Maximize the current view. */
2963                 memset(display, 0, sizeof(display));
2964                 current_view = 0;
2965                 display[current_view] = view;
2966         }
2967
2968         /* Resize the view when switching between split- and full-screen,
2969          * or when switching between two different full-screen views. */
2970         if (nviews != displayed_views() ||
2971             (nviews == 1 && base_view != display[0]))
2972                 resize_display();
2973
2974         if (view->ops->open) {
2975                 if (view->pipe)
2976                         end_update(view, TRUE);
2977                 if (!view->ops->open(view)) {
2978                         report("Failed to load %s view", view->name);
2979                         return;
2980                 }
2981                 restore_view_position(view);
2982
2983         } else if ((reload || strcmp(view->vid, view->id)) &&
2984                    !begin_update(view, flags & (OPEN_REFRESH | OPEN_PREPARED))) {
2985                 report("Failed to load %s view", view->name);
2986                 return;
2987         }
2988
2989         if (split && prev->lineno - prev->offset >= prev->height) {
2990                 /* Take the title line into account. */
2991                 int lines = prev->lineno - prev->offset - prev->height + 1;
2992
2993                 /* Scroll the view that was split if the current line is
2994                  * outside the new limited view. */
2995                 do_scroll_view(prev, lines);
2996         }
2997
2998         if (prev && view != prev) {
2999                 if (split) {
3000                         /* "Blur" the previous view. */
3001                         update_view_title(prev);
3002                 }
3003
3004                 view->parent = prev;
3005         }
3006
3007         if (view->pipe && view->lines == 0) {
3008                 /* Clear the old view and let the incremental updating refill
3009                  * the screen. */
3010                 werase(view->win);
3011                 view->p_restore = flags & (OPEN_RELOAD | OPEN_REFRESH);
3012                 report("");
3013         } else if (view_is_displayed(view)) {
3014                 redraw_view(view);
3015                 report("");
3016         }
3017 }
3018
3019 static void
3020 open_external_viewer(const char *argv[], const char *dir)
3021 {
3022         def_prog_mode();           /* save current tty modes */
3023         endwin();                  /* restore original tty modes */
3024         run_io_fg(argv, dir);
3025         fprintf(stderr, "Press Enter to continue");
3026         getc(opt_tty);
3027         reset_prog_mode();
3028         redraw_display(TRUE);
3029 }
3030
3031 static void
3032 open_mergetool(const char *file)
3033 {
3034         const char *mergetool_argv[] = { "git", "mergetool", file, NULL };
3035
3036         open_external_viewer(mergetool_argv, opt_cdup);
3037 }
3038
3039 static void
3040 open_editor(bool from_root, const char *file)
3041 {
3042         const char *editor_argv[] = { "vi", file, NULL };
3043         const char *editor;
3044
3045         editor = getenv("GIT_EDITOR");
3046         if (!editor && *opt_editor)
3047                 editor = opt_editor;
3048         if (!editor)
3049                 editor = getenv("VISUAL");
3050         if (!editor)
3051                 editor = getenv("EDITOR");
3052         if (!editor)
3053                 editor = "vi";
3054
3055         editor_argv[0] = editor;
3056         open_external_viewer(editor_argv, from_root ? opt_cdup : NULL);
3057 }
3058
3059 static void
3060 open_run_request(enum request request)
3061 {
3062         struct run_request *req = get_run_request(request);
3063         const char *argv[ARRAY_SIZE(req->argv)] = { NULL };
3064
3065         if (!req) {
3066                 report("Unknown run request");
3067                 return;
3068         }
3069
3070         if (format_argv(argv, req->argv, FORMAT_ALL))
3071                 open_external_viewer(argv, NULL);
3072         free_argv(argv);
3073 }
3074
3075 /*
3076  * User request switch noodle
3077  */
3078
3079 static int
3080 view_driver(struct view *view, enum request request)
3081 {
3082         int i;
3083
3084         if (request == REQ_NONE) {
3085                 doupdate();
3086                 return TRUE;
3087         }
3088
3089         if (request > REQ_NONE) {
3090                 open_run_request(request);
3091                 /* FIXME: When all views can refresh always do this. */
3092                 if (view == VIEW(REQ_VIEW_STATUS) ||
3093                     view == VIEW(REQ_VIEW_MAIN) ||
3094                     view == VIEW(REQ_VIEW_LOG) ||
3095                     view == VIEW(REQ_VIEW_STAGE))
3096                         request = REQ_REFRESH;
3097                 else
3098                         return TRUE;
3099         }
3100
3101         if (view && view->lines) {
3102                 request = view->ops->request(view, request, &view->line[view->lineno]);
3103                 if (request == REQ_NONE)
3104                         return TRUE;
3105         }
3106
3107         switch (request) {
3108         case REQ_MOVE_UP:
3109         case REQ_MOVE_DOWN:
3110         case REQ_MOVE_PAGE_UP:
3111         case REQ_MOVE_PAGE_DOWN:
3112         case REQ_MOVE_FIRST_LINE:
3113         case REQ_MOVE_LAST_LINE:
3114                 move_view(view, request);
3115                 break;
3116
3117         case REQ_SCROLL_LEFT:
3118         case REQ_SCROLL_RIGHT:
3119         case REQ_SCROLL_LINE_DOWN:
3120         case REQ_SCROLL_LINE_UP:
3121         case REQ_SCROLL_PAGE_DOWN:
3122         case REQ_SCROLL_PAGE_UP:
3123                 scroll_view(view, request);
3124                 break;
3125
3126         case REQ_VIEW_BLAME:
3127                 if (!opt_file[0]) {
3128                         report("No file chosen, press %s to open tree view",
3129                                get_key(REQ_VIEW_TREE));
3130                         break;
3131                 }
3132                 open_view(view, request, OPEN_DEFAULT);
3133                 break;
3134
3135         case REQ_VIEW_BLOB:
3136                 if (!ref_blob[0]) {
3137                         report("No file chosen, press %s to open tree view",
3138                                get_key(REQ_VIEW_TREE));
3139                         break;
3140                 }
3141                 open_view(view, request, OPEN_DEFAULT);
3142                 break;
3143
3144         case REQ_VIEW_PAGER:
3145                 if (!VIEW(REQ_VIEW_PAGER)->pipe && !VIEW(REQ_VIEW_PAGER)->lines) {
3146                         report("No pager content, press %s to run command from prompt",
3147                                get_key(REQ_PROMPT));
3148                         break;
3149                 }
3150                 open_view(view, request, OPEN_DEFAULT);
3151                 break;
3152
3153         case REQ_VIEW_STAGE:
3154                 if (!VIEW(REQ_VIEW_STAGE)->lines) {
3155                         report("No stage content, press %s to open the status view and choose file",
3156                                get_key(REQ_VIEW_STATUS));
3157                         break;
3158                 }
3159                 open_view(view, request, OPEN_DEFAULT);
3160                 break;
3161
3162         case REQ_VIEW_STATUS:
3163                 if (opt_is_inside_work_tree == FALSE) {
3164                         report("The status view requires a working tree");
3165                         break;
3166                 }
3167                 open_view(view, request, OPEN_DEFAULT);
3168                 break;
3169
3170         case REQ_VIEW_MAIN:
3171         case REQ_VIEW_DIFF:
3172         case REQ_VIEW_LOG:
3173         case REQ_VIEW_TREE:
3174         case REQ_VIEW_HELP:
3175                 open_view(view, request, OPEN_DEFAULT);
3176                 break;
3177
3178         case REQ_NEXT:
3179         case REQ_PREVIOUS:
3180                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
3181
3182                 if ((view == VIEW(REQ_VIEW_DIFF) &&
3183                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
3184                    (view == VIEW(REQ_VIEW_DIFF) &&
3185                      view->parent == VIEW(REQ_VIEW_BLAME)) ||
3186                    (view == VIEW(REQ_VIEW_STAGE) &&
3187                      view->parent == VIEW(REQ_VIEW_STATUS)) ||
3188                    (view == VIEW(REQ_VIEW_BLOB) &&
3189                      view->parent == VIEW(REQ_VIEW_TREE))) {
3190                         int line;
3191
3192                         view = view->parent;
3193                         line = view->lineno;
3194                         move_view(view, request);
3195                         if (view_is_displayed(view))
3196                                 update_view_title(view);
3197                         if (line != view->lineno)
3198                                 view->ops->request(view, REQ_ENTER,
3199                                                    &view->line[view->lineno]);
3200
3201                 } else {
3202                         move_view(view, request);
3203                 }
3204                 break;
3205
3206         case REQ_VIEW_NEXT:
3207         {
3208                 int nviews = displayed_views();
3209                 int next_view = (current_view + 1) % nviews;
3210
3211                 if (next_view == current_view) {
3212                         report("Only one view is displayed");
3213                         break;
3214                 }
3215
3216                 current_view = next_view;
3217                 /* Blur out the title of the previous view. */
3218                 update_view_title(view);
3219                 report("");
3220                 break;
3221         }
3222         case REQ_REFRESH:
3223                 report("Refreshing is not yet supported for the %s view", view->name);
3224                 break;
3225
3226         case REQ_MAXIMIZE:
3227                 if (displayed_views() == 2)
3228                         open_view(view, VIEW_REQ(view), OPEN_DEFAULT);
3229                 break;
3230
3231         case REQ_TOGGLE_LINENO:
3232                 toggle_view_option(&opt_line_number, "line numbers");
3233                 break;
3234
3235         case REQ_TOGGLE_DATE:
3236                 toggle_view_option(&opt_date, "date display");
3237                 break;
3238
3239         case REQ_TOGGLE_AUTHOR:
3240                 toggle_view_option(&opt_author, "author display");
3241                 break;
3242
3243         case REQ_TOGGLE_REV_GRAPH:
3244                 toggle_view_option(&opt_rev_graph, "revision graph display");
3245                 break;
3246
3247         case REQ_TOGGLE_REFS:
3248                 toggle_view_option(&opt_show_refs, "reference display");
3249                 break;
3250
3251         case REQ_SEARCH:
3252         case REQ_SEARCH_BACK:
3253                 search_view(view, request);
3254                 break;
3255
3256         case REQ_FIND_NEXT:
3257         case REQ_FIND_PREV:
3258                 find_next(view, request);
3259                 break;
3260
3261         case REQ_STOP_LOADING:
3262                 for (i = 0; i < ARRAY_SIZE(views); i++) {
3263                         view = &views[i];
3264                         if (view->pipe)
3265                                 report("Stopped loading the %s view", view->name),
3266                         end_update(view, TRUE);
3267                 }
3268                 break;
3269
3270         case REQ_SHOW_VERSION:
3271                 report("tig-%s (built %s)", TIG_VERSION, __DATE__);
3272                 return TRUE;
3273
3274         case REQ_SCREEN_REDRAW:
3275                 redraw_display(TRUE);
3276                 break;
3277
3278         case REQ_EDIT:
3279                 report("Nothing to edit");
3280                 break;
3281
3282         case REQ_ENTER:
3283                 report("Nothing to enter");
3284                 break;
3285
3286         case REQ_VIEW_CLOSE:
3287                 /* XXX: Mark closed views by letting view->parent point to the
3288                  * view itself. Parents to closed view should never be
3289                  * followed. */
3290                 if (view->parent &&
3291                     view->parent->parent != view->parent) {
3292                         memset(display, 0, sizeof(display));
3293                         current_view = 0;
3294                         display[current_view] = view->parent;
3295                         view->parent = view;
3296                         resize_display();
3297                         redraw_display(FALSE);
3298                         report("");
3299                         break;
3300                 }
3301                 /* Fall-through */
3302         case REQ_QUIT:
3303                 return FALSE;
3304
3305         default:
3306                 report("Unknown key, press 'h' for help");
3307                 return TRUE;
3308         }
3309
3310         return TRUE;
3311 }
3312
3313
3314 /*
3315  * View backend utilities
3316  */
3317
3318 static void
3319 parse_timezone(time_t *time, const char *zone)
3320 {
3321         long tz;
3322
3323         tz  = ('0' - zone[1]) * 60 * 60 * 10;
3324         tz += ('0' - zone[2]) * 60 * 60;
3325         tz += ('0' - zone[3]) * 60;
3326         tz += ('0' - zone[4]);
3327
3328         if (zone[0] == '-')
3329                 tz = -tz;
3330
3331         *time -= tz;
3332 }
3333
3334 /* Parse author lines where the name may be empty:
3335  *      author  <email@address.tld> 1138474660 +0100
3336  */
3337 static void
3338 parse_author_line(char *ident, char *author, size_t authorsize, struct tm *tm)
3339 {
3340         char *nameend = strchr(ident, '<');
3341         char *emailend = strchr(ident, '>');
3342
3343         if (nameend && emailend)
3344                 *nameend = *emailend = 0;
3345         ident = chomp_string(ident);
3346         if (!*ident) {
3347                 if (nameend)
3348                         ident = chomp_string(nameend + 1);
3349                 if (!*ident)
3350                         ident = "Unknown";
3351         }
3352
3353         string_ncopy_do(author, authorsize, ident, strlen(ident));
3354
3355         /* Parse epoch and timezone */
3356         if (emailend && emailend[1] == ' ') {
3357                 char *secs = emailend + 2;
3358                 char *zone = strchr(secs, ' ');
3359                 time_t time = (time_t) atol(secs);
3360
3361                 if (zone && strlen(zone) == STRING_SIZE(" +0700"))
3362                         parse_timezone(&time, zone + 1);
3363
3364                 gmtime_r(&time, tm);
3365         }
3366 }
3367
3368 static enum input_status
3369 select_commit_parent_handler(void *data, char *buf, int c)
3370 {
3371         size_t parents = *(size_t *) data;
3372         int parent = 0;
3373
3374         if (!isdigit(c))
3375                 return INPUT_SKIP;
3376
3377         if (*buf)
3378                 parent = atoi(buf) * 10;
3379         parent += c - '0';
3380
3381         if (parent > parents)
3382                 return INPUT_SKIP;
3383         return INPUT_OK;
3384 }
3385
3386 static bool
3387 select_commit_parent(const char *id, char rev[SIZEOF_REV], const char *path)
3388 {
3389         char buf[SIZEOF_STR * 4];
3390         const char *revlist_argv[] = {
3391                 "git", "rev-list", "-1", "--parents", id, "--", path, NULL
3392         };
3393         int parents;
3394
3395         if (!run_io_buf(revlist_argv, buf, sizeof(buf)) ||
3396             !*chomp_string(buf) ||
3397             (parents = (strlen(buf) / 40) - 1) < 0) {
3398                 report("Failed to get parent information");
3399                 return FALSE;
3400
3401         } else if (parents == 0) {
3402                 if (path)
3403                         report("Path '%s' does not exist in the parent", path);
3404                 else
3405                         report("The selected commit has no parents");
3406                 return FALSE;
3407         }
3408
3409         if (parents > 1) {
3410                 char prompt[SIZEOF_STR];
3411                 char *result;
3412
3413                 if (!string_format(prompt, "Which parent? [1..%d] ", parents))
3414                         return FALSE;
3415                 result = prompt_input(prompt, select_commit_parent_handler, &parents);
3416                 if (!result)
3417                         return FALSE;
3418                 parents = atoi(result);
3419         }
3420
3421         string_copy_rev(rev, &buf[41 * parents]);
3422         return TRUE;
3423 }
3424
3425 /*
3426  * Pager backend
3427  */
3428
3429 static bool
3430 pager_draw(struct view *view, struct line *line, unsigned int lineno)
3431 {
3432         char text[SIZEOF_STR];
3433
3434         if (opt_line_number && draw_lineno(view, lineno))
3435                 return TRUE;
3436
3437         string_expand(text, sizeof(text), line->data, opt_tab_size);
3438         draw_text(view, line->type, text, TRUE);
3439         return TRUE;
3440 }
3441
3442 static bool
3443 add_describe_ref(char *buf, size_t *bufpos, const char *commit_id, const char *sep)
3444 {
3445         const char *describe_argv[] = { "git", "describe", commit_id, NULL };
3446         char refbuf[SIZEOF_STR];
3447         char *ref = NULL;
3448
3449         if (run_io_buf(describe_argv, refbuf, sizeof(refbuf)))
3450                 ref = chomp_string(refbuf);
3451
3452         if (!ref || !*ref)
3453                 return TRUE;
3454
3455         /* This is the only fatal call, since it can "corrupt" the buffer. */
3456         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
3457                 return FALSE;
3458
3459         return TRUE;
3460 }
3461
3462 static void
3463 add_pager_refs(struct view *view, struct line *line)
3464 {
3465         char buf[SIZEOF_STR];
3466         char *commit_id = (char *)line->data + STRING_SIZE("commit ");
3467         struct ref **refs;
3468         size_t bufpos = 0, refpos = 0;
3469         const char *sep = "Refs: ";
3470         bool is_tag = FALSE;
3471
3472         assert(line->type == LINE_COMMIT);
3473
3474         refs = get_refs(commit_id);
3475         if (!refs) {
3476                 if (view == VIEW(REQ_VIEW_DIFF))
3477                         goto try_add_describe_ref;
3478                 return;
3479         }
3480
3481         do {
3482                 struct ref *ref = refs[refpos];
3483                 const char *fmt = ref->tag    ? "%s[%s]" :
3484                                   ref->remote ? "%s<%s>" : "%s%s";
3485
3486                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
3487                         return;
3488                 sep = ", ";
3489                 if (ref->tag)
3490                         is_tag = TRUE;
3491         } while (refs[refpos++]->next);
3492
3493         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
3494 try_add_describe_ref:
3495                 /* Add <tag>-g<commit_id> "fake" reference. */
3496                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
3497                         return;
3498         }
3499
3500         if (bufpos == 0)
3501                 return;
3502
3503         add_line_text(view, buf, LINE_PP_REFS);
3504 }
3505
3506 static bool
3507 pager_read(struct view *view, char *data)
3508 {
3509         struct line *line;
3510
3511         if (!data)
3512                 return TRUE;
3513
3514         line = add_line_text(view, data, get_line_type(data));
3515         if (!line)
3516                 return FALSE;
3517
3518         if (line->type == LINE_COMMIT &&
3519             (view == VIEW(REQ_VIEW_DIFF) ||
3520              view == VIEW(REQ_VIEW_LOG)))
3521                 add_pager_refs(view, line);
3522
3523         return TRUE;
3524 }
3525
3526 static enum request
3527 pager_request(struct view *view, enum request request, struct line *line)
3528 {
3529         int split = 0;
3530
3531         if (request != REQ_ENTER)
3532                 return request;
3533
3534         if (line->type == LINE_COMMIT &&
3535            (view == VIEW(REQ_VIEW_LOG) ||
3536             view == VIEW(REQ_VIEW_PAGER))) {
3537                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
3538                 split = 1;
3539         }
3540
3541         /* Always scroll the view even if it was split. That way
3542          * you can use Enter to scroll through the log view and
3543          * split open each commit diff. */
3544         scroll_view(view, REQ_SCROLL_LINE_DOWN);
3545
3546         /* FIXME: A minor workaround. Scrolling the view will call report("")
3547          * but if we are scrolling a non-current view this won't properly
3548          * update the view title. */
3549         if (split)
3550                 update_view_title(view);
3551
3552         return REQ_NONE;
3553 }
3554
3555 static bool
3556 pager_grep(struct view *view, struct line *line)
3557 {
3558         regmatch_t pmatch;
3559         char *text = line->data;
3560
3561         if (!*text)
3562                 return FALSE;
3563
3564         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
3565                 return FALSE;
3566
3567         return TRUE;
3568 }
3569
3570 static void
3571 pager_select(struct view *view, struct line *line)
3572 {
3573         if (line->type == LINE_COMMIT) {
3574                 char *text = (char *)line->data + STRING_SIZE("commit ");
3575
3576                 if (view != VIEW(REQ_VIEW_PAGER))
3577                         string_copy_rev(view->ref, text);
3578                 string_copy_rev(ref_commit, text);
3579         }
3580 }
3581
3582 static struct view_ops pager_ops = {
3583         "line",
3584         NULL,
3585         NULL,
3586         pager_read,
3587         pager_draw,
3588         pager_request,
3589         pager_grep,
3590         pager_select,
3591 };
3592
3593 static const char *log_argv[SIZEOF_ARG] = {
3594         "git", "log", "--no-color", "--cc", "--stat", "-n100", "%(head)", NULL
3595 };
3596
3597 static enum request
3598 log_request(struct view *view, enum request request, struct line *line)
3599 {
3600         switch (request) {
3601         case REQ_REFRESH:
3602                 load_refs();
3603                 open_view(view, REQ_VIEW_LOG, OPEN_REFRESH);
3604                 return REQ_NONE;
3605         default:
3606                 return pager_request(view, request, line);
3607         }
3608 }
3609
3610 static struct view_ops log_ops = {
3611         "line",
3612         log_argv,
3613         NULL,
3614         pager_read,
3615         pager_draw,
3616         log_request,
3617         pager_grep,
3618         pager_select,
3619 };
3620
3621 static const char *diff_argv[SIZEOF_ARG] = {
3622         "git", "show", "--pretty=fuller", "--no-color", "--root",
3623                 "--patch-with-stat", "--find-copies-harder", "-C", "%(commit)", NULL
3624 };
3625
3626 static struct view_ops diff_ops = {
3627         "line",
3628         diff_argv,
3629         NULL,
3630         pager_read,
3631         pager_draw,
3632         pager_request,
3633         pager_grep,
3634         pager_select,
3635 };
3636
3637 /*
3638  * Help backend
3639  */
3640
3641 static bool
3642 help_open(struct view *view)
3643 {
3644         char buf[SIZEOF_STR];
3645         size_t bufpos;
3646         int i;
3647
3648         if (view->lines > 0)
3649                 return TRUE;
3650
3651         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
3652
3653         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
3654                 const char *key;
3655
3656                 if (req_info[i].request == REQ_NONE)
3657                         continue;
3658
3659                 if (!req_info[i].request) {
3660                         add_line_text(view, "", LINE_DEFAULT);
3661                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
3662                         continue;
3663                 }
3664
3665                 key = get_key(req_info[i].request);
3666                 if (!*key)
3667                         key = "(no key defined)";
3668
3669                 for (bufpos = 0; bufpos <= req_info[i].namelen; bufpos++) {
3670                         buf[bufpos] = tolower(req_info[i].name[bufpos]);
3671                         if (buf[bufpos] == '_')
3672                                 buf[bufpos] = '-';
3673                 }
3674
3675                 add_line_format(view, LINE_DEFAULT, "    %-25s %-20s %s",
3676                                 key, buf, req_info[i].help);
3677         }
3678
3679         if (run_requests) {
3680                 add_line_text(view, "", LINE_DEFAULT);
3681                 add_line_text(view, "External commands:", LINE_DEFAULT);
3682         }
3683
3684         for (i = 0; i < run_requests; i++) {
3685                 struct run_request *req = get_run_request(REQ_NONE + i + 1);
3686                 const char *key;
3687                 int argc;
3688
3689                 if (!req)
3690                         continue;
3691
3692                 key = get_key_name(req->key);
3693                 if (!*key)
3694                         key = "(no key defined)";
3695
3696                 for (bufpos = 0, argc = 0; req->argv[argc]; argc++)
3697                         if (!string_format_from(buf, &bufpos, "%s%s",
3698                                                 argc ? " " : "", req->argv[argc]))
3699                                 return REQ_NONE;
3700
3701                 add_line_format(view, LINE_DEFAULT, "    %-10s %-14s `%s`",
3702                                 keymap_table[req->keymap].name, key, buf);
3703         }
3704
3705         return TRUE;
3706 }
3707
3708 static struct view_ops help_ops = {
3709         "line",
3710         NULL,
3711         help_open,
3712         NULL,
3713         pager_draw,
3714         pager_request,
3715         pager_grep,
3716         pager_select,
3717 };
3718
3719
3720 /*
3721  * Tree backend
3722  */
3723
3724 struct tree_stack_entry {
3725         struct tree_stack_entry *prev;  /* Entry below this in the stack */
3726         unsigned long lineno;           /* Line number to restore */
3727         char *name;                     /* Position of name in opt_path */
3728 };
3729
3730 /* The top of the path stack. */
3731 static struct tree_stack_entry *tree_stack = NULL;
3732 unsigned long tree_lineno = 0;
3733
3734 static void
3735 pop_tree_stack_entry(void)
3736 {
3737         struct tree_stack_entry *entry = tree_stack;
3738
3739         tree_lineno = entry->lineno;
3740         entry->name[0] = 0;
3741         tree_stack = entry->prev;
3742         free(entry);
3743 }
3744
3745 static void
3746 push_tree_stack_entry(const char *name, unsigned long lineno)
3747 {
3748         struct tree_stack_entry *entry = calloc(1, sizeof(*entry));
3749         size_t pathlen = strlen(opt_path);
3750
3751         if (!entry)
3752                 return;
3753
3754         entry->prev = tree_stack;
3755         entry->name = opt_path + pathlen;
3756         tree_stack = entry;
3757
3758         if (!string_format_from(opt_path, &pathlen, "%s/", name)) {
3759                 pop_tree_stack_entry();
3760                 return;
3761         }
3762
3763         /* Move the current line to the first tree entry. */
3764         tree_lineno = 1;
3765         entry->lineno = lineno;
3766 }
3767
3768 /* Parse output from git-ls-tree(1):
3769  *
3770  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
3771  */
3772
3773 #define SIZEOF_TREE_ATTR \
3774         STRING_SIZE("100644 blob f931e1d229c3e185caad4449bf5b66ed72462657\t")
3775
3776 #define SIZEOF_TREE_MODE \
3777         STRING_SIZE("100644 ")
3778
3779 #define TREE_ID_OFFSET \
3780         STRING_SIZE("100644 blob ")
3781
3782 struct tree_entry {
3783         char id[SIZEOF_REV];
3784         mode_t mode;
3785         struct tm time;                 /* Date from the author ident. */
3786         char author[75];                /* Author of the commit. */
3787         char name[1];
3788 };
3789
3790 static const char *
3791 tree_path(struct line *line)
3792 {
3793         return ((struct tree_entry *) line->data)->name;
3794 }
3795
3796
3797 static int
3798 tree_compare_entry(struct line *line1, struct line *line2)
3799 {
3800         if (line1->type != line2->type)
3801                 return line1->type == LINE_TREE_DIR ? -1 : 1;
3802         return strcmp(tree_path(line1), tree_path(line2));
3803 }
3804
3805 static struct line *
3806 tree_entry(struct view *view, enum line_type type, const char *path,
3807            const char *mode, const char *id)
3808 {
3809         struct tree_entry *entry = calloc(1, sizeof(*entry) + strlen(path));
3810         struct line *line = entry ? add_line_data(view, entry, type) : NULL;
3811
3812         if (!entry || !line) {
3813                 free(entry);
3814                 return NULL;
3815         }
3816
3817         strncpy(entry->name, path, strlen(path));
3818         if (mode)
3819                 entry->mode = strtoul(mode, NULL, 8);
3820         if (id)
3821                 string_copy_rev(entry->id, id);
3822
3823         return line;
3824 }
3825
3826 static bool
3827 tree_read_date(struct view *view, char *text, bool *read_date)
3828 {
3829         static char author_name[SIZEOF_STR];
3830         static struct tm author_time;
3831
3832         if (!text && *read_date) {
3833                 *read_date = FALSE;
3834                 return TRUE;
3835
3836         } else if (!text) {
3837                 char *path = *opt_path ? opt_path : ".";
3838                 /* Find next entry to process */
3839                 const char *log_file[] = {
3840                         "git", "log", "--no-color", "--pretty=raw",
3841                                 "--cc", "--raw", view->id, "--", path, NULL
3842                 };
3843                 struct io io = {};
3844
3845                 if (!view->lines) {
3846                         tree_entry(view, LINE_TREE_HEAD, opt_path, NULL, NULL);
3847                         report("Tree is empty");
3848                         return TRUE;
3849                 }
3850
3851                 if (!run_io_rd(&io, log_file, FORMAT_NONE)) {
3852                         report("Failed to load tree data");
3853                         return TRUE;
3854                 }
3855
3856                 done_io(view->pipe);
3857                 view->io = io;
3858                 *read_date = TRUE;
3859                 return FALSE;
3860
3861         } else if (*text == 'a' && get_line_type(text) == LINE_AUTHOR) {
3862                 parse_author_line(text + STRING_SIZE("author "),
3863                                   author_name, sizeof(author_name), &author_time);
3864
3865         } else if (*text == ':') {
3866                 char *pos;
3867                 size_t annotated = 1;
3868                 size_t i;
3869
3870                 pos = strchr(text, '\t');
3871                 if (!pos)
3872                         return TRUE;
3873                 text = pos + 1;
3874                 if (*opt_prefix && !strncmp(text, opt_prefix, strlen(opt_prefix)))
3875                         text += strlen(opt_prefix);
3876                 if (*opt_path && !strncmp(text, opt_path, strlen(opt_path)))
3877                         text += strlen(opt_path);
3878                 pos = strchr(text, '/');
3879                 if (pos)
3880                         *pos = 0;
3881
3882                 for (i = 1; i < view->lines; i++) {
3883                         struct line *line = &view->line[i];
3884                         struct tree_entry *entry = line->data;
3885
3886                         annotated += !!*entry->author;
3887                         if (*entry->author || strcmp(entry->name, text))
3888                                 continue;
3889
3890                         string_copy(entry->author, author_name);
3891                         memcpy(&entry->time, &author_time, sizeof(entry->time));
3892                         line->dirty = 1;
3893                         break;
3894                 }
3895
3896                 if (annotated == view->lines)
3897                         kill_io(view->pipe);
3898         }
3899         return TRUE;
3900 }
3901
3902 static bool
3903 tree_read(struct view *view, char *text)
3904 {
3905         static bool read_date = FALSE;
3906         struct tree_entry *data;
3907         struct line *entry, *line;
3908         enum line_type type;
3909         size_t textlen = text ? strlen(text) : 0;
3910         char *path = text + SIZEOF_TREE_ATTR;
3911
3912         if (read_date || !text)
3913                 return tree_read_date(view, text, &read_date);
3914
3915         if (textlen <= SIZEOF_TREE_ATTR)
3916                 return FALSE;
3917         if (view->lines == 0 &&
3918             !tree_entry(view, LINE_TREE_HEAD, opt_path, NULL, NULL))
3919                 return FALSE;
3920
3921         /* Strip the path part ... */
3922         if (*opt_path) {
3923                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
3924                 size_t striplen = strlen(opt_path);
3925
3926                 if (pathlen > striplen)
3927                         memmove(path, path + striplen,
3928                                 pathlen - striplen + 1);
3929
3930                 /* Insert "link" to parent directory. */
3931                 if (view->lines == 1 &&
3932                     !tree_entry(view, LINE_TREE_DIR, "..", "040000", view->ref))
3933                         return FALSE;
3934         }
3935
3936         type = text[SIZEOF_TREE_MODE] == 't' ? LINE_TREE_DIR : LINE_TREE_FILE;
3937         entry = tree_entry(view, type, path, text, text + TREE_ID_OFFSET);
3938         if (!entry)
3939                 return FALSE;
3940         data = entry->data;
3941
3942         /* Skip "Directory ..." and ".." line. */
3943         for (line = &view->line[1 + !!*opt_path]; line < entry; line++) {
3944                 if (tree_compare_entry(line, entry) <= 0)
3945                         continue;
3946
3947                 memmove(line + 1, line, (entry - line) * sizeof(*entry));
3948
3949                 line->data = data;
3950                 line->type = type;
3951                 for (; line <= entry; line++)
3952                         line->dirty = line->cleareol = 1;
3953                 return TRUE;
3954         }
3955
3956         if (tree_lineno > view->lineno) {
3957                 view->lineno = tree_lineno;
3958                 tree_lineno = 0;
3959         }
3960
3961         return TRUE;
3962 }
3963
3964 static bool
3965 tree_draw(struct view *view, struct line *line, unsigned int lineno)
3966 {
3967         struct tree_entry *entry = line->data;
3968
3969         if (line->type == LINE_TREE_HEAD) {
3970                 if (draw_text(view, line->type, "Directory path /", TRUE))
3971                         return TRUE;
3972         } else {
3973                 if (draw_mode(view, entry->mode))
3974                         return TRUE;
3975
3976                 if (opt_author && draw_author(view, entry->author))
3977                         return TRUE;
3978
3979                 if (opt_date && draw_date(view, *entry->author ? &entry->time : NULL))
3980                         return TRUE;
3981         }
3982         if (draw_text(view, line->type, entry->name, TRUE))
3983                 return TRUE;
3984         return TRUE;
3985 }
3986
3987 static void
3988 open_blob_editor()
3989 {
3990         char file[SIZEOF_STR] = "/tmp/tigblob.XXXXXX";
3991         int fd = mkstemp(file);
3992
3993         if (fd == -1)
3994                 report("Failed to create temporary file");
3995         else if (!run_io_append(blob_ops.argv, FORMAT_ALL, fd))
3996                 report("Failed to save blob data to file");
3997         else
3998                 open_editor(FALSE, file);
3999         if (fd != -1)
4000                 unlink(file);
4001 }
4002
4003 static enum request
4004 tree_request(struct view *view, enum request request, struct line *line)
4005 {
4006         enum open_flags flags;
4007
4008         switch (request) {
4009         case REQ_VIEW_BLAME:
4010                 if (line->type != LINE_TREE_FILE) {
4011                         report("Blame only supported for files");
4012                         return REQ_NONE;
4013                 }
4014
4015                 string_copy(opt_ref, view->vid);
4016                 return request;
4017
4018         case REQ_EDIT:
4019                 if (line->type != LINE_TREE_FILE) {
4020                         report("Edit only supported for files");
4021                 } else if (!is_head_commit(view->vid)) {
4022                         open_blob_editor();
4023                 } else {
4024                         open_editor(TRUE, opt_file);
4025                 }
4026                 return REQ_NONE;
4027
4028         case REQ_PARENT:
4029                 if (!*opt_path) {
4030                         /* quit view if at top of tree */
4031                         return REQ_VIEW_CLOSE;
4032                 }
4033                 /* fake 'cd  ..' */
4034                 line = &view->line[1];
4035                 break;
4036
4037         case REQ_ENTER:
4038                 break;
4039
4040         default:
4041                 return request;
4042         }
4043
4044         /* Cleanup the stack if the tree view is at a different tree. */
4045         while (!*opt_path && tree_stack)
4046                 pop_tree_stack_entry();
4047
4048         switch (line->type) {
4049         case LINE_TREE_DIR:
4050                 /* Depending on whether it is a subdirectory or parent link
4051                  * mangle the path buffer. */
4052                 if (line == &view->line[1] && *opt_path) {
4053                         pop_tree_stack_entry();
4054
4055                 } else {
4056                         const char *basename = tree_path(line);
4057
4058                         push_tree_stack_entry(basename, view->lineno);
4059                 }
4060
4061                 /* Trees and subtrees share the same ID, so they are not not
4062                  * unique like blobs. */
4063                 flags = OPEN_RELOAD;
4064                 request = REQ_VIEW_TREE;
4065                 break;
4066
4067         case LINE_TREE_FILE:
4068                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4069                 request = REQ_VIEW_BLOB;
4070                 break;
4071
4072         default:
4073                 return REQ_NONE;
4074         }
4075
4076         open_view(view, request, flags);
4077         if (request == REQ_VIEW_TREE)
4078                 view->lineno = tree_lineno;
4079
4080         return REQ_NONE;
4081 }
4082
4083 static void
4084 tree_select(struct view *view, struct line *line)
4085 {
4086         struct tree_entry *entry = line->data;
4087
4088         if (line->type == LINE_TREE_FILE) {
4089                 string_copy_rev(ref_blob, entry->id);
4090                 string_format(opt_file, "%s%s", opt_path, tree_path(line));
4091
4092         } else if (line->type != LINE_TREE_DIR) {
4093                 return;
4094         }
4095
4096         string_copy_rev(view->ref, entry->id);
4097 }
4098
4099 static const char *tree_argv[SIZEOF_ARG] = {
4100         "git", "ls-tree", "%(commit)", "%(directory)", NULL
4101 };
4102
4103 static struct view_ops tree_ops = {
4104         "file",
4105         tree_argv,
4106         NULL,
4107         tree_read,
4108         tree_draw,
4109         tree_request,
4110         pager_grep,
4111         tree_select,
4112 };
4113
4114 static bool
4115 blob_read(struct view *view, char *line)
4116 {
4117         if (!line)
4118                 return TRUE;
4119         return add_line_text(view, line, LINE_DEFAULT) != NULL;
4120 }
4121
4122 static enum request
4123 blob_request(struct view *view, enum request request, struct line *line)
4124 {
4125         switch (request) {
4126         case REQ_EDIT:
4127                 open_blob_editor();
4128                 return REQ_NONE;
4129         default:
4130                 return pager_request(view, request, line);
4131         }
4132 }
4133
4134 static const char *blob_argv[SIZEOF_ARG] = {
4135         "git", "cat-file", "blob", "%(blob)", NULL
4136 };
4137
4138 static struct view_ops blob_ops = {
4139         "line",
4140         blob_argv,
4141         NULL,
4142         blob_read,
4143         pager_draw,
4144         blob_request,
4145         pager_grep,
4146         pager_select,
4147 };
4148
4149 /*
4150  * Blame backend
4151  *
4152  * Loading the blame view is a two phase job:
4153  *
4154  *  1. File content is read either using opt_file from the
4155  *     filesystem or using git-cat-file.
4156  *  2. Then blame information is incrementally added by
4157  *     reading output from git-blame.
4158  */
4159
4160 static const char *blame_head_argv[] = {
4161         "git", "blame", "--incremental", "--", "%(file)", NULL
4162 };
4163
4164 static const char *blame_ref_argv[] = {
4165         "git", "blame", "--incremental", "%(ref)", "--", "%(file)", NULL
4166 };
4167
4168 static const char *blame_cat_file_argv[] = {
4169         "git", "cat-file", "blob", "%(ref):%(file)", NULL
4170 };
4171
4172 struct blame_commit {
4173         char id[SIZEOF_REV];            /* SHA1 ID. */
4174         char title[128];                /* First line of the commit message. */
4175         char author[75];                /* Author of the commit. */
4176         struct tm time;                 /* Date from the author ident. */
4177         char filename[128];             /* Name of file. */
4178         bool has_previous;              /* Was a "previous" line detected. */
4179 };
4180
4181 struct blame {
4182         struct blame_commit *commit;
4183         unsigned long lineno;
4184         char text[1];
4185 };
4186
4187 static bool
4188 blame_open(struct view *view)
4189 {
4190         if (*opt_ref || !io_open(&view->io, opt_file)) {
4191                 if (!run_io_rd(&view->io, blame_cat_file_argv, FORMAT_ALL))
4192                         return FALSE;
4193         }
4194
4195         setup_update(view, opt_file);
4196         string_format(view->ref, "%s ...", opt_file);
4197
4198         return TRUE;
4199 }
4200
4201 static struct blame_commit *
4202 get_blame_commit(struct view *view, const char *id)
4203 {
4204         size_t i;
4205
4206         for (i = 0; i < view->lines; i++) {
4207                 struct blame *blame = view->line[i].data;
4208
4209                 if (!blame->commit)
4210                         continue;
4211
4212                 if (!strncmp(blame->commit->id, id, SIZEOF_REV - 1))
4213                         return blame->commit;
4214         }
4215
4216         {
4217                 struct blame_commit *commit = calloc(1, sizeof(*commit));
4218
4219                 if (commit)
4220                         string_ncopy(commit->id, id, SIZEOF_REV);
4221                 return commit;
4222         }
4223 }
4224
4225 static bool
4226 parse_number(const char **posref, size_t *number, size_t min, size_t max)
4227 {
4228         const char *pos = *posref;
4229
4230         *posref = NULL;
4231         pos = strchr(pos + 1, ' ');
4232         if (!pos || !isdigit(pos[1]))
4233                 return FALSE;
4234         *number = atoi(pos + 1);
4235         if (*number < min || *number > max)
4236                 return FALSE;
4237
4238         *posref = pos;
4239         return TRUE;
4240 }
4241
4242 static struct blame_commit *
4243 parse_blame_commit(struct view *view, const char *text, int *blamed)
4244 {
4245         struct blame_commit *commit;
4246         struct blame *blame;
4247         const char *pos = text + SIZEOF_REV - 2;
4248         size_t orig_lineno = 0;
4249         size_t lineno;
4250         size_t group;
4251
4252         if (strlen(text) <= SIZEOF_REV || pos[1] != ' ')
4253                 return NULL;
4254
4255         if (!parse_number(&pos, &orig_lineno, 1, 9999999) ||
4256             !parse_number(&pos, &lineno, 1, view->lines) ||
4257             !parse_number(&pos, &group, 1, view->lines - lineno + 1))
4258                 return NULL;
4259
4260         commit = get_blame_commit(view, text);
4261         if (!commit)
4262                 return NULL;
4263
4264         *blamed += group;
4265         while (group--) {
4266                 struct line *line = &view->line[lineno + group - 1];
4267
4268                 blame = line->data;
4269                 blame->commit = commit;
4270                 blame->lineno = orig_lineno + group - 1;
4271                 line->dirty = 1;
4272         }
4273
4274         return commit;
4275 }
4276
4277 static bool
4278 blame_read_file(struct view *view, const char *line, bool *read_file)
4279 {
4280         if (!line) {
4281                 const char **argv = *opt_ref ? blame_ref_argv : blame_head_argv;
4282                 struct io io = {};
4283
4284                 if (view->lines == 0 && !view->parent)
4285                         die("No blame exist for %s", view->vid);
4286
4287                 if (view->lines == 0 || !run_io_rd(&io, argv, FORMAT_ALL)) {
4288                         report("Failed to load blame data");
4289                         return TRUE;
4290                 }
4291
4292                 done_io(view->pipe);
4293                 view->io = io;
4294                 *read_file = FALSE;
4295                 return FALSE;
4296
4297         } else {
4298                 size_t linelen = strlen(line);
4299                 struct blame *blame = malloc(sizeof(*blame) + linelen);
4300
4301                 if (!blame)
4302                         return FALSE;
4303
4304                 blame->commit = NULL;
4305                 strncpy(blame->text, line, linelen);
4306                 blame->text[linelen] = 0;
4307                 return add_line_data(view, blame, LINE_BLAME_ID) != NULL;
4308         }
4309 }
4310
4311 static bool
4312 match_blame_header(const char *name, char **line)
4313 {
4314         size_t namelen = strlen(name);
4315         bool matched = !strncmp(name, *line, namelen);
4316
4317         if (matched)
4318                 *line += namelen;
4319
4320         return matched;
4321 }
4322
4323 static bool
4324 blame_read(struct view *view, char *line)
4325 {
4326         static struct blame_commit *commit = NULL;
4327         static int blamed = 0;
4328         static time_t author_time;
4329         static bool read_file = TRUE;
4330
4331         if (read_file)
4332                 return blame_read_file(view, line, &read_file);
4333
4334         if (!line) {
4335                 /* Reset all! */
4336                 commit = NULL;
4337                 blamed = 0;
4338                 read_file = TRUE;
4339                 string_format(view->ref, "%s", view->vid);
4340                 if (view_is_displayed(view)) {
4341                         update_view_title(view);
4342                         redraw_view_from(view, 0);
4343                 }
4344                 return TRUE;
4345         }
4346
4347         if (!commit) {
4348                 commit = parse_blame_commit(view, line, &blamed);
4349                 string_format(view->ref, "%s %2d%%", view->vid,
4350                               view->lines ? blamed * 100 / view->lines : 0);
4351
4352         } else if (match_blame_header("author ", &line)) {
4353                 string_ncopy(commit->author, line, strlen(line));
4354
4355         } else if (match_blame_header("author-time ", &line)) {
4356                 author_time = (time_t) atol(line);
4357
4358         } else if (match_blame_header("author-tz ", &line)) {
4359                 parse_timezone(&author_time, line);
4360                 gmtime_r(&author_time, &commit->time);
4361
4362         } else if (match_blame_header("summary ", &line)) {
4363                 string_ncopy(commit->title, line, strlen(line));
4364
4365         } else if (match_blame_header("previous ", &line)) {
4366                 commit->has_previous = TRUE;
4367
4368         } else if (match_blame_header("filename ", &line)) {
4369                 string_ncopy(commit->filename, line, strlen(line));
4370                 commit = NULL;
4371         }
4372
4373         return TRUE;
4374 }
4375
4376 static bool
4377 blame_draw(struct view *view, struct line *line, unsigned int lineno)
4378 {
4379         struct blame *blame = line->data;
4380         struct tm *time = NULL;
4381         const char *id = NULL, *author = NULL;
4382         char text[SIZEOF_STR];
4383
4384         if (blame->commit && *blame->commit->filename) {
4385                 id = blame->commit->id;
4386                 author = blame->commit->author;
4387                 time = &blame->commit->time;
4388         }
4389
4390         if (opt_date && draw_date(view, time))
4391                 return TRUE;
4392
4393         if (opt_author && draw_author(view, author))
4394                 return TRUE;
4395
4396         if (draw_field(view, LINE_BLAME_ID, id, ID_COLS, FALSE))
4397                 return TRUE;
4398
4399         if (draw_lineno(view, lineno))
4400                 return TRUE;
4401
4402         string_expand(text, sizeof(text), blame->text, opt_tab_size);
4403         draw_text(view, LINE_DEFAULT, text, TRUE);
4404         return TRUE;
4405 }
4406
4407 static bool
4408 check_blame_commit(struct blame *blame)
4409 {
4410         if (!blame->commit)
4411                 report("Commit data not loaded yet");
4412         else if (!strcmp(blame->commit->id, NULL_ID))
4413                 report("No commit exist for the selected line");
4414         else
4415                 return TRUE;
4416         return FALSE;
4417 }
4418
4419 static void
4420 setup_blame_parent_line(struct view *view, struct blame *blame)
4421 {
4422         const char *diff_tree_argv[] = {
4423                 "git", "diff-tree", "-U0", blame->commit->id,
4424                         "--", blame->commit->filename, NULL
4425         };
4426         struct io io = {};
4427         int parent_lineno = -1;
4428         int blamed_lineno = -1;
4429         char *line;
4430
4431         if (!run_io(&io, diff_tree_argv, NULL, IO_RD))
4432                 return;
4433
4434         while ((line = io_get(&io, '\n', TRUE))) {
4435                 if (*line == '@') {
4436                         char *pos = strchr(line, '+');
4437
4438                         parent_lineno = atoi(line + 4);
4439                         if (pos)
4440                                 blamed_lineno = atoi(pos + 1);
4441
4442                 } else if (*line == '+' && parent_lineno != -1) {
4443                         if (blame->lineno == blamed_lineno - 1 &&
4444                             !strcmp(blame->text, line + 1)) {
4445                                 view->lineno = parent_lineno ? parent_lineno - 1 : 0;
4446                                 break;
4447                         }
4448                         blamed_lineno++;
4449                 }
4450         }
4451
4452         done_io(&io);
4453 }
4454
4455 static enum request
4456 blame_request(struct view *view, enum request request, struct line *line)
4457 {
4458         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4459         struct blame *blame = line->data;
4460
4461         switch (request) {
4462         case REQ_VIEW_BLAME:
4463                 if (check_blame_commit(blame)) {
4464                         string_copy(opt_ref, blame->commit->id);
4465                         string_copy(opt_file, blame->commit->filename);
4466                         if (blame->lineno)
4467                                 view->lineno = blame->lineno;
4468                         open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
4469                 }
4470                 break;
4471
4472         case REQ_PARENT:
4473                 if (check_blame_commit(blame) &&
4474                     select_commit_parent(blame->commit->id, opt_ref,
4475                                          blame->commit->filename)) {
4476                         string_copy(opt_file, blame->commit->filename);
4477                         setup_blame_parent_line(view, blame);
4478                         open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
4479                 }
4480                 break;
4481
4482         case REQ_ENTER:
4483                 if (!blame->commit) {
4484                         report("No commit loaded yet");
4485                         break;
4486                 }
4487
4488                 if (view_is_displayed(VIEW(REQ_VIEW_DIFF)) &&
4489                     !strcmp(blame->commit->id, VIEW(REQ_VIEW_DIFF)->ref))
4490                         break;
4491
4492                 if (!strcmp(blame->commit->id, NULL_ID)) {
4493                         struct view *diff = VIEW(REQ_VIEW_DIFF);
4494                         const char *diff_index_argv[] = {
4495                                 "git", "diff-index", "--root", "--patch-with-stat",
4496                                         "-C", "-M", "HEAD", "--", view->vid, NULL
4497                         };
4498
4499                         if (!blame->commit->has_previous) {
4500                                 diff_index_argv[1] = "diff";
4501                                 diff_index_argv[2] = "--no-color";
4502                                 diff_index_argv[6] = "--";
4503                                 diff_index_argv[7] = "/dev/null";
4504                         }
4505
4506                         if (!prepare_update(diff, diff_index_argv, NULL, FORMAT_DASH)) {
4507                                 report("Failed to allocate diff command");
4508                                 break;
4509                         }
4510                         flags |= OPEN_PREPARED;
4511                 }
4512
4513                 open_view(view, REQ_VIEW_DIFF, flags);
4514                 if (VIEW(REQ_VIEW_DIFF)->pipe && !strcmp(blame->commit->id, NULL_ID))
4515                         string_copy_rev(VIEW(REQ_VIEW_DIFF)->ref, NULL_ID);
4516                 break;
4517
4518         default:
4519                 return request;
4520         }
4521
4522         return REQ_NONE;
4523 }
4524
4525 static bool
4526 blame_grep(struct view *view, struct line *line)
4527 {
4528         struct blame *blame = line->data;
4529         struct blame_commit *commit = blame->commit;
4530         regmatch_t pmatch;
4531
4532 #define MATCH(text, on)                                                 \
4533         (on && *text && regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4534
4535         if (commit) {
4536                 char buf[DATE_COLS + 1];
4537
4538                 if (MATCH(commit->title, 1) ||
4539                     MATCH(commit->author, opt_author) ||
4540                     MATCH(commit->id, opt_date))
4541                         return TRUE;
4542
4543                 if (strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time) &&
4544                     MATCH(buf, 1))
4545                         return TRUE;
4546         }
4547
4548         return MATCH(blame->text, 1);
4549
4550 #undef MATCH
4551 }
4552
4553 static void
4554 blame_select(struct view *view, struct line *line)
4555 {
4556         struct blame *blame = line->data;
4557         struct blame_commit *commit = blame->commit;
4558
4559         if (!commit)
4560                 return;
4561
4562         if (!strcmp(commit->id, NULL_ID))
4563                 string_ncopy(ref_commit, "HEAD", 4);
4564         else
4565                 string_copy_rev(ref_commit, commit->id);
4566 }
4567
4568 static struct view_ops blame_ops = {
4569         "line",
4570         NULL,
4571         blame_open,
4572         blame_read,
4573         blame_draw,
4574         blame_request,
4575         blame_grep,
4576         blame_select,
4577 };
4578
4579 /*
4580  * Status backend
4581  */
4582
4583 struct status {
4584         char status;
4585         struct {
4586                 mode_t mode;
4587                 char rev[SIZEOF_REV];
4588                 char name[SIZEOF_STR];
4589         } old;
4590         struct {
4591                 mode_t mode;
4592                 char rev[SIZEOF_REV];
4593                 char name[SIZEOF_STR];
4594         } new;
4595 };
4596
4597 static char status_onbranch[SIZEOF_STR];
4598 static struct status stage_status;
4599 static enum line_type stage_line_type;
4600 static size_t stage_chunks;
4601 static int *stage_chunk;
4602
4603 /* This should work even for the "On branch" line. */
4604 static inline bool
4605 status_has_none(struct view *view, struct line *line)
4606 {
4607         return line < view->line + view->lines && !line[1].data;
4608 }
4609
4610 /* Get fields from the diff line:
4611  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
4612  */
4613 static inline bool
4614 status_get_diff(struct status *file, const char *buf, size_t bufsize)
4615 {
4616         const char *old_mode = buf +  1;
4617         const char *new_mode = buf +  8;
4618         const char *old_rev  = buf + 15;
4619         const char *new_rev  = buf + 56;
4620         const char *status   = buf + 97;
4621
4622         if (bufsize < 98 ||
4623             old_mode[-1] != ':' ||
4624             new_mode[-1] != ' ' ||
4625             old_rev[-1]  != ' ' ||
4626             new_rev[-1]  != ' ' ||
4627             status[-1]   != ' ')
4628                 return FALSE;
4629
4630         file->status = *status;
4631
4632         string_copy_rev(file->old.rev, old_rev);
4633         string_copy_rev(file->new.rev, new_rev);
4634
4635         file->old.mode = strtoul(old_mode, NULL, 8);
4636         file->new.mode = strtoul(new_mode, NULL, 8);
4637
4638         file->old.name[0] = file->new.name[0] = 0;
4639
4640         return TRUE;
4641 }
4642
4643 static bool
4644 status_run(struct view *view, const char *argv[], char status, enum line_type type)
4645 {
4646         struct status *unmerged = NULL;
4647         char *buf;
4648         struct io io = {};
4649
4650         if (!run_io(&io, argv, NULL, IO_RD))
4651                 return FALSE;
4652
4653         add_line_data(view, NULL, type);
4654
4655         while ((buf = io_get(&io, 0, TRUE))) {
4656                 struct status *file = unmerged;
4657
4658                 if (!file) {
4659                         file = calloc(1, sizeof(*file));
4660                         if (!file || !add_line_data(view, file, type))
4661                                 goto error_out;
4662                 }
4663
4664                 /* Parse diff info part. */
4665                 if (status) {
4666                         file->status = status;
4667                         if (status == 'A')
4668                                 string_copy(file->old.rev, NULL_ID);
4669
4670                 } else if (!file->status || file == unmerged) {
4671                         if (!status_get_diff(file, buf, strlen(buf)))
4672                                 goto error_out;
4673
4674                         buf = io_get(&io, 0, TRUE);
4675                         if (!buf)
4676                                 break;
4677
4678                         /* Collapse all modified entries that follow an
4679                          * associated unmerged entry. */
4680                         if (unmerged == file) {
4681                                 unmerged->status = 'U';
4682                                 unmerged = NULL;
4683                         } else if (file->status == 'U') {
4684                                 unmerged = file;
4685                         }
4686                 }
4687
4688                 /* Grab the old name for rename/copy. */
4689                 if (!*file->old.name &&
4690                     (file->status == 'R' || file->status == 'C')) {
4691                         string_ncopy(file->old.name, buf, strlen(buf));
4692
4693                         buf = io_get(&io, 0, TRUE);
4694                         if (!buf)
4695                                 break;
4696                 }
4697
4698                 /* git-ls-files just delivers a NUL separated list of
4699                  * file names similar to the second half of the
4700                  * git-diff-* output. */
4701                 string_ncopy(file->new.name, buf, strlen(buf));
4702                 if (!*file->old.name)
4703                         string_copy(file->old.name, file->new.name);
4704                 file = NULL;
4705         }
4706
4707         if (io_error(&io)) {
4708 error_out:
4709                 done_io(&io);
4710                 return FALSE;
4711         }
4712
4713         if (!view->line[view->lines - 1].data)
4714                 add_line_data(view, NULL, LINE_STAT_NONE);
4715
4716         done_io(&io);
4717         return TRUE;
4718 }
4719
4720 /* Don't show unmerged entries in the staged section. */
4721 static const char *status_diff_index_argv[] = {
4722         "git", "diff-index", "-z", "--diff-filter=ACDMRTXB",
4723                              "--cached", "-M", "HEAD", NULL
4724 };
4725
4726 static const char *status_diff_files_argv[] = {
4727         "git", "diff-files", "-z", NULL
4728 };
4729
4730 static const char *status_list_other_argv[] = {
4731         "git", "ls-files", "-z", "--others", "--exclude-standard", NULL
4732 };
4733
4734 static const char *status_list_no_head_argv[] = {
4735         "git", "ls-files", "-z", "--cached", "--exclude-standard", NULL
4736 };
4737
4738 static const char *update_index_argv[] = {
4739         "git", "update-index", "-q", "--unmerged", "--refresh", NULL
4740 };
4741
4742 /* Restore the previous line number to stay in the context or select a
4743  * line with something that can be updated. */
4744 static void
4745 status_restore(struct view *view)
4746 {
4747         if (view->p_lineno >= view->lines)
4748                 view->p_lineno = view->lines - 1;
4749         while (view->p_lineno < view->lines && !view->line[view->p_lineno].data)
4750                 view->p_lineno++;
4751         while (view->p_lineno > 0 && !view->line[view->p_lineno].data)
4752                 view->p_lineno--;
4753
4754         /* If the above fails, always skip the "On branch" line. */
4755         if (view->p_lineno < view->lines)
4756                 view->lineno = view->p_lineno;
4757         else
4758                 view->lineno = 1;
4759
4760         if (view->lineno < view->offset)
4761                 view->offset = view->lineno;
4762         else if (view->offset + view->height <= view->lineno)
4763                 view->offset = view->lineno - view->height + 1;
4764
4765         view->p_restore = FALSE;
4766 }
4767
4768 static void
4769 status_update_onbranch(void)
4770 {
4771         static const char *paths[][2] = {
4772                 { "rebase-apply/rebasing",      "Rebasing" },
4773                 { "rebase-apply/applying",      "Applying mailbox" },
4774                 { "rebase-apply/",              "Rebasing mailbox" },
4775                 { "rebase-merge/interactive",   "Interactive rebase" },
4776                 { "rebase-merge/",              "Rebase merge" },
4777                 { "MERGE_HEAD",                 "Merging" },
4778                 { "BISECT_LOG",                 "Bisecting" },
4779                 { "HEAD",                       "On branch" },
4780         };
4781         char buf[SIZEOF_STR];
4782         struct stat stat;
4783         int i;
4784
4785         if (is_initial_commit()) {
4786                 string_copy(status_onbranch, "Initial commit");
4787                 return;
4788         }
4789
4790         for (i = 0; i < ARRAY_SIZE(paths); i++) {
4791                 char *head = opt_head;
4792
4793                 if (!string_format(buf, "%s/%s", opt_git_dir, paths[i][0]) ||
4794                     lstat(buf, &stat) < 0)
4795                         continue;
4796
4797                 if (!*opt_head) {
4798                         struct io io = {};
4799
4800                         if (string_format(buf, "%s/rebase-merge/head-name", opt_git_dir) &&
4801                             io_open(&io, buf) &&
4802                             io_read_buf(&io, buf, sizeof(buf))) {
4803                                 head = chomp_string(buf);
4804                                 if (!prefixcmp(head, "refs/heads/"))
4805                                         head += STRING_SIZE("refs/heads/");
4806                         }
4807                 }
4808
4809                 if (!string_format(status_onbranch, "%s %s", paths[i][1], head))
4810                         string_copy(status_onbranch, opt_head);
4811                 return;
4812         }
4813
4814         string_copy(status_onbranch, "Not currently on any branch");
4815 }
4816
4817 /* First parse staged info using git-diff-index(1), then parse unstaged
4818  * info using git-diff-files(1), and finally untracked files using
4819  * git-ls-files(1). */
4820 static bool
4821 status_open(struct view *view)
4822 {
4823         reset_view(view);
4824
4825         add_line_data(view, NULL, LINE_STAT_HEAD);
4826         status_update_onbranch();
4827
4828         run_io_bg(update_index_argv);
4829
4830         if (is_initial_commit()) {
4831                 if (!status_run(view, status_list_no_head_argv, 'A', LINE_STAT_STAGED))
4832                         return FALSE;
4833         } else if (!status_run(view, status_diff_index_argv, 0, LINE_STAT_STAGED)) {
4834                 return FALSE;
4835         }
4836
4837         if (!status_run(view, status_diff_files_argv, 0, LINE_STAT_UNSTAGED) ||
4838             !status_run(view, status_list_other_argv, '?', LINE_STAT_UNTRACKED))
4839                 return FALSE;
4840
4841         /* Restore the exact position or use the specialized restore
4842          * mode? */
4843         if (!view->p_restore)
4844                 status_restore(view);
4845         return TRUE;
4846 }
4847
4848 static bool
4849 status_draw(struct view *view, struct line *line, unsigned int lineno)
4850 {
4851         struct status *status = line->data;
4852         enum line_type type;
4853         const char *text;
4854
4855         if (!status) {
4856                 switch (line->type) {
4857                 case LINE_STAT_STAGED:
4858                         type = LINE_STAT_SECTION;
4859                         text = "Changes to be committed:";
4860                         break;
4861
4862                 case LINE_STAT_UNSTAGED:
4863                         type = LINE_STAT_SECTION;
4864                         text = "Changed but not updated:";
4865                         break;
4866
4867                 case LINE_STAT_UNTRACKED:
4868                         type = LINE_STAT_SECTION;
4869                         text = "Untracked files:";
4870                         break;
4871
4872                 case LINE_STAT_NONE:
4873                         type = LINE_DEFAULT;
4874                         text = "  (no files)";
4875                         break;
4876
4877                 case LINE_STAT_HEAD:
4878                         type = LINE_STAT_HEAD;
4879                         text = status_onbranch;
4880                         break;
4881
4882                 default:
4883                         return FALSE;
4884                 }
4885         } else {
4886                 static char buf[] = { '?', ' ', ' ', ' ', 0 };
4887
4888                 buf[0] = status->status;
4889                 if (draw_text(view, line->type, buf, TRUE))
4890                         return TRUE;
4891                 type = LINE_DEFAULT;
4892                 text = status->new.name;
4893         }
4894
4895         draw_text(view, type, text, TRUE);
4896         return TRUE;
4897 }
4898
4899 static enum request
4900 status_enter(struct view *view, struct line *line)
4901 {
4902         struct status *status = line->data;
4903         const char *oldpath = status ? status->old.name : NULL;
4904         /* Diffs for unmerged entries are empty when passing the new
4905          * path, so leave it empty. */
4906         const char *newpath = status && status->status != 'U' ? status->new.name : NULL;
4907         const char *info;
4908         enum open_flags split;
4909         struct view *stage = VIEW(REQ_VIEW_STAGE);
4910
4911         if (line->type == LINE_STAT_NONE ||
4912             (!status && line[1].type == LINE_STAT_NONE)) {
4913                 report("No file to diff");
4914                 return REQ_NONE;
4915         }
4916
4917         switch (line->type) {
4918         case LINE_STAT_STAGED:
4919                 if (is_initial_commit()) {
4920                         const char *no_head_diff_argv[] = {
4921                                 "git", "diff", "--no-color", "--patch-with-stat",
4922                                         "--", "/dev/null", newpath, NULL
4923                         };
4924
4925                         if (!prepare_update(stage, no_head_diff_argv, opt_cdup, FORMAT_DASH))
4926                                 return REQ_QUIT;
4927                 } else {
4928                         const char *index_show_argv[] = {
4929                                 "git", "diff-index", "--root", "--patch-with-stat",
4930                                         "-C", "-M", "--cached", "HEAD", "--",
4931                                         oldpath, newpath, NULL
4932                         };
4933
4934                         if (!prepare_update(stage, index_show_argv, opt_cdup, FORMAT_DASH))
4935                                 return REQ_QUIT;
4936                 }
4937
4938                 if (status)
4939                         info = "Staged changes to %s";
4940                 else
4941                         info = "Staged changes";
4942                 break;
4943
4944         case LINE_STAT_UNSTAGED:
4945         {
4946                 const char *files_show_argv[] = {
4947                         "git", "diff-files", "--root", "--patch-with-stat",
4948                                 "-C", "-M", "--", oldpath, newpath, NULL
4949                 };
4950
4951                 if (!prepare_update(stage, files_show_argv, opt_cdup, FORMAT_DASH))
4952                         return REQ_QUIT;
4953                 if (status)
4954                         info = "Unstaged changes to %s";
4955                 else
4956                         info = "Unstaged changes";
4957                 break;
4958         }
4959         case LINE_STAT_UNTRACKED:
4960                 if (!newpath) {
4961                         report("No file to show");
4962                         return REQ_NONE;
4963                 }
4964
4965                 if (!suffixcmp(status->new.name, -1, "/")) {
4966                         report("Cannot display a directory");
4967                         return REQ_NONE;
4968                 }
4969
4970                 if (!prepare_update_file(stage, newpath))
4971                         return REQ_QUIT;
4972                 info = "Untracked file %s";
4973                 break;
4974
4975         case LINE_STAT_HEAD:
4976                 return REQ_NONE;
4977
4978         default:
4979                 die("line type %d not handled in switch", line->type);
4980         }
4981
4982         split = view_is_displayed(view) ? OPEN_SPLIT : 0;
4983         open_view(view, REQ_VIEW_STAGE, OPEN_PREPARED | split);
4984         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
4985                 if (status) {
4986                         stage_status = *status;
4987                 } else {
4988                         memset(&stage_status, 0, sizeof(stage_status));
4989                 }
4990
4991                 stage_line_type = line->type;
4992                 stage_chunks = 0;
4993                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
4994         }
4995
4996         return REQ_NONE;
4997 }
4998
4999 static bool
5000 status_exists(struct status *status, enum line_type type)
5001 {
5002         struct view *view = VIEW(REQ_VIEW_STATUS);
5003         unsigned long lineno;
5004
5005         for (lineno = 0; lineno < view->lines; lineno++) {
5006                 struct line *line = &view->line[lineno];
5007                 struct status *pos = line->data;
5008
5009                 if (line->type != type)
5010                         continue;
5011                 if (!pos && (!status || !status->status) && line[1].data) {
5012                         select_view_line(view, lineno);
5013                         return TRUE;
5014                 }
5015                 if (pos && !strcmp(status->new.name, pos->new.name)) {
5016                         select_view_line(view, lineno);
5017                         return TRUE;
5018                 }
5019         }
5020
5021         return FALSE;
5022 }
5023
5024
5025 static bool
5026 status_update_prepare(struct io *io, enum line_type type)
5027 {
5028         const char *staged_argv[] = {
5029                 "git", "update-index", "-z", "--index-info", NULL
5030         };
5031         const char *others_argv[] = {
5032                 "git", "update-index", "-z", "--add", "--remove", "--stdin", NULL
5033         };
5034
5035         switch (type) {
5036         case LINE_STAT_STAGED:
5037                 return run_io(io, staged_argv, opt_cdup, IO_WR);
5038
5039         case LINE_STAT_UNSTAGED:
5040                 return run_io(io, others_argv, opt_cdup, IO_WR);
5041
5042         case LINE_STAT_UNTRACKED:
5043                 return run_io(io, others_argv, NULL, IO_WR);
5044
5045         default:
5046                 die("line type %d not handled in switch", type);
5047                 return FALSE;
5048         }
5049 }
5050
5051 static bool
5052 status_update_write(struct io *io, struct status *status, enum line_type type)
5053 {
5054         char buf[SIZEOF_STR];
5055         size_t bufsize = 0;
5056
5057         switch (type) {
5058         case LINE_STAT_STAGED:
5059                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
5060                                         status->old.mode,
5061                                         status->old.rev,
5062                                         status->old.name, 0))
5063                         return FALSE;
5064                 break;
5065
5066         case LINE_STAT_UNSTAGED:
5067         case LINE_STAT_UNTRACKED:
5068                 if (!string_format_from(buf, &bufsize, "%s%c", status->new.name, 0))
5069                         return FALSE;
5070                 break;
5071
5072         default:
5073                 die("line type %d not handled in switch", type);
5074         }
5075
5076         return io_write(io, buf, bufsize);
5077 }
5078
5079 static bool
5080 status_update_file(struct status *status, enum line_type type)
5081 {
5082         struct io io = {};
5083         bool result;
5084
5085         if (!status_update_prepare(&io, type))
5086                 return FALSE;
5087
5088         result = status_update_write(&io, status, type);
5089         done_io(&io);
5090         return result;
5091 }
5092
5093 static bool
5094 status_update_files(struct view *view, struct line *line)
5095 {
5096         struct io io = {};
5097         bool result = TRUE;
5098         struct line *pos = view->line + view->lines;
5099         int files = 0;
5100         int file, done;
5101
5102         if (!status_update_prepare(&io, line->type))
5103                 return FALSE;
5104
5105         for (pos = line; pos < view->line + view->lines && pos->data; pos++)
5106                 files++;
5107
5108         for (file = 0, done = 0; result && file < files; line++, file++) {
5109                 int almost_done = file * 100 / files;
5110
5111                 if (almost_done > done) {
5112                         done = almost_done;
5113                         string_format(view->ref, "updating file %u of %u (%d%% done)",
5114                                       file, files, done);
5115                         update_view_title(view);
5116                 }
5117                 result = status_update_write(&io, line->data, line->type);
5118         }
5119
5120         done_io(&io);
5121         return result;
5122 }
5123
5124 static bool
5125 status_update(struct view *view)
5126 {
5127         struct line *line = &view->line[view->lineno];
5128
5129         assert(view->lines);
5130
5131         if (!line->data) {
5132                 /* This should work even for the "On branch" line. */
5133                 if (line < view->line + view->lines && !line[1].data) {
5134                         report("Nothing to update");
5135                         return FALSE;
5136                 }
5137
5138                 if (!status_update_files(view, line + 1)) {
5139                         report("Failed to update file status");
5140                         return FALSE;
5141                 }
5142
5143         } else if (!status_update_file(line->data, line->type)) {
5144                 report("Failed to update file status");
5145                 return FALSE;
5146         }
5147
5148         return TRUE;
5149 }
5150
5151 static bool
5152 status_revert(struct status *status, enum line_type type, bool has_none)
5153 {
5154         if (!status || type != LINE_STAT_UNSTAGED) {
5155                 if (type == LINE_STAT_STAGED) {
5156                         report("Cannot revert changes to staged files");
5157                 } else if (type == LINE_STAT_UNTRACKED) {
5158                         report("Cannot revert changes to untracked files");
5159                 } else if (has_none) {
5160                         report("Nothing to revert");
5161                 } else {
5162                         report("Cannot revert changes to multiple files");
5163                 }
5164                 return FALSE;
5165
5166         } else {
5167                 char mode[10] = "100644";
5168                 const char *reset_argv[] = {
5169                         "git", "update-index", "--cacheinfo", mode,
5170                                 status->old.rev, status->old.name, NULL
5171                 };
5172                 const char *checkout_argv[] = {
5173                         "git", "checkout", "--", status->old.name, NULL
5174                 };
5175
5176                 if (!prompt_yesno("Are you sure you want to overwrite any changes?"))
5177                         return FALSE;
5178                 string_format(mode, "%o", status->old.mode);
5179                 return (status->status != 'U' || run_io_fg(reset_argv, opt_cdup)) &&
5180                         run_io_fg(checkout_argv, opt_cdup);
5181         }
5182 }
5183
5184 static enum request
5185 status_request(struct view *view, enum request request, struct line *line)
5186 {
5187         struct status *status = line->data;
5188
5189         switch (request) {
5190         case REQ_STATUS_UPDATE:
5191                 if (!status_update(view))
5192                         return REQ_NONE;
5193                 break;
5194
5195         case REQ_STATUS_REVERT:
5196                 if (!status_revert(status, line->type, status_has_none(view, line)))
5197                         return REQ_NONE;
5198                 break;
5199
5200         case REQ_STATUS_MERGE:
5201                 if (!status || status->status != 'U') {
5202                         report("Merging only possible for files with unmerged status ('U').");
5203                         return REQ_NONE;
5204                 }
5205                 open_mergetool(status->new.name);
5206                 break;
5207
5208         case REQ_EDIT:
5209                 if (!status)
5210                         return request;
5211                 if (status->status == 'D') {
5212                         report("File has been deleted.");
5213                         return REQ_NONE;
5214                 }
5215
5216                 open_editor(status->status != '?', status->new.name);
5217                 break;
5218
5219         case REQ_VIEW_BLAME:
5220                 if (status) {
5221                         string_copy(opt_file, status->new.name);
5222                         opt_ref[0] = 0;
5223                 }
5224                 return request;
5225
5226         case REQ_ENTER:
5227                 /* After returning the status view has been split to
5228                  * show the stage view. No further reloading is
5229                  * necessary. */
5230                 status_enter(view, line);
5231                 return REQ_NONE;
5232
5233         case REQ_REFRESH:
5234                 /* Simply reload the view. */
5235                 break;
5236
5237         default:
5238                 return request;
5239         }
5240
5241         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
5242
5243         return REQ_NONE;
5244 }
5245
5246 static void
5247 status_select(struct view *view, struct line *line)
5248 {
5249         struct status *status = line->data;
5250         char file[SIZEOF_STR] = "all files";
5251         const char *text;
5252         const char *key;
5253
5254         if (status && !string_format(file, "'%s'", status->new.name))
5255                 return;
5256
5257         if (!status && line[1].type == LINE_STAT_NONE)
5258                 line++;
5259
5260         switch (line->type) {
5261         case LINE_STAT_STAGED:
5262                 text = "Press %s to unstage %s for commit";
5263                 break;
5264
5265         case LINE_STAT_UNSTAGED:
5266                 text = "Press %s to stage %s for commit";
5267                 break;
5268
5269         case LINE_STAT_UNTRACKED:
5270                 text = "Press %s to stage %s for addition";
5271                 break;
5272
5273         case LINE_STAT_HEAD:
5274         case LINE_STAT_NONE:
5275                 text = "Nothing to update";
5276                 break;
5277
5278         default:
5279                 die("line type %d not handled in switch", line->type);
5280         }
5281
5282         if (status && status->status == 'U') {
5283                 text = "Press %s to resolve conflict in %s";
5284                 key = get_key(REQ_STATUS_MERGE);
5285
5286         } else {
5287                 key = get_key(REQ_STATUS_UPDATE);
5288         }
5289
5290         string_format(view->ref, text, key, file);
5291 }
5292
5293 static bool
5294 status_grep(struct view *view, struct line *line)
5295 {
5296         struct status *status = line->data;
5297         enum { S_STATUS, S_NAME, S_END } state;
5298         char buf[2] = "?";
5299         regmatch_t pmatch;
5300
5301         if (!status)
5302                 return FALSE;
5303
5304         for (state = S_STATUS; state < S_END; state++) {
5305                 const char *text;
5306
5307                 switch (state) {
5308                 case S_NAME:    text = status->new.name;        break;
5309                 case S_STATUS:
5310                         buf[0] = status->status;
5311                         text = buf;
5312                         break;
5313
5314                 default:
5315                         return FALSE;
5316                 }
5317
5318                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5319                         return TRUE;
5320         }
5321
5322         return FALSE;
5323 }
5324
5325 static struct view_ops status_ops = {
5326         "file",
5327         NULL,
5328         status_open,
5329         NULL,
5330         status_draw,
5331         status_request,
5332         status_grep,
5333         status_select,
5334 };
5335
5336
5337 static bool
5338 stage_diff_write(struct io *io, struct line *line, struct line *end)
5339 {
5340         while (line < end) {
5341                 if (!io_write(io, line->data, strlen(line->data)) ||
5342                     !io_write(io, "\n", 1))
5343                         return FALSE;
5344                 line++;
5345                 if (line->type == LINE_DIFF_CHUNK ||
5346                     line->type == LINE_DIFF_HEADER)
5347                         break;
5348         }
5349
5350         return TRUE;
5351 }
5352
5353 static struct line *
5354 stage_diff_find(struct view *view, struct line *line, enum line_type type)
5355 {
5356         for (; view->line < line; line--)
5357                 if (line->type == type)
5358                         return line;
5359
5360         return NULL;
5361 }
5362
5363 static bool
5364 stage_apply_chunk(struct view *view, struct line *chunk, bool revert)
5365 {
5366         const char *apply_argv[SIZEOF_ARG] = {
5367                 "git", "apply", "--whitespace=nowarn", NULL
5368         };
5369         struct line *diff_hdr;
5370         struct io io = {};
5371         int argc = 3;
5372
5373         diff_hdr = stage_diff_find(view, chunk, LINE_DIFF_HEADER);
5374         if (!diff_hdr)
5375                 return FALSE;
5376
5377         if (!revert)
5378                 apply_argv[argc++] = "--cached";
5379         if (revert || stage_line_type == LINE_STAT_STAGED)
5380                 apply_argv[argc++] = "-R";
5381         apply_argv[argc++] = "-";
5382         apply_argv[argc++] = NULL;
5383         if (!run_io(&io, apply_argv, opt_cdup, IO_WR))
5384                 return FALSE;
5385
5386         if (!stage_diff_write(&io, diff_hdr, chunk) ||
5387             !stage_diff_write(&io, chunk, view->line + view->lines))
5388                 chunk = NULL;
5389
5390         done_io(&io);
5391         run_io_bg(update_index_argv);
5392
5393         return chunk ? TRUE : FALSE;
5394 }
5395
5396 static bool
5397 stage_update(struct view *view, struct line *line)
5398 {
5399         struct line *chunk = NULL;
5400
5401         if (!is_initial_commit() && stage_line_type != LINE_STAT_UNTRACKED)
5402                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
5403
5404         if (chunk) {
5405                 if (!stage_apply_chunk(view, chunk, FALSE)) {
5406                         report("Failed to apply chunk");
5407                         return FALSE;
5408                 }
5409
5410         } else if (!stage_status.status) {
5411                 view = VIEW(REQ_VIEW_STATUS);
5412
5413                 for (line = view->line; line < view->line + view->lines; line++)
5414                         if (line->type == stage_line_type)
5415                                 break;
5416
5417                 if (!status_update_files(view, line + 1)) {
5418                         report("Failed to update files");
5419                         return FALSE;
5420                 }
5421
5422         } else if (!status_update_file(&stage_status, stage_line_type)) {
5423                 report("Failed to update file");
5424                 return FALSE;
5425         }
5426
5427         return TRUE;
5428 }
5429
5430 static bool
5431 stage_revert(struct view *view, struct line *line)
5432 {
5433         struct line *chunk = NULL;
5434
5435         if (!is_initial_commit() && stage_line_type == LINE_STAT_UNSTAGED)
5436                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
5437
5438         if (chunk) {
5439                 if (!prompt_yesno("Are you sure you want to revert changes?"))
5440                         return FALSE;
5441
5442                 if (!stage_apply_chunk(view, chunk, TRUE)) {
5443                         report("Failed to revert chunk");
5444                         return FALSE;
5445                 }
5446                 return TRUE;
5447
5448         } else {
5449                 return status_revert(stage_status.status ? &stage_status : NULL,
5450                                      stage_line_type, FALSE);
5451         }
5452 }
5453
5454
5455 static void
5456 stage_next(struct view *view, struct line *line)
5457 {
5458         int i;
5459
5460         if (!stage_chunks) {
5461                 static size_t alloc = 0;
5462                 int *tmp;
5463
5464                 for (line = view->line; line < view->line + view->lines; line++) {
5465                         if (line->type != LINE_DIFF_CHUNK)
5466                                 continue;
5467
5468                         tmp = realloc_items(stage_chunk, &alloc,
5469                                             stage_chunks, sizeof(*tmp));
5470                         if (!tmp) {
5471                                 report("Allocation failure");
5472                                 return;
5473                         }
5474
5475                         stage_chunk = tmp;
5476                         stage_chunk[stage_chunks++] = line - view->line;
5477                 }
5478         }
5479
5480         for (i = 0; i < stage_chunks; i++) {
5481                 if (stage_chunk[i] > view->lineno) {
5482                         do_scroll_view(view, stage_chunk[i] - view->lineno);
5483                         report("Chunk %d of %d", i + 1, stage_chunks);
5484                         return;
5485                 }
5486         }
5487
5488         report("No next chunk found");
5489 }
5490
5491 static enum request
5492 stage_request(struct view *view, enum request request, struct line *line)
5493 {
5494         switch (request) {
5495         case REQ_STATUS_UPDATE:
5496                 if (!stage_update(view, line))
5497                         return REQ_NONE;
5498                 break;
5499
5500         case REQ_STATUS_REVERT:
5501                 if (!stage_revert(view, line))
5502                         return REQ_NONE;
5503                 break;
5504
5505         case REQ_STAGE_NEXT:
5506                 if (stage_line_type == LINE_STAT_UNTRACKED) {
5507                         report("File is untracked; press %s to add",
5508                                get_key(REQ_STATUS_UPDATE));
5509                         return REQ_NONE;
5510                 }
5511                 stage_next(view, line);
5512                 return REQ_NONE;
5513
5514         case REQ_EDIT:
5515                 if (!stage_status.new.name[0])
5516                         return request;
5517                 if (stage_status.status == 'D') {
5518                         report("File has been deleted.");
5519                         return REQ_NONE;
5520                 }
5521
5522                 open_editor(stage_status.status != '?', stage_status.new.name);
5523                 break;
5524
5525         case REQ_REFRESH:
5526                 /* Reload everything ... */
5527                 break;
5528
5529         case REQ_VIEW_BLAME:
5530                 if (stage_status.new.name[0]) {
5531                         string_copy(opt_file, stage_status.new.name);
5532                         opt_ref[0] = 0;
5533                 }
5534                 return request;
5535
5536         case REQ_ENTER:
5537                 return pager_request(view, request, line);
5538
5539         default:
5540                 return request;
5541         }
5542
5543         VIEW(REQ_VIEW_STATUS)->p_restore = TRUE;
5544         open_view(view, REQ_VIEW_STATUS, OPEN_REFRESH);
5545
5546         /* Check whether the staged entry still exists, and close the
5547          * stage view if it doesn't. */
5548         if (!status_exists(&stage_status, stage_line_type)) {
5549                 status_restore(VIEW(REQ_VIEW_STATUS));
5550                 return REQ_VIEW_CLOSE;
5551         }
5552
5553         if (stage_line_type == LINE_STAT_UNTRACKED) {
5554                 if (!suffixcmp(stage_status.new.name, -1, "/")) {
5555                         report("Cannot display a directory");
5556                         return REQ_NONE;
5557                 }
5558
5559                 if (!prepare_update_file(view, stage_status.new.name)) {
5560                         report("Failed to open file: %s", strerror(errno));
5561                         return REQ_NONE;
5562                 }
5563         }
5564         open_view(view, REQ_VIEW_STAGE, OPEN_REFRESH);
5565
5566         return REQ_NONE;
5567 }
5568
5569 static struct view_ops stage_ops = {
5570         "line",
5571         NULL,
5572         NULL,
5573         pager_read,
5574         pager_draw,
5575         stage_request,
5576         pager_grep,
5577         pager_select,
5578 };
5579
5580
5581 /*
5582  * Revision graph
5583  */
5584
5585 struct commit {
5586         char id[SIZEOF_REV];            /* SHA1 ID. */
5587         char title[128];                /* First line of the commit message. */
5588         char author[75];                /* Author of the commit. */
5589         struct tm time;                 /* Date from the author ident. */
5590         struct ref **refs;              /* Repository references. */
5591         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
5592         size_t graph_size;              /* The width of the graph array. */
5593         bool has_parents;               /* Rewritten --parents seen. */
5594 };
5595
5596 /* Size of rev graph with no  "padding" columns */
5597 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
5598
5599 struct rev_graph {
5600         struct rev_graph *prev, *next, *parents;
5601         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
5602         size_t size;
5603         struct commit *commit;
5604         size_t pos;
5605         unsigned int boundary:1;
5606 };
5607
5608 /* Parents of the commit being visualized. */
5609 static struct rev_graph graph_parents[4];
5610
5611 /* The current stack of revisions on the graph. */
5612 static struct rev_graph graph_stacks[4] = {
5613         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
5614         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
5615         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
5616         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
5617 };
5618
5619 static inline bool
5620 graph_parent_is_merge(struct rev_graph *graph)
5621 {
5622         return graph->parents->size > 1;
5623 }
5624
5625 static inline void
5626 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
5627 {
5628         struct commit *commit = graph->commit;
5629
5630         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
5631                 commit->graph[commit->graph_size++] = symbol;
5632 }
5633
5634 static void
5635 clear_rev_graph(struct rev_graph *graph)
5636 {
5637         graph->boundary = 0;
5638         graph->size = graph->pos = 0;
5639         graph->commit = NULL;
5640         memset(graph->parents, 0, sizeof(*graph->parents));
5641 }
5642
5643 static void
5644 done_rev_graph(struct rev_graph *graph)
5645 {
5646         if (graph_parent_is_merge(graph) &&
5647             graph->pos < graph->size - 1 &&
5648             graph->next->size == graph->size + graph->parents->size - 1) {
5649                 size_t i = graph->pos + graph->parents->size - 1;
5650
5651                 graph->commit->graph_size = i * 2;
5652                 while (i < graph->next->size - 1) {
5653                         append_to_rev_graph(graph, ' ');
5654                         append_to_rev_graph(graph, '\\');
5655                         i++;
5656                 }
5657         }
5658
5659         clear_rev_graph(graph);
5660 }
5661
5662 static void
5663 push_rev_graph(struct rev_graph *graph, const char *parent)
5664 {
5665         int i;
5666
5667         /* "Collapse" duplicate parents lines.
5668          *
5669          * FIXME: This needs to also update update the drawn graph but
5670          * for now it just serves as a method for pruning graph lines. */
5671         for (i = 0; i < graph->size; i++)
5672                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
5673                         return;
5674
5675         if (graph->size < SIZEOF_REVITEMS) {
5676                 string_copy_rev(graph->rev[graph->size++], parent);
5677         }
5678 }
5679
5680 static chtype
5681 get_rev_graph_symbol(struct rev_graph *graph)
5682 {
5683         chtype symbol;
5684
5685         if (graph->boundary)
5686                 symbol = REVGRAPH_BOUND;
5687         else if (graph->parents->size == 0)
5688                 symbol = REVGRAPH_INIT;
5689         else if (graph_parent_is_merge(graph))
5690                 symbol = REVGRAPH_MERGE;
5691         else if (graph->pos >= graph->size)
5692                 symbol = REVGRAPH_BRANCH;
5693         else
5694                 symbol = REVGRAPH_COMMIT;
5695
5696         return symbol;
5697 }
5698
5699 static void
5700 draw_rev_graph(struct rev_graph *graph)
5701 {
5702         struct rev_filler {
5703                 chtype separator, line;
5704         };
5705         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
5706         static struct rev_filler fillers[] = {
5707                 { ' ',  '|' },
5708                 { '`',  '.' },
5709                 { '\'', ' ' },
5710                 { '/',  ' ' },
5711         };
5712         chtype symbol = get_rev_graph_symbol(graph);
5713         struct rev_filler *filler;
5714         size_t i;
5715
5716         if (opt_line_graphics)
5717                 fillers[DEFAULT].line = line_graphics[LINE_GRAPHIC_VLINE];
5718
5719         filler = &fillers[DEFAULT];
5720
5721         for (i = 0; i < graph->pos; i++) {
5722                 append_to_rev_graph(graph, filler->line);
5723                 if (graph_parent_is_merge(graph->prev) &&
5724                     graph->prev->pos == i)
5725                         filler = &fillers[RSHARP];
5726
5727                 append_to_rev_graph(graph, filler->separator);
5728         }
5729
5730         /* Place the symbol for this revision. */
5731         append_to_rev_graph(graph, symbol);
5732
5733         if (graph->prev->size > graph->size)
5734                 filler = &fillers[RDIAG];
5735         else
5736                 filler = &fillers[DEFAULT];
5737
5738         i++;
5739
5740         for (; i < graph->size; i++) {
5741                 append_to_rev_graph(graph, filler->separator);
5742                 append_to_rev_graph(graph, filler->line);
5743                 if (graph_parent_is_merge(graph->prev) &&
5744                     i < graph->prev->pos + graph->parents->size)
5745                         filler = &fillers[RSHARP];
5746                 if (graph->prev->size > graph->size)
5747                         filler = &fillers[LDIAG];
5748         }
5749
5750         if (graph->prev->size > graph->size) {
5751                 append_to_rev_graph(graph, filler->separator);
5752                 if (filler->line != ' ')
5753                         append_to_rev_graph(graph, filler->line);
5754         }
5755 }
5756
5757 /* Prepare the next rev graph */
5758 static void
5759 prepare_rev_graph(struct rev_graph *graph)
5760 {
5761         size_t i;
5762
5763         /* First, traverse all lines of revisions up to the active one. */
5764         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
5765                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
5766                         break;
5767
5768                 push_rev_graph(graph->next, graph->rev[graph->pos]);
5769         }
5770
5771         /* Interleave the new revision parent(s). */
5772         for (i = 0; !graph->boundary && i < graph->parents->size; i++)
5773                 push_rev_graph(graph->next, graph->parents->rev[i]);
5774
5775         /* Lastly, put any remaining revisions. */
5776         for (i = graph->pos + 1; i < graph->size; i++)
5777                 push_rev_graph(graph->next, graph->rev[i]);
5778 }
5779
5780 static void
5781 update_rev_graph(struct view *view, struct rev_graph *graph)
5782 {
5783         /* If this is the finalizing update ... */
5784         if (graph->commit)
5785                 prepare_rev_graph(graph);
5786
5787         /* Graph visualization needs a one rev look-ahead,
5788          * so the first update doesn't visualize anything. */
5789         if (!graph->prev->commit)
5790                 return;
5791
5792         if (view->lines > 2)
5793                 view->line[view->lines - 3].dirty = 1;
5794         if (view->lines > 1)
5795                 view->line[view->lines - 2].dirty = 1;
5796         draw_rev_graph(graph->prev);
5797         done_rev_graph(graph->prev->prev);
5798 }
5799
5800
5801 /*
5802  * Main view backend
5803  */
5804
5805 static const char *main_argv[SIZEOF_ARG] = {
5806         "git", "log", "--no-color", "--pretty=raw", "--parents",
5807                       "--topo-order", "%(head)", NULL
5808 };
5809
5810 static bool
5811 main_draw(struct view *view, struct line *line, unsigned int lineno)
5812 {
5813         struct commit *commit = line->data;
5814
5815         if (!*commit->author)
5816                 return FALSE;
5817
5818         if (opt_date && draw_date(view, &commit->time))
5819                 return TRUE;
5820
5821         if (opt_author && draw_author(view, commit->author))
5822                 return TRUE;
5823
5824         if (opt_rev_graph && commit->graph_size &&
5825             draw_graphic(view, LINE_MAIN_REVGRAPH, commit->graph, commit->graph_size))
5826                 return TRUE;
5827
5828         if (opt_show_refs && commit->refs) {
5829                 size_t i = 0;
5830
5831                 do {
5832                         enum line_type type;
5833
5834                         if (commit->refs[i]->head)
5835                                 type = LINE_MAIN_HEAD;
5836                         else if (commit->refs[i]->ltag)
5837                                 type = LINE_MAIN_LOCAL_TAG;
5838                         else if (commit->refs[i]->tag)
5839                                 type = LINE_MAIN_TAG;
5840                         else if (commit->refs[i]->tracked)
5841                                 type = LINE_MAIN_TRACKED;
5842                         else if (commit->refs[i]->remote)
5843                                 type = LINE_MAIN_REMOTE;
5844                         else
5845                                 type = LINE_MAIN_REF;
5846
5847                         if (draw_text(view, type, "[", TRUE) ||
5848                             draw_text(view, type, commit->refs[i]->name, TRUE) ||
5849                             draw_text(view, type, "]", TRUE))
5850                                 return TRUE;
5851
5852                         if (draw_text(view, LINE_DEFAULT, " ", TRUE))
5853                                 return TRUE;
5854                 } while (commit->refs[i++]->next);
5855         }
5856
5857         draw_text(view, LINE_DEFAULT, commit->title, TRUE);
5858         return TRUE;
5859 }
5860
5861 /* Reads git log --pretty=raw output and parses it into the commit struct. */
5862 static bool
5863 main_read(struct view *view, char *line)
5864 {
5865         static struct rev_graph *graph = graph_stacks;
5866         enum line_type type;
5867         struct commit *commit;
5868
5869         if (!line) {
5870                 int i;
5871
5872                 if (!view->lines && !view->parent)
5873                         die("No revisions match the given arguments.");
5874                 if (view->lines > 0) {
5875                         commit = view->line[view->lines - 1].data;
5876                         view->line[view->lines - 1].dirty = 1;
5877                         if (!*commit->author) {
5878                                 view->lines--;
5879                                 free(commit);
5880                                 graph->commit = NULL;
5881                         }
5882                 }
5883                 update_rev_graph(view, graph);
5884
5885                 for (i = 0; i < ARRAY_SIZE(graph_stacks); i++)
5886                         clear_rev_graph(&graph_stacks[i]);
5887                 return TRUE;
5888         }
5889
5890         type = get_line_type(line);
5891         if (type == LINE_COMMIT) {
5892                 commit = calloc(1, sizeof(struct commit));
5893                 if (!commit)
5894                         return FALSE;
5895
5896                 line += STRING_SIZE("commit ");
5897                 if (*line == '-') {
5898                         graph->boundary = 1;
5899                         line++;
5900                 }
5901
5902                 string_copy_rev(commit->id, line);
5903                 commit->refs = get_refs(commit->id);
5904                 graph->commit = commit;
5905                 add_line_data(view, commit, LINE_MAIN_COMMIT);
5906
5907                 while ((line = strchr(line, ' '))) {
5908                         line++;
5909                         push_rev_graph(graph->parents, line);
5910                         commit->has_parents = TRUE;
5911                 }
5912                 return TRUE;
5913         }
5914
5915         if (!view->lines)
5916                 return TRUE;
5917         commit = view->line[view->lines - 1].data;
5918
5919         switch (type) {
5920         case LINE_PARENT:
5921                 if (commit->has_parents)
5922                         break;
5923                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
5924                 break;
5925
5926         case LINE_AUTHOR:
5927                 parse_author_line(line + STRING_SIZE("author "),
5928                                   commit->author, sizeof(commit->author),
5929                                   &commit->time);
5930                 update_rev_graph(view, graph);
5931                 graph = graph->next;
5932                 break;
5933
5934         default:
5935                 /* Fill in the commit title if it has not already been set. */
5936                 if (commit->title[0])
5937                         break;
5938
5939                 /* Require titles to start with a non-space character at the
5940                  * offset used by git log. */
5941                 if (strncmp(line, "    ", 4))
5942                         break;
5943                 line += 4;
5944                 /* Well, if the title starts with a whitespace character,
5945                  * try to be forgiving.  Otherwise we end up with no title. */
5946                 while (isspace(*line))
5947                         line++;
5948                 if (*line == '\0')
5949                         break;
5950                 /* FIXME: More graceful handling of titles; append "..." to
5951                  * shortened titles, etc. */
5952
5953                 string_expand(commit->title, sizeof(commit->title), line, 1);
5954                 view->line[view->lines - 1].dirty = 1;
5955         }
5956
5957         return TRUE;
5958 }
5959
5960 static enum request
5961 main_request(struct view *view, enum request request, struct line *line)
5962 {
5963         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
5964
5965         switch (request) {
5966         case REQ_ENTER:
5967                 open_view(view, REQ_VIEW_DIFF, flags);
5968                 break;
5969         case REQ_REFRESH:
5970                 load_refs();
5971                 open_view(view, REQ_VIEW_MAIN, OPEN_REFRESH);
5972                 break;
5973         default:
5974                 return request;
5975         }
5976
5977         return REQ_NONE;
5978 }
5979
5980 static bool
5981 grep_refs(struct ref **refs, regex_t *regex)
5982 {
5983         regmatch_t pmatch;
5984         size_t i = 0;
5985
5986         if (!refs)
5987                 return FALSE;
5988         do {
5989                 if (regexec(regex, refs[i]->name, 1, &pmatch, 0) != REG_NOMATCH)
5990                         return TRUE;
5991         } while (refs[i++]->next);
5992
5993         return FALSE;
5994 }
5995
5996 static bool
5997 main_grep(struct view *view, struct line *line)
5998 {
5999         struct commit *commit = line->data;
6000         enum { S_TITLE, S_AUTHOR, S_DATE, S_REFS, S_END } state;
6001         char buf[DATE_COLS + 1];
6002         regmatch_t pmatch;
6003
6004         for (state = S_TITLE; state < S_END; state++) {
6005                 char *text;
6006
6007                 switch (state) {
6008                 case S_TITLE:   text = commit->title;   break;
6009                 case S_AUTHOR:
6010                         if (!opt_author)
6011                                 continue;
6012                         text = commit->author;
6013                         break;
6014                 case S_DATE:
6015                         if (!opt_date)
6016                                 continue;
6017                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
6018                                 continue;
6019                         text = buf;
6020                         break;
6021                 case S_REFS:
6022                         if (!opt_show_refs)
6023                                 continue;
6024                         if (grep_refs(commit->refs, view->regex) == TRUE)
6025                                 return TRUE;
6026                         continue;
6027                 default:
6028                         return FALSE;
6029                 }
6030
6031                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
6032                         return TRUE;
6033         }
6034
6035         return FALSE;
6036 }
6037
6038 static void
6039 main_select(struct view *view, struct line *line)
6040 {
6041         struct commit *commit = line->data;
6042
6043         string_copy_rev(view->ref, commit->id);
6044         string_copy_rev(ref_commit, view->ref);
6045 }
6046
6047 static struct view_ops main_ops = {
6048         "commit",
6049         main_argv,
6050         NULL,
6051         main_read,
6052         main_draw,
6053         main_request,
6054         main_grep,
6055         main_select,
6056 };
6057
6058
6059 /*
6060  * Unicode / UTF-8 handling
6061  *
6062  * NOTE: Much of the following code for dealing with Unicode is derived from
6063  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
6064  * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
6065  */
6066
6067 static inline int
6068 unicode_width(unsigned long c)
6069 {
6070         if (c >= 0x1100 &&
6071            (c <= 0x115f                         /* Hangul Jamo */
6072             || c == 0x2329
6073             || c == 0x232a
6074             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
6075                                                 /* CJK ... Yi */
6076             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
6077             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
6078             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
6079             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
6080             || (c >= 0xffe0  && c <= 0xffe6)
6081             || (c >= 0x20000 && c <= 0x2fffd)
6082             || (c >= 0x30000 && c <= 0x3fffd)))
6083                 return 2;
6084
6085         if (c == '\t')
6086                 return opt_tab_size;
6087
6088         return 1;
6089 }
6090
6091 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
6092  * Illegal bytes are set one. */
6093 static const unsigned char utf8_bytes[256] = {
6094         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,
6095         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,
6096         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,
6097         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,
6098         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,
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         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,
6101         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,
6102 };
6103
6104 /* Decode UTF-8 multi-byte representation into a Unicode character. */
6105 static inline unsigned long
6106 utf8_to_unicode(const char *string, size_t length)
6107 {
6108         unsigned long unicode;
6109
6110         switch (length) {
6111         case 1:
6112                 unicode  =   string[0];
6113                 break;
6114         case 2:
6115                 unicode  =  (string[0] & 0x1f) << 6;
6116                 unicode +=  (string[1] & 0x3f);
6117                 break;
6118         case 3:
6119                 unicode  =  (string[0] & 0x0f) << 12;
6120                 unicode += ((string[1] & 0x3f) << 6);
6121                 unicode +=  (string[2] & 0x3f);
6122                 break;
6123         case 4:
6124                 unicode  =  (string[0] & 0x0f) << 18;
6125                 unicode += ((string[1] & 0x3f) << 12);
6126                 unicode += ((string[2] & 0x3f) << 6);
6127                 unicode +=  (string[3] & 0x3f);
6128                 break;
6129         case 5:
6130                 unicode  =  (string[0] & 0x0f) << 24;
6131                 unicode += ((string[1] & 0x3f) << 18);
6132                 unicode += ((string[2] & 0x3f) << 12);
6133                 unicode += ((string[3] & 0x3f) << 6);
6134                 unicode +=  (string[4] & 0x3f);
6135                 break;
6136         case 6:
6137                 unicode  =  (string[0] & 0x01) << 30;
6138                 unicode += ((string[1] & 0x3f) << 24);
6139                 unicode += ((string[2] & 0x3f) << 18);
6140                 unicode += ((string[3] & 0x3f) << 12);
6141                 unicode += ((string[4] & 0x3f) << 6);
6142                 unicode +=  (string[5] & 0x3f);
6143                 break;
6144         default:
6145                 die("Invalid Unicode length");
6146         }
6147
6148         /* Invalid characters could return the special 0xfffd value but NUL
6149          * should be just as good. */
6150         return unicode > 0xffff ? 0 : unicode;
6151 }
6152
6153 /* Calculates how much of string can be shown within the given maximum width
6154  * and sets trimmed parameter to non-zero value if all of string could not be
6155  * shown. If the reserve flag is TRUE, it will reserve at least one
6156  * trailing character, which can be useful when drawing a delimiter.
6157  *
6158  * Returns the number of bytes to output from string to satisfy max_width. */
6159 static size_t
6160 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve)
6161 {
6162         const char *string = *start;
6163         const char *end = strchr(string, '\0');
6164         unsigned char last_bytes = 0;
6165         size_t last_ucwidth = 0;
6166
6167         *width = 0;
6168         *trimmed = 0;
6169
6170         while (string < end) {
6171                 int c = *(unsigned char *) string;
6172                 unsigned char bytes = utf8_bytes[c];
6173                 size_t ucwidth;
6174                 unsigned long unicode;
6175
6176                 if (string + bytes > end)
6177                         break;
6178
6179                 /* Change representation to figure out whether
6180                  * it is a single- or double-width character. */
6181
6182                 unicode = utf8_to_unicode(string, bytes);
6183                 /* FIXME: Graceful handling of invalid Unicode character. */
6184                 if (!unicode)
6185                         break;
6186
6187                 ucwidth = unicode_width(unicode);
6188                 if (skip > 0) {
6189                         skip -= ucwidth <= skip ? ucwidth : skip;
6190                         *start += bytes;
6191                 }
6192                 *width  += ucwidth;
6193                 if (*width > max_width) {
6194                         *trimmed = 1;
6195                         *width -= ucwidth;
6196                         if (reserve && *width == max_width) {
6197                                 string -= last_bytes;
6198                                 *width -= last_ucwidth;
6199                         }
6200                         break;
6201                 }
6202
6203                 string  += bytes;
6204                 last_bytes = ucwidth ? bytes : 0;
6205                 last_ucwidth = ucwidth;
6206         }
6207
6208         return string - *start;
6209 }
6210
6211
6212 /*
6213  * Status management
6214  */
6215
6216 /* Whether or not the curses interface has been initialized. */
6217 static bool cursed = FALSE;
6218
6219 /* Terminal hacks and workarounds. */
6220 static bool use_scroll_redrawwin;
6221 static bool use_scroll_status_wclear;
6222
6223 /* The status window is used for polling keystrokes. */
6224 static WINDOW *status_win;
6225
6226 /* Reading from the prompt? */
6227 static bool input_mode = FALSE;
6228
6229 static bool status_empty = FALSE;
6230
6231 /* Update status and title window. */
6232 static void
6233 report(const char *msg, ...)
6234 {
6235         struct view *view = display[current_view];
6236
6237         if (input_mode)
6238                 return;
6239
6240         if (!view) {
6241                 char buf[SIZEOF_STR];
6242                 va_list args;
6243
6244                 va_start(args, msg);
6245                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
6246                         buf[sizeof(buf) - 1] = 0;
6247                         buf[sizeof(buf) - 2] = '.';
6248                         buf[sizeof(buf) - 3] = '.';
6249                         buf[sizeof(buf) - 4] = '.';
6250                 }
6251                 va_end(args);
6252                 die("%s", buf);
6253         }
6254
6255         if (!status_empty || *msg) {
6256                 va_list args;
6257
6258                 va_start(args, msg);
6259
6260                 wmove(status_win, 0, 0);
6261                 if (view->has_scrolled && use_scroll_status_wclear)
6262                         wclear(status_win);
6263                 if (*msg) {
6264                         vwprintw(status_win, msg, args);
6265                         status_empty = FALSE;
6266                 } else {
6267                         status_empty = TRUE;
6268                 }
6269                 wclrtoeol(status_win);
6270                 wnoutrefresh(status_win);
6271
6272                 va_end(args);
6273         }
6274
6275         update_view_title(view);
6276 }
6277
6278 /* Controls when nodelay should be in effect when polling user input. */
6279 static void
6280 set_nonblocking_input(bool loading)
6281 {
6282         static unsigned int loading_views;
6283
6284         if ((loading == FALSE && loading_views-- == 1) ||
6285             (loading == TRUE  && loading_views++ == 0))
6286                 nodelay(status_win, loading);
6287 }
6288
6289 static void
6290 init_display(void)
6291 {
6292         const char *term;
6293         int x, y;
6294
6295         /* Initialize the curses library */
6296         if (isatty(STDIN_FILENO)) {
6297                 cursed = !!initscr();
6298                 opt_tty = stdin;
6299         } else {
6300                 /* Leave stdin and stdout alone when acting as a pager. */
6301                 opt_tty = fopen("/dev/tty", "r+");
6302                 if (!opt_tty)
6303                         die("Failed to open /dev/tty");
6304                 cursed = !!newterm(NULL, opt_tty, opt_tty);
6305         }
6306
6307         if (!cursed)
6308                 die("Failed to initialize curses");
6309
6310         nonl();         /* Disable conversion and detect newlines from input. */
6311         cbreak();       /* Take input chars one at a time, no wait for \n */
6312         noecho();       /* Don't echo input */
6313         leaveok(stdscr, FALSE);
6314
6315         if (has_colors())
6316                 init_colors();
6317
6318         getmaxyx(stdscr, y, x);
6319         status_win = newwin(1, 0, y - 1, 0);
6320         if (!status_win)
6321                 die("Failed to create status window");
6322
6323         /* Enable keyboard mapping */
6324         keypad(status_win, TRUE);
6325         wbkgdset(status_win, get_line_attr(LINE_STATUS));
6326
6327         TABSIZE = opt_tab_size;
6328         if (opt_line_graphics) {
6329                 line_graphics[LINE_GRAPHIC_VLINE] = ACS_VLINE;
6330         }
6331
6332         term = getenv("XTERM_VERSION") ? NULL : getenv("COLORTERM");
6333         if (term && !strcmp(term, "gnome-terminal")) {
6334                 /* In the gnome-terminal-emulator, the message from
6335                  * scrolling up one line when impossible followed by
6336                  * scrolling down one line causes corruption of the
6337                  * status line. This is fixed by calling wclear. */
6338                 use_scroll_status_wclear = TRUE;
6339                 use_scroll_redrawwin = FALSE;
6340
6341         } else if (term && !strcmp(term, "xrvt-xpm")) {
6342                 /* No problems with full optimizations in xrvt-(unicode)
6343                  * and aterm. */
6344                 use_scroll_status_wclear = use_scroll_redrawwin = FALSE;
6345
6346         } else {
6347                 /* When scrolling in (u)xterm the last line in the
6348                  * scrolling direction will update slowly. */
6349                 use_scroll_redrawwin = TRUE;
6350                 use_scroll_status_wclear = FALSE;
6351         }
6352 }
6353
6354 static int
6355 get_input(int prompt_position)
6356 {
6357         struct view *view;
6358         int i, key, cursor_y, cursor_x;
6359
6360         if (prompt_position)
6361                 input_mode = TRUE;
6362
6363         while (TRUE) {
6364                 foreach_view (view, i) {
6365                         update_view(view);
6366                         if (view_is_displayed(view) && view->has_scrolled &&
6367                             use_scroll_redrawwin)
6368                                 redrawwin(view->win);
6369                         view->has_scrolled = FALSE;
6370                 }
6371
6372                 /* Update the cursor position. */
6373                 if (prompt_position) {
6374                         getbegyx(status_win, cursor_y, cursor_x);
6375                         cursor_x = prompt_position;
6376                 } else {
6377                         view = display[current_view];
6378                         getbegyx(view->win, cursor_y, cursor_x);
6379                         cursor_x = view->width - 1;
6380                         cursor_y += view->lineno - view->offset;
6381                 }
6382                 setsyx(cursor_y, cursor_x);
6383
6384                 /* Refresh, accept single keystroke of input */
6385                 doupdate();
6386                 key = wgetch(status_win);
6387
6388                 /* wgetch() with nodelay() enabled returns ERR when
6389                  * there's no input. */
6390                 if (key == ERR) {
6391
6392                 } else if (key == KEY_RESIZE) {
6393                         int height, width;
6394
6395                         getmaxyx(stdscr, height, width);
6396
6397                         wresize(status_win, 1, width);
6398                         mvwin(status_win, height - 1, 0);
6399                         wnoutrefresh(status_win);
6400                         resize_display();
6401                         redraw_display(TRUE);
6402
6403                 } else {
6404                         input_mode = FALSE;
6405                         return key;
6406                 }
6407         }
6408 }
6409
6410 static char *
6411 prompt_input(const char *prompt, input_handler handler, void *data)
6412 {
6413         enum input_status status = INPUT_OK;
6414         static char buf[SIZEOF_STR];
6415         size_t pos = 0;
6416
6417         buf[pos] = 0;
6418
6419         while (status == INPUT_OK || status == INPUT_SKIP) {
6420                 int key;
6421
6422                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
6423                 wclrtoeol(status_win);
6424
6425                 key = get_input(pos + 1);
6426                 switch (key) {
6427                 case KEY_RETURN:
6428                 case KEY_ENTER:
6429                 case '\n':
6430                         status = pos ? INPUT_STOP : INPUT_CANCEL;
6431                         break;
6432
6433                 case KEY_BACKSPACE:
6434                         if (pos > 0)
6435                                 buf[--pos] = 0;
6436                         else
6437                                 status = INPUT_CANCEL;
6438                         break;
6439
6440                 case KEY_ESC:
6441                         status = INPUT_CANCEL;
6442                         break;
6443
6444                 default:
6445                         if (pos >= sizeof(buf)) {
6446                                 report("Input string too long");
6447                                 return NULL;
6448                         }
6449
6450                         status = handler(data, buf, key);
6451                         if (status == INPUT_OK)
6452                                 buf[pos++] = (char) key;
6453                 }
6454         }
6455
6456         /* Clear the status window */
6457         status_empty = FALSE;
6458         report("");
6459
6460         if (status == INPUT_CANCEL)
6461                 return NULL;
6462
6463         buf[pos++] = 0;
6464
6465         return buf;
6466 }
6467
6468 static enum input_status
6469 prompt_yesno_handler(void *data, char *buf, int c)
6470 {
6471         if (c == 'y' || c == 'Y')
6472                 return INPUT_STOP;
6473         if (c == 'n' || c == 'N')
6474                 return INPUT_CANCEL;
6475         return INPUT_SKIP;
6476 }
6477
6478 static bool
6479 prompt_yesno(const char *prompt)
6480 {
6481         char prompt2[SIZEOF_STR];
6482
6483         if (!string_format(prompt2, "%s [Yy/Nn]", prompt))
6484                 return FALSE;
6485
6486         return !!prompt_input(prompt2, prompt_yesno_handler, NULL);
6487 }
6488
6489 static enum input_status
6490 read_prompt_handler(void *data, char *buf, int c)
6491 {
6492         return isprint(c) ? INPUT_OK : INPUT_SKIP;
6493 }
6494
6495 static char *
6496 read_prompt(const char *prompt)
6497 {
6498         return prompt_input(prompt, read_prompt_handler, NULL);
6499 }
6500
6501 /*
6502  * Repository properties
6503  */
6504
6505 static struct ref *refs = NULL;
6506 static size_t refs_alloc = 0;
6507 static size_t refs_size = 0;
6508
6509 /* Id <-> ref store */
6510 static struct ref ***id_refs = NULL;
6511 static size_t id_refs_alloc = 0;
6512 static size_t id_refs_size = 0;
6513
6514 static int
6515 compare_refs(const void *ref1_, const void *ref2_)
6516 {
6517         const struct ref *ref1 = *(const struct ref **)ref1_;
6518         const struct ref *ref2 = *(const struct ref **)ref2_;
6519
6520         if (ref1->tag != ref2->tag)
6521                 return ref2->tag - ref1->tag;
6522         if (ref1->ltag != ref2->ltag)
6523                 return ref2->ltag - ref2->ltag;
6524         if (ref1->head != ref2->head)
6525                 return ref2->head - ref1->head;
6526         if (ref1->tracked != ref2->tracked)
6527                 return ref2->tracked - ref1->tracked;
6528         if (ref1->remote != ref2->remote)
6529                 return ref2->remote - ref1->remote;
6530         return strcmp(ref1->name, ref2->name);
6531 }
6532
6533 static struct ref **
6534 get_refs(const char *id)
6535 {
6536         struct ref ***tmp_id_refs;
6537         struct ref **ref_list = NULL;
6538         size_t ref_list_alloc = 0;
6539         size_t ref_list_size = 0;
6540         size_t i;
6541
6542         for (i = 0; i < id_refs_size; i++)
6543                 if (!strcmp(id, id_refs[i][0]->id))
6544                         return id_refs[i];
6545
6546         tmp_id_refs = realloc_items(id_refs, &id_refs_alloc, id_refs_size + 1,
6547                                     sizeof(*id_refs));
6548         if (!tmp_id_refs)
6549                 return NULL;
6550
6551         id_refs = tmp_id_refs;
6552
6553         for (i = 0; i < refs_size; i++) {
6554                 struct ref **tmp;
6555
6556                 if (strcmp(id, refs[i].id))
6557                         continue;
6558
6559                 tmp = realloc_items(ref_list, &ref_list_alloc,
6560                                     ref_list_size + 1, sizeof(*ref_list));
6561                 if (!tmp) {
6562                         if (ref_list)
6563                                 free(ref_list);
6564                         return NULL;
6565                 }
6566
6567                 ref_list = tmp;
6568                 ref_list[ref_list_size] = &refs[i];
6569                 /* XXX: The properties of the commit chains ensures that we can
6570                  * safely modify the shared ref. The repo references will
6571                  * always be similar for the same id. */
6572                 ref_list[ref_list_size]->next = 1;
6573
6574                 ref_list_size++;
6575         }
6576
6577         if (ref_list) {
6578                 qsort(ref_list, ref_list_size, sizeof(*ref_list), compare_refs);
6579                 ref_list[ref_list_size - 1]->next = 0;
6580                 id_refs[id_refs_size++] = ref_list;
6581         }
6582
6583         return ref_list;
6584 }
6585
6586 static int
6587 read_ref(char *id, size_t idlen, char *name, size_t namelen)
6588 {
6589         struct ref *ref;
6590         bool tag = FALSE;
6591         bool ltag = FALSE;
6592         bool remote = FALSE;
6593         bool tracked = FALSE;
6594         bool check_replace = FALSE;
6595         bool head = FALSE;
6596
6597         if (!prefixcmp(name, "refs/tags/")) {
6598                 if (!suffixcmp(name, namelen, "^{}")) {
6599                         namelen -= 3;
6600                         name[namelen] = 0;
6601                         if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
6602                                 check_replace = TRUE;
6603                 } else {
6604                         ltag = TRUE;
6605                 }
6606
6607                 tag = TRUE;
6608                 namelen -= STRING_SIZE("refs/tags/");
6609                 name    += STRING_SIZE("refs/tags/");
6610
6611         } else if (!prefixcmp(name, "refs/remotes/")) {
6612                 remote = TRUE;
6613                 namelen -= STRING_SIZE("refs/remotes/");
6614                 name    += STRING_SIZE("refs/remotes/");
6615                 tracked  = !strcmp(opt_remote, name);
6616
6617         } else if (!prefixcmp(name, "refs/heads/")) {
6618                 namelen -= STRING_SIZE("refs/heads/");
6619                 name    += STRING_SIZE("refs/heads/");
6620                 head     = !strncmp(opt_head, name, namelen);
6621
6622         } else if (!strcmp(name, "HEAD")) {
6623                 string_ncopy(opt_head_rev, id, idlen);
6624                 return OK;
6625         }
6626
6627         if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
6628                 /* it's an annotated tag, replace the previous SHA1 with the
6629                  * resolved commit id; relies on the fact git-ls-remote lists
6630                  * the commit id of an annotated tag right before the commit id
6631                  * it points to. */
6632                 refs[refs_size - 1].ltag = ltag;
6633                 string_copy_rev(refs[refs_size - 1].id, id);
6634
6635                 return OK;
6636         }
6637         refs = realloc_items(refs, &refs_alloc, refs_size + 1, sizeof(*refs));
6638         if (!refs)
6639                 return ERR;
6640
6641         ref = &refs[refs_size++];
6642         ref->name = malloc(namelen + 1);
6643         if (!ref->name)
6644                 return ERR;
6645
6646         strncpy(ref->name, name, namelen);
6647         ref->name[namelen] = 0;
6648         ref->head = head;
6649         ref->tag = tag;
6650         ref->ltag = ltag;
6651         ref->remote = remote;
6652         ref->tracked = tracked;
6653         string_copy_rev(ref->id, id);
6654
6655         return OK;
6656 }
6657
6658 static int
6659 load_refs(void)
6660 {
6661         static const char *ls_remote_argv[SIZEOF_ARG] = {
6662                 "git", "ls-remote", ".", NULL
6663         };
6664         static bool init = FALSE;
6665
6666         if (!init) {
6667                 argv_from_env(ls_remote_argv, "TIG_LS_REMOTE");
6668                 init = TRUE;
6669         }
6670
6671         if (!*opt_git_dir)
6672                 return OK;
6673
6674         while (refs_size > 0)
6675                 free(refs[--refs_size].name);
6676         while (id_refs_size > 0)
6677                 free(id_refs[--id_refs_size]);
6678
6679         return run_io_load(ls_remote_argv, "\t", read_ref);
6680 }
6681
6682 static void
6683 set_repo_config_option(char *name, char *value, int (*cmd)(int, const char **))
6684 {
6685         const char *argv[SIZEOF_ARG] = { name, "=" };
6686         int argc = 1 + (cmd == option_set_command);
6687         int error = ERR;
6688
6689         if (!argv_from_string(argv, &argc, value))
6690                 config_msg = "Too many option arguments";
6691         else
6692                 error = cmd(argc, argv);
6693
6694         if (error == ERR)
6695                 warn("Option 'tig.%s': %s", name, config_msg);
6696 }
6697
6698 static int
6699 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
6700 {
6701         if (!strcmp(name, "i18n.commitencoding"))
6702                 string_ncopy(opt_encoding, value, valuelen);
6703
6704         if (!strcmp(name, "core.editor"))
6705                 string_ncopy(opt_editor, value, valuelen);
6706
6707         if (!prefixcmp(name, "tig.color."))
6708                 set_repo_config_option(name + 10, value, option_color_command);
6709
6710         else if (!prefixcmp(name, "tig.bind."))
6711                 set_repo_config_option(name + 9, value, option_bind_command);
6712
6713         else if (!prefixcmp(name, "tig."))
6714                 set_repo_config_option(name + 4, value, option_set_command);
6715
6716         /* branch.<head>.remote */
6717         if (*opt_head &&
6718             !strncmp(name, "branch.", 7) &&
6719             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
6720             !strcmp(name + 7 + strlen(opt_head), ".remote"))
6721                 string_ncopy(opt_remote, value, valuelen);
6722
6723         if (*opt_head && *opt_remote &&
6724             !strncmp(name, "branch.", 7) &&
6725             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
6726             !strcmp(name + 7 + strlen(opt_head), ".merge")) {
6727                 size_t from = strlen(opt_remote);
6728
6729                 if (!prefixcmp(value, "refs/heads/")) {
6730                         value += STRING_SIZE("refs/heads/");
6731                         valuelen -= STRING_SIZE("refs/heads/");
6732                 }
6733
6734                 if (!string_format_from(opt_remote, &from, "/%s", value))
6735                         opt_remote[0] = 0;
6736         }
6737
6738         return OK;
6739 }
6740
6741 static int
6742 load_git_config(void)
6743 {
6744         const char *config_list_argv[] = { "git", GIT_CONFIG, "--list", NULL };
6745
6746         return run_io_load(config_list_argv, "=", read_repo_config_option);
6747 }
6748
6749 static int
6750 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
6751 {
6752         if (!opt_git_dir[0]) {
6753                 string_ncopy(opt_git_dir, name, namelen);
6754
6755         } else if (opt_is_inside_work_tree == -1) {
6756                 /* This can be 3 different values depending on the
6757                  * version of git being used. If git-rev-parse does not
6758                  * understand --is-inside-work-tree it will simply echo
6759                  * the option else either "true" or "false" is printed.
6760                  * Default to true for the unknown case. */
6761                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
6762
6763         } else if (*name == '.') {
6764                 string_ncopy(opt_cdup, name, namelen);
6765
6766         } else {
6767                 string_ncopy(opt_prefix, name, namelen);
6768         }
6769
6770         return OK;
6771 }
6772
6773 static int
6774 load_repo_info(void)
6775 {
6776         const char *head_argv[] = {
6777                 "git", "symbolic-ref", "HEAD", NULL
6778         };
6779         const char *rev_parse_argv[] = {
6780                 "git", "rev-parse", "--git-dir", "--is-inside-work-tree",
6781                         "--show-cdup", "--show-prefix", NULL
6782         };
6783
6784         if (run_io_buf(head_argv, opt_head, sizeof(opt_head))) {
6785                 chomp_string(opt_head);
6786                 if (!prefixcmp(opt_head, "refs/heads/")) {
6787                         char *offset = opt_head + STRING_SIZE("refs/heads/");
6788
6789                         memmove(opt_head, offset, strlen(offset) + 1);
6790                 }
6791         }
6792
6793         return run_io_load(rev_parse_argv, "=", read_repo_info);
6794 }
6795
6796
6797 /*
6798  * Main
6799  */
6800
6801 static const char usage[] =
6802 "tig " TIG_VERSION " (" __DATE__ ")\n"
6803 "\n"
6804 "Usage: tig        [options] [revs] [--] [paths]\n"
6805 "   or: tig show   [options] [revs] [--] [paths]\n"
6806 "   or: tig blame  [rev] path\n"
6807 "   or: tig status\n"
6808 "   or: tig <      [git command output]\n"
6809 "\n"
6810 "Options:\n"
6811 "  -v, --version   Show version and exit\n"
6812 "  -h, --help      Show help message and exit";
6813
6814 static void __NORETURN
6815 quit(int sig)
6816 {
6817         /* XXX: Restore tty modes and let the OS cleanup the rest! */
6818         if (cursed)
6819                 endwin();
6820         exit(0);
6821 }
6822
6823 static void __NORETURN
6824 die(const char *err, ...)
6825 {
6826         va_list args;
6827
6828         endwin();
6829
6830         va_start(args, err);
6831         fputs("tig: ", stderr);
6832         vfprintf(stderr, err, args);
6833         fputs("\n", stderr);
6834         va_end(args);
6835
6836         exit(1);
6837 }
6838
6839 static void
6840 warn(const char *msg, ...)
6841 {
6842         va_list args;
6843
6844         va_start(args, msg);
6845         fputs("tig warning: ", stderr);
6846         vfprintf(stderr, msg, args);
6847         fputs("\n", stderr);
6848         va_end(args);
6849 }
6850
6851 static enum request
6852 parse_options(int argc, const char *argv[])
6853 {
6854         enum request request = REQ_VIEW_MAIN;
6855         const char *subcommand;
6856         bool seen_dashdash = FALSE;
6857         /* XXX: This is vulnerable to the user overriding options
6858          * required for the main view parser. */
6859         const char *custom_argv[SIZEOF_ARG] = {
6860                 "git", "log", "--no-color", "--pretty=raw", "--parents",
6861                         "--topo-order", NULL
6862         };
6863         int i, j = 6;
6864
6865         if (!isatty(STDIN_FILENO)) {
6866                 io_open(&VIEW(REQ_VIEW_PAGER)->io, "");
6867                 return REQ_VIEW_PAGER;
6868         }
6869
6870         if (argc <= 1)
6871                 return REQ_NONE;
6872
6873         subcommand = argv[1];
6874         if (!strcmp(subcommand, "status")) {
6875                 if (argc > 2)
6876                         warn("ignoring arguments after `%s'", subcommand);
6877                 return REQ_VIEW_STATUS;
6878
6879         } else if (!strcmp(subcommand, "blame")) {
6880                 if (argc <= 2 || argc > 4)
6881                         die("invalid number of options to blame\n\n%s", usage);
6882
6883                 i = 2;
6884                 if (argc == 4) {
6885                         string_ncopy(opt_ref, argv[i], strlen(argv[i]));
6886                         i++;
6887                 }
6888
6889                 string_ncopy(opt_file, argv[i], strlen(argv[i]));
6890                 return REQ_VIEW_BLAME;
6891
6892         } else if (!strcmp(subcommand, "show")) {
6893                 request = REQ_VIEW_DIFF;
6894
6895         } else {
6896                 subcommand = NULL;
6897         }
6898
6899         if (subcommand) {
6900                 custom_argv[1] = subcommand;
6901                 j = 2;
6902         }
6903
6904         for (i = 1 + !!subcommand; i < argc; i++) {
6905                 const char *opt = argv[i];
6906
6907                 if (seen_dashdash || !strcmp(opt, "--")) {
6908                         seen_dashdash = TRUE;
6909
6910                 } else if (!strcmp(opt, "-v") || !strcmp(opt, "--version")) {
6911                         printf("tig version %s\n", TIG_VERSION);
6912                         quit(0);
6913
6914                 } else if (!strcmp(opt, "-h") || !strcmp(opt, "--help")) {
6915                         printf("%s\n", usage);
6916                         quit(0);
6917                 }
6918
6919                 custom_argv[j++] = opt;
6920                 if (j >= ARRAY_SIZE(custom_argv))
6921                         die("command too long");
6922         }
6923
6924         if (!prepare_update(VIEW(request), custom_argv, NULL, FORMAT_NONE))                                                                        
6925                 die("Failed to format arguments"); 
6926
6927         return request;
6928 }
6929
6930 int
6931 main(int argc, const char *argv[])
6932 {
6933         enum request request = parse_options(argc, argv);
6934         struct view *view;
6935         size_t i;
6936
6937         signal(SIGINT, quit);
6938
6939         if (setlocale(LC_ALL, "")) {
6940                 char *codeset = nl_langinfo(CODESET);
6941
6942                 string_ncopy(opt_codeset, codeset, strlen(codeset));
6943         }
6944
6945         if (load_repo_info() == ERR)
6946                 die("Failed to load repo info.");
6947
6948         if (load_options() == ERR)
6949                 die("Failed to load user config.");
6950
6951         if (load_git_config() == ERR)
6952                 die("Failed to load repo config.");
6953
6954         /* Require a git repository unless when running in pager mode. */
6955         if (!opt_git_dir[0] && request != REQ_VIEW_PAGER)
6956                 die("Not a git repository");
6957
6958         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
6959                 opt_utf8 = FALSE;
6960
6961         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
6962                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
6963                 if (opt_iconv == ICONV_NONE)
6964                         die("Failed to initialize character set conversion");
6965         }
6966
6967         if (load_refs() == ERR)
6968                 die("Failed to load refs.");
6969
6970         foreach_view (view, i)
6971                 argv_from_env(view->ops->argv, view->cmd_env);
6972
6973         init_display();
6974
6975         if (request != REQ_NONE)
6976                 open_view(NULL, request, OPEN_PREPARED);
6977         request = request == REQ_NONE ? REQ_VIEW_MAIN : REQ_NONE;
6978
6979         while (view_driver(display[current_view], request)) {
6980                 int key = get_input(0);
6981
6982                 view = display[current_view];
6983                 request = get_keybinding(view->keymap, key);
6984
6985                 /* Some low-level request handling. This keeps access to
6986                  * status_win restricted. */
6987                 switch (request) {
6988                 case REQ_PROMPT:
6989                 {
6990                         char *cmd = read_prompt(":");
6991
6992                         if (cmd && isdigit(*cmd)) {
6993                                 int lineno = view->lineno + 1;
6994
6995                                 if (parse_int(&lineno, cmd, 1, view->lines + 1) == OK) {
6996                                         select_view_line(view, lineno - 1);
6997                                         report("");
6998                                 } else {
6999                                         report("Unable to parse '%s' as a line number", cmd);
7000                                 }
7001
7002                         } else if (cmd) {
7003                                 struct view *next = VIEW(REQ_VIEW_PAGER);
7004                                 const char *argv[SIZEOF_ARG] = { "git" };
7005                                 int argc = 1;
7006
7007                                 /* When running random commands, initially show the
7008                                  * command in the title. However, it maybe later be
7009                                  * overwritten if a commit line is selected. */
7010                                 string_ncopy(next->ref, cmd, strlen(cmd));
7011
7012                                 if (!argv_from_string(argv, &argc, cmd)) {
7013                                         report("Too many arguments");
7014                                 } else if (!prepare_update(next, argv, NULL, FORMAT_DASH)) {
7015                                         report("Failed to format command");
7016                                 } else {
7017                                         open_view(view, REQ_VIEW_PAGER, OPEN_PREPARED);
7018                                 }
7019                         }
7020
7021                         request = REQ_NONE;
7022                         break;
7023                 }
7024                 case REQ_SEARCH:
7025                 case REQ_SEARCH_BACK:
7026                 {
7027                         const char *prompt = request == REQ_SEARCH ? "/" : "?";
7028                         char *search = read_prompt(prompt);
7029
7030                         if (search)
7031                                 string_ncopy(opt_search, search, strlen(search));
7032                         else if (*opt_search)
7033                                 request = request == REQ_SEARCH ?
7034                                         REQ_FIND_NEXT :
7035                                         REQ_FIND_PREV;
7036                         else
7037                                 request = REQ_NONE;
7038                         break;
7039                 }
7040                 default:
7041                         break;
7042                 }
7043         }
7044
7045         quit(0);
7046
7047         return 0;
7048 }