Use file and line number information when loading blame for commit
[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])
3388 {
3389         char buf[SIZEOF_STR * 4];
3390         const char *revlist_argv[] = {
3391                 "git", "rev-list", "-1", "--parents", id, 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                 report("The selected commit has no parents");
3403                 return FALSE;
3404         }
3405
3406         if (parents > 1) {
3407                 char prompt[SIZEOF_STR];
3408                 char *result;
3409
3410                 if (!string_format(prompt, "Which parent? [1..%d] ", parents))
3411                         return FALSE;
3412                 result = prompt_input(prompt, select_commit_parent_handler, &parents);
3413                 if (!result)
3414                         return FALSE;
3415                 parents = atoi(result);
3416         }
3417
3418         string_copy_rev(rev, &buf[41 * parents]);
3419         return TRUE;
3420 }
3421
3422 /*
3423  * Pager backend
3424  */
3425
3426 static bool
3427 pager_draw(struct view *view, struct line *line, unsigned int lineno)
3428 {
3429         char text[SIZEOF_STR];
3430
3431         if (opt_line_number && draw_lineno(view, lineno))
3432                 return TRUE;
3433
3434         string_expand(text, sizeof(text), line->data, opt_tab_size);
3435         draw_text(view, line->type, text, TRUE);
3436         return TRUE;
3437 }
3438
3439 static bool
3440 add_describe_ref(char *buf, size_t *bufpos, const char *commit_id, const char *sep)
3441 {
3442         const char *describe_argv[] = { "git", "describe", commit_id, NULL };
3443         char refbuf[SIZEOF_STR];
3444         char *ref = NULL;
3445
3446         if (run_io_buf(describe_argv, refbuf, sizeof(refbuf)))
3447                 ref = chomp_string(refbuf);
3448
3449         if (!ref || !*ref)
3450                 return TRUE;
3451
3452         /* This is the only fatal call, since it can "corrupt" the buffer. */
3453         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
3454                 return FALSE;
3455
3456         return TRUE;
3457 }
3458
3459 static void
3460 add_pager_refs(struct view *view, struct line *line)
3461 {
3462         char buf[SIZEOF_STR];
3463         char *commit_id = (char *)line->data + STRING_SIZE("commit ");
3464         struct ref **refs;
3465         size_t bufpos = 0, refpos = 0;
3466         const char *sep = "Refs: ";
3467         bool is_tag = FALSE;
3468
3469         assert(line->type == LINE_COMMIT);
3470
3471         refs = get_refs(commit_id);
3472         if (!refs) {
3473                 if (view == VIEW(REQ_VIEW_DIFF))
3474                         goto try_add_describe_ref;
3475                 return;
3476         }
3477
3478         do {
3479                 struct ref *ref = refs[refpos];
3480                 const char *fmt = ref->tag    ? "%s[%s]" :
3481                                   ref->remote ? "%s<%s>" : "%s%s";
3482
3483                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
3484                         return;
3485                 sep = ", ";
3486                 if (ref->tag)
3487                         is_tag = TRUE;
3488         } while (refs[refpos++]->next);
3489
3490         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
3491 try_add_describe_ref:
3492                 /* Add <tag>-g<commit_id> "fake" reference. */
3493                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
3494                         return;
3495         }
3496
3497         if (bufpos == 0)
3498                 return;
3499
3500         add_line_text(view, buf, LINE_PP_REFS);
3501 }
3502
3503 static bool
3504 pager_read(struct view *view, char *data)
3505 {
3506         struct line *line;
3507
3508         if (!data)
3509                 return TRUE;
3510
3511         line = add_line_text(view, data, get_line_type(data));
3512         if (!line)
3513                 return FALSE;
3514
3515         if (line->type == LINE_COMMIT &&
3516             (view == VIEW(REQ_VIEW_DIFF) ||
3517              view == VIEW(REQ_VIEW_LOG)))
3518                 add_pager_refs(view, line);
3519
3520         return TRUE;
3521 }
3522
3523 static enum request
3524 pager_request(struct view *view, enum request request, struct line *line)
3525 {
3526         int split = 0;
3527
3528         if (request != REQ_ENTER)
3529                 return request;
3530
3531         if (line->type == LINE_COMMIT &&
3532            (view == VIEW(REQ_VIEW_LOG) ||
3533             view == VIEW(REQ_VIEW_PAGER))) {
3534                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
3535                 split = 1;
3536         }
3537
3538         /* Always scroll the view even if it was split. That way
3539          * you can use Enter to scroll through the log view and
3540          * split open each commit diff. */
3541         scroll_view(view, REQ_SCROLL_LINE_DOWN);
3542
3543         /* FIXME: A minor workaround. Scrolling the view will call report("")
3544          * but if we are scrolling a non-current view this won't properly
3545          * update the view title. */
3546         if (split)
3547                 update_view_title(view);
3548
3549         return REQ_NONE;
3550 }
3551
3552 static bool
3553 pager_grep(struct view *view, struct line *line)
3554 {
3555         regmatch_t pmatch;
3556         char *text = line->data;
3557
3558         if (!*text)
3559                 return FALSE;
3560
3561         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
3562                 return FALSE;
3563
3564         return TRUE;
3565 }
3566
3567 static void
3568 pager_select(struct view *view, struct line *line)
3569 {
3570         if (line->type == LINE_COMMIT) {
3571                 char *text = (char *)line->data + STRING_SIZE("commit ");
3572
3573                 if (view != VIEW(REQ_VIEW_PAGER))
3574                         string_copy_rev(view->ref, text);
3575                 string_copy_rev(ref_commit, text);
3576         }
3577 }
3578
3579 static struct view_ops pager_ops = {
3580         "line",
3581         NULL,
3582         NULL,
3583         pager_read,
3584         pager_draw,
3585         pager_request,
3586         pager_grep,
3587         pager_select,
3588 };
3589
3590 static const char *log_argv[SIZEOF_ARG] = {
3591         "git", "log", "--no-color", "--cc", "--stat", "-n100", "%(head)", NULL
3592 };
3593
3594 static enum request
3595 log_request(struct view *view, enum request request, struct line *line)
3596 {
3597         switch (request) {
3598         case REQ_REFRESH:
3599                 load_refs();
3600                 open_view(view, REQ_VIEW_LOG, OPEN_REFRESH);
3601                 return REQ_NONE;
3602         default:
3603                 return pager_request(view, request, line);
3604         }
3605 }
3606
3607 static struct view_ops log_ops = {
3608         "line",
3609         log_argv,
3610         NULL,
3611         pager_read,
3612         pager_draw,
3613         log_request,
3614         pager_grep,
3615         pager_select,
3616 };
3617
3618 static const char *diff_argv[SIZEOF_ARG] = {
3619         "git", "show", "--pretty=fuller", "--no-color", "--root",
3620                 "--patch-with-stat", "--find-copies-harder", "-C", "%(commit)", NULL
3621 };
3622
3623 static struct view_ops diff_ops = {
3624         "line",
3625         diff_argv,
3626         NULL,
3627         pager_read,
3628         pager_draw,
3629         pager_request,
3630         pager_grep,
3631         pager_select,
3632 };
3633
3634 /*
3635  * Help backend
3636  */
3637
3638 static bool
3639 help_open(struct view *view)
3640 {
3641         char buf[SIZEOF_STR];
3642         size_t bufpos;
3643         int i;
3644
3645         if (view->lines > 0)
3646                 return TRUE;
3647
3648         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
3649
3650         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
3651                 const char *key;
3652
3653                 if (req_info[i].request == REQ_NONE)
3654                         continue;
3655
3656                 if (!req_info[i].request) {
3657                         add_line_text(view, "", LINE_DEFAULT);
3658                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
3659                         continue;
3660                 }
3661
3662                 key = get_key(req_info[i].request);
3663                 if (!*key)
3664                         key = "(no key defined)";
3665
3666                 for (bufpos = 0; bufpos <= req_info[i].namelen; bufpos++) {
3667                         buf[bufpos] = tolower(req_info[i].name[bufpos]);
3668                         if (buf[bufpos] == '_')
3669                                 buf[bufpos] = '-';
3670                 }
3671
3672                 add_line_format(view, LINE_DEFAULT, "    %-25s %-20s %s",
3673                                 key, buf, req_info[i].help);
3674         }
3675
3676         if (run_requests) {
3677                 add_line_text(view, "", LINE_DEFAULT);
3678                 add_line_text(view, "External commands:", LINE_DEFAULT);
3679         }
3680
3681         for (i = 0; i < run_requests; i++) {
3682                 struct run_request *req = get_run_request(REQ_NONE + i + 1);
3683                 const char *key;
3684                 int argc;
3685
3686                 if (!req)
3687                         continue;
3688
3689                 key = get_key_name(req->key);
3690                 if (!*key)
3691                         key = "(no key defined)";
3692
3693                 for (bufpos = 0, argc = 0; req->argv[argc]; argc++)
3694                         if (!string_format_from(buf, &bufpos, "%s%s",
3695                                                 argc ? " " : "", req->argv[argc]))
3696                                 return REQ_NONE;
3697
3698                 add_line_format(view, LINE_DEFAULT, "    %-10s %-14s `%s`",
3699                                 keymap_table[req->keymap].name, key, buf);
3700         }
3701
3702         return TRUE;
3703 }
3704
3705 static struct view_ops help_ops = {
3706         "line",
3707         NULL,
3708         help_open,
3709         NULL,
3710         pager_draw,
3711         pager_request,
3712         pager_grep,
3713         pager_select,
3714 };
3715
3716
3717 /*
3718  * Tree backend
3719  */
3720
3721 struct tree_stack_entry {
3722         struct tree_stack_entry *prev;  /* Entry below this in the stack */
3723         unsigned long lineno;           /* Line number to restore */
3724         char *name;                     /* Position of name in opt_path */
3725 };
3726
3727 /* The top of the path stack. */
3728 static struct tree_stack_entry *tree_stack = NULL;
3729 unsigned long tree_lineno = 0;
3730
3731 static void
3732 pop_tree_stack_entry(void)
3733 {
3734         struct tree_stack_entry *entry = tree_stack;
3735
3736         tree_lineno = entry->lineno;
3737         entry->name[0] = 0;
3738         tree_stack = entry->prev;
3739         free(entry);
3740 }
3741
3742 static void
3743 push_tree_stack_entry(const char *name, unsigned long lineno)
3744 {
3745         struct tree_stack_entry *entry = calloc(1, sizeof(*entry));
3746         size_t pathlen = strlen(opt_path);
3747
3748         if (!entry)
3749                 return;
3750
3751         entry->prev = tree_stack;
3752         entry->name = opt_path + pathlen;
3753         tree_stack = entry;
3754
3755         if (!string_format_from(opt_path, &pathlen, "%s/", name)) {
3756                 pop_tree_stack_entry();
3757                 return;
3758         }
3759
3760         /* Move the current line to the first tree entry. */
3761         tree_lineno = 1;
3762         entry->lineno = lineno;
3763 }
3764
3765 /* Parse output from git-ls-tree(1):
3766  *
3767  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
3768  */
3769
3770 #define SIZEOF_TREE_ATTR \
3771         STRING_SIZE("100644 blob f931e1d229c3e185caad4449bf5b66ed72462657\t")
3772
3773 #define SIZEOF_TREE_MODE \
3774         STRING_SIZE("100644 ")
3775
3776 #define TREE_ID_OFFSET \
3777         STRING_SIZE("100644 blob ")
3778
3779 struct tree_entry {
3780         char id[SIZEOF_REV];
3781         mode_t mode;
3782         struct tm time;                 /* Date from the author ident. */
3783         char author[75];                /* Author of the commit. */
3784         char name[1];
3785 };
3786
3787 static const char *
3788 tree_path(struct line *line)
3789 {
3790         return ((struct tree_entry *) line->data)->name;
3791 }
3792
3793
3794 static int
3795 tree_compare_entry(struct line *line1, struct line *line2)
3796 {
3797         if (line1->type != line2->type)
3798                 return line1->type == LINE_TREE_DIR ? -1 : 1;
3799         return strcmp(tree_path(line1), tree_path(line2));
3800 }
3801
3802 static struct line *
3803 tree_entry(struct view *view, enum line_type type, const char *path,
3804            const char *mode, const char *id)
3805 {
3806         struct tree_entry *entry = calloc(1, sizeof(*entry) + strlen(path));
3807         struct line *line = entry ? add_line_data(view, entry, type) : NULL;
3808
3809         if (!entry || !line) {
3810                 free(entry);
3811                 return NULL;
3812         }
3813
3814         strncpy(entry->name, path, strlen(path));
3815         if (mode)
3816                 entry->mode = strtoul(mode, NULL, 8);
3817         if (id)
3818                 string_copy_rev(entry->id, id);
3819
3820         return line;
3821 }
3822
3823 static bool
3824 tree_read_date(struct view *view, char *text, bool *read_date)
3825 {
3826         static char author_name[SIZEOF_STR];
3827         static struct tm author_time;
3828
3829         if (!text && *read_date) {
3830                 *read_date = FALSE;
3831                 return TRUE;
3832
3833         } else if (!text) {
3834                 char *path = *opt_path ? opt_path : ".";
3835                 /* Find next entry to process */
3836                 const char *log_file[] = {
3837                         "git", "log", "--no-color", "--pretty=raw",
3838                                 "--cc", "--raw", view->id, "--", path, NULL
3839                 };
3840                 struct io io = {};
3841
3842                 if (!view->lines) {
3843                         tree_entry(view, LINE_TREE_HEAD, opt_path, NULL, NULL);
3844                         report("Tree is empty");
3845                         return TRUE;
3846                 }
3847
3848                 if (!run_io_rd(&io, log_file, FORMAT_NONE)) {
3849                         report("Failed to load tree data");
3850                         return TRUE;
3851                 }
3852
3853                 done_io(view->pipe);
3854                 view->io = io;
3855                 *read_date = TRUE;
3856                 return FALSE;
3857
3858         } else if (*text == 'a' && get_line_type(text) == LINE_AUTHOR) {
3859                 parse_author_line(text + STRING_SIZE("author "),
3860                                   author_name, sizeof(author_name), &author_time);
3861
3862         } else if (*text == ':') {
3863                 char *pos;
3864                 size_t annotated = 1;
3865                 size_t i;
3866
3867                 pos = strchr(text, '\t');
3868                 if (!pos)
3869                         return TRUE;
3870                 text = pos + 1;
3871                 if (*opt_prefix && !strncmp(text, opt_prefix, strlen(opt_prefix)))
3872                         text += strlen(opt_prefix);
3873                 if (*opt_path && !strncmp(text, opt_path, strlen(opt_path)))
3874                         text += strlen(opt_path);
3875                 pos = strchr(text, '/');
3876                 if (pos)
3877                         *pos = 0;
3878
3879                 for (i = 1; i < view->lines; i++) {
3880                         struct line *line = &view->line[i];
3881                         struct tree_entry *entry = line->data;
3882
3883                         annotated += !!*entry->author;
3884                         if (*entry->author || strcmp(entry->name, text))
3885                                 continue;
3886
3887                         string_copy(entry->author, author_name);
3888                         memcpy(&entry->time, &author_time, sizeof(entry->time));
3889                         line->dirty = 1;
3890                         break;
3891                 }
3892
3893                 if (annotated == view->lines)
3894                         kill_io(view->pipe);
3895         }
3896         return TRUE;
3897 }
3898
3899 static bool
3900 tree_read(struct view *view, char *text)
3901 {
3902         static bool read_date = FALSE;
3903         struct tree_entry *data;
3904         struct line *entry, *line;
3905         enum line_type type;
3906         size_t textlen = text ? strlen(text) : 0;
3907         char *path = text + SIZEOF_TREE_ATTR;
3908
3909         if (read_date || !text)
3910                 return tree_read_date(view, text, &read_date);
3911
3912         if (textlen <= SIZEOF_TREE_ATTR)
3913                 return FALSE;
3914         if (view->lines == 0 &&
3915             !tree_entry(view, LINE_TREE_HEAD, opt_path, NULL, NULL))
3916                 return FALSE;
3917
3918         /* Strip the path part ... */
3919         if (*opt_path) {
3920                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
3921                 size_t striplen = strlen(opt_path);
3922
3923                 if (pathlen > striplen)
3924                         memmove(path, path + striplen,
3925                                 pathlen - striplen + 1);
3926
3927                 /* Insert "link" to parent directory. */
3928                 if (view->lines == 1 &&
3929                     !tree_entry(view, LINE_TREE_DIR, "..", "040000", view->ref))
3930                         return FALSE;
3931         }
3932
3933         type = text[SIZEOF_TREE_MODE] == 't' ? LINE_TREE_DIR : LINE_TREE_FILE;
3934         entry = tree_entry(view, type, path, text, text + TREE_ID_OFFSET);
3935         if (!entry)
3936                 return FALSE;
3937         data = entry->data;
3938
3939         /* Skip "Directory ..." and ".." line. */
3940         for (line = &view->line[1 + !!*opt_path]; line < entry; line++) {
3941                 if (tree_compare_entry(line, entry) <= 0)
3942                         continue;
3943
3944                 memmove(line + 1, line, (entry - line) * sizeof(*entry));
3945
3946                 line->data = data;
3947                 line->type = type;
3948                 for (; line <= entry; line++)
3949                         line->dirty = line->cleareol = 1;
3950                 return TRUE;
3951         }
3952
3953         if (tree_lineno > view->lineno) {
3954                 view->lineno = tree_lineno;
3955                 tree_lineno = 0;
3956         }
3957
3958         return TRUE;
3959 }
3960
3961 static bool
3962 tree_draw(struct view *view, struct line *line, unsigned int lineno)
3963 {
3964         struct tree_entry *entry = line->data;
3965
3966         if (line->type == LINE_TREE_HEAD) {
3967                 if (draw_text(view, line->type, "Directory path /", TRUE))
3968                         return TRUE;
3969         } else {
3970                 if (draw_mode(view, entry->mode))
3971                         return TRUE;
3972
3973                 if (opt_author && draw_author(view, entry->author))
3974                         return TRUE;
3975
3976                 if (opt_date && draw_date(view, *entry->author ? &entry->time : NULL))
3977                         return TRUE;
3978         }
3979         if (draw_text(view, line->type, entry->name, TRUE))
3980                 return TRUE;
3981         return TRUE;
3982 }
3983
3984 static void
3985 open_blob_editor()
3986 {
3987         char file[SIZEOF_STR] = "/tmp/tigblob.XXXXXX";
3988         int fd = mkstemp(file);
3989
3990         if (fd == -1)
3991                 report("Failed to create temporary file");
3992         else if (!run_io_append(blob_ops.argv, FORMAT_ALL, fd))
3993                 report("Failed to save blob data to file");
3994         else
3995                 open_editor(FALSE, file);
3996         if (fd != -1)
3997                 unlink(file);
3998 }
3999
4000 static enum request
4001 tree_request(struct view *view, enum request request, struct line *line)
4002 {
4003         enum open_flags flags;
4004
4005         switch (request) {
4006         case REQ_VIEW_BLAME:
4007                 if (line->type != LINE_TREE_FILE) {
4008                         report("Blame only supported for files");
4009                         return REQ_NONE;
4010                 }
4011
4012                 string_copy(opt_ref, view->vid);
4013                 return request;
4014
4015         case REQ_EDIT:
4016                 if (line->type != LINE_TREE_FILE) {
4017                         report("Edit only supported for files");
4018                 } else if (!is_head_commit(view->vid)) {
4019                         open_blob_editor();
4020                 } else {
4021                         open_editor(TRUE, opt_file);
4022                 }
4023                 return REQ_NONE;
4024
4025         case REQ_PARENT:
4026                 if (!*opt_path) {
4027                         /* quit view if at top of tree */
4028                         return REQ_VIEW_CLOSE;
4029                 }
4030                 /* fake 'cd  ..' */
4031                 line = &view->line[1];
4032                 break;
4033
4034         case REQ_ENTER:
4035                 break;
4036
4037         default:
4038                 return request;
4039         }
4040
4041         /* Cleanup the stack if the tree view is at a different tree. */
4042         while (!*opt_path && tree_stack)
4043                 pop_tree_stack_entry();
4044
4045         switch (line->type) {
4046         case LINE_TREE_DIR:
4047                 /* Depending on whether it is a subdirectory or parent link
4048                  * mangle the path buffer. */
4049                 if (line == &view->line[1] && *opt_path) {
4050                         pop_tree_stack_entry();
4051
4052                 } else {
4053                         const char *basename = tree_path(line);
4054
4055                         push_tree_stack_entry(basename, view->lineno);
4056                 }
4057
4058                 /* Trees and subtrees share the same ID, so they are not not
4059                  * unique like blobs. */
4060                 flags = OPEN_RELOAD;
4061                 request = REQ_VIEW_TREE;
4062                 break;
4063
4064         case LINE_TREE_FILE:
4065                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4066                 request = REQ_VIEW_BLOB;
4067                 break;
4068
4069         default:
4070                 return REQ_NONE;
4071         }
4072
4073         open_view(view, request, flags);
4074         if (request == REQ_VIEW_TREE)
4075                 view->lineno = tree_lineno;
4076
4077         return REQ_NONE;
4078 }
4079
4080 static void
4081 tree_select(struct view *view, struct line *line)
4082 {
4083         struct tree_entry *entry = line->data;
4084
4085         if (line->type == LINE_TREE_FILE) {
4086                 string_copy_rev(ref_blob, entry->id);
4087                 string_format(opt_file, "%s%s", opt_path, tree_path(line));
4088
4089         } else if (line->type != LINE_TREE_DIR) {
4090                 return;
4091         }
4092
4093         string_copy_rev(view->ref, entry->id);
4094 }
4095
4096 static const char *tree_argv[SIZEOF_ARG] = {
4097         "git", "ls-tree", "%(commit)", "%(directory)", NULL
4098 };
4099
4100 static struct view_ops tree_ops = {
4101         "file",
4102         tree_argv,
4103         NULL,
4104         tree_read,
4105         tree_draw,
4106         tree_request,
4107         pager_grep,
4108         tree_select,
4109 };
4110
4111 static bool
4112 blob_read(struct view *view, char *line)
4113 {
4114         if (!line)
4115                 return TRUE;
4116         return add_line_text(view, line, LINE_DEFAULT) != NULL;
4117 }
4118
4119 static enum request
4120 blob_request(struct view *view, enum request request, struct line *line)
4121 {
4122         switch (request) {
4123         case REQ_EDIT:
4124                 open_blob_editor();
4125                 return REQ_NONE;
4126         default:
4127                 return pager_request(view, request, line);
4128         }
4129 }
4130
4131 static const char *blob_argv[SIZEOF_ARG] = {
4132         "git", "cat-file", "blob", "%(blob)", NULL
4133 };
4134
4135 static struct view_ops blob_ops = {
4136         "line",
4137         blob_argv,
4138         NULL,
4139         blob_read,
4140         pager_draw,
4141         blob_request,
4142         pager_grep,
4143         pager_select,
4144 };
4145
4146 /*
4147  * Blame backend
4148  *
4149  * Loading the blame view is a two phase job:
4150  *
4151  *  1. File content is read either using opt_file from the
4152  *     filesystem or using git-cat-file.
4153  *  2. Then blame information is incrementally added by
4154  *     reading output from git-blame.
4155  */
4156
4157 static const char *blame_head_argv[] = {
4158         "git", "blame", "--incremental", "--", "%(file)", NULL
4159 };
4160
4161 static const char *blame_ref_argv[] = {
4162         "git", "blame", "--incremental", "%(ref)", "--", "%(file)", NULL
4163 };
4164
4165 static const char *blame_cat_file_argv[] = {
4166         "git", "cat-file", "blob", "%(ref):%(file)", NULL
4167 };
4168
4169 struct blame_commit {
4170         char id[SIZEOF_REV];            /* SHA1 ID. */
4171         char title[128];                /* First line of the commit message. */
4172         char author[75];                /* Author of the commit. */
4173         struct tm time;                 /* Date from the author ident. */
4174         char filename[128];             /* Name of file. */
4175         bool has_previous;              /* Was a "previous" line detected. */
4176 };
4177
4178 struct blame {
4179         struct blame_commit *commit;
4180         unsigned long lineno;
4181         char text[1];
4182 };
4183
4184 static bool
4185 blame_open(struct view *view)
4186 {
4187         if (*opt_ref || !io_open(&view->io, opt_file)) {
4188                 if (!run_io_rd(&view->io, blame_cat_file_argv, FORMAT_ALL))
4189                         return FALSE;
4190         }
4191
4192         setup_update(view, opt_file);
4193         string_format(view->ref, "%s ...", opt_file);
4194
4195         return TRUE;
4196 }
4197
4198 static struct blame_commit *
4199 get_blame_commit(struct view *view, const char *id)
4200 {
4201         size_t i;
4202
4203         for (i = 0; i < view->lines; i++) {
4204                 struct blame *blame = view->line[i].data;
4205
4206                 if (!blame->commit)
4207                         continue;
4208
4209                 if (!strncmp(blame->commit->id, id, SIZEOF_REV - 1))
4210                         return blame->commit;
4211         }
4212
4213         {
4214                 struct blame_commit *commit = calloc(1, sizeof(*commit));
4215
4216                 if (commit)
4217                         string_ncopy(commit->id, id, SIZEOF_REV);
4218                 return commit;
4219         }
4220 }
4221
4222 static bool
4223 parse_number(const char **posref, size_t *number, size_t min, size_t max)
4224 {
4225         const char *pos = *posref;
4226
4227         *posref = NULL;
4228         pos = strchr(pos + 1, ' ');
4229         if (!pos || !isdigit(pos[1]))
4230                 return FALSE;
4231         *number = atoi(pos + 1);
4232         if (*number < min || *number > max)
4233                 return FALSE;
4234
4235         *posref = pos;
4236         return TRUE;
4237 }
4238
4239 static struct blame_commit *
4240 parse_blame_commit(struct view *view, const char *text, int *blamed)
4241 {
4242         struct blame_commit *commit;
4243         struct blame *blame;
4244         const char *pos = text + SIZEOF_REV - 2;
4245         size_t orig_lineno = 0;
4246         size_t lineno;
4247         size_t group;
4248
4249         if (strlen(text) <= SIZEOF_REV || pos[1] != ' ')
4250                 return NULL;
4251
4252         if (!parse_number(&pos, &orig_lineno, 1, 9999999) ||
4253             !parse_number(&pos, &lineno, 1, view->lines) ||
4254             !parse_number(&pos, &group, 1, view->lines - lineno + 1))
4255                 return NULL;
4256
4257         commit = get_blame_commit(view, text);
4258         if (!commit)
4259                 return NULL;
4260
4261         *blamed += group;
4262         while (group--) {
4263                 struct line *line = &view->line[lineno + group - 1];
4264
4265                 blame = line->data;
4266                 blame->commit = commit;
4267                 blame->lineno = orig_lineno + group - 1;
4268                 line->dirty = 1;
4269         }
4270
4271         return commit;
4272 }
4273
4274 static bool
4275 blame_read_file(struct view *view, const char *line, bool *read_file)
4276 {
4277         if (!line) {
4278                 const char **argv = *opt_ref ? blame_ref_argv : blame_head_argv;
4279                 struct io io = {};
4280
4281                 if (view->lines == 0 && !view->parent)
4282                         die("No blame exist for %s", view->vid);
4283
4284                 if (view->lines == 0 || !run_io_rd(&io, argv, FORMAT_ALL)) {
4285                         report("Failed to load blame data");
4286                         return TRUE;
4287                 }
4288
4289                 done_io(view->pipe);
4290                 view->io = io;
4291                 *read_file = FALSE;
4292                 return FALSE;
4293
4294         } else {
4295                 size_t linelen = strlen(line);
4296                 struct blame *blame = malloc(sizeof(*blame) + linelen);
4297
4298                 if (!blame)
4299                         return FALSE;
4300
4301                 blame->commit = NULL;
4302                 strncpy(blame->text, line, linelen);
4303                 blame->text[linelen] = 0;
4304                 return add_line_data(view, blame, LINE_BLAME_ID) != NULL;
4305         }
4306 }
4307
4308 static bool
4309 match_blame_header(const char *name, char **line)
4310 {
4311         size_t namelen = strlen(name);
4312         bool matched = !strncmp(name, *line, namelen);
4313
4314         if (matched)
4315                 *line += namelen;
4316
4317         return matched;
4318 }
4319
4320 static bool
4321 blame_read(struct view *view, char *line)
4322 {
4323         static struct blame_commit *commit = NULL;
4324         static int blamed = 0;
4325         static time_t author_time;
4326         static bool read_file = TRUE;
4327
4328         if (read_file)
4329                 return blame_read_file(view, line, &read_file);
4330
4331         if (!line) {
4332                 /* Reset all! */
4333                 commit = NULL;
4334                 blamed = 0;
4335                 read_file = TRUE;
4336                 string_format(view->ref, "%s", view->vid);
4337                 if (view_is_displayed(view)) {
4338                         update_view_title(view);
4339                         redraw_view_from(view, 0);
4340                 }
4341                 return TRUE;
4342         }
4343
4344         if (!commit) {
4345                 commit = parse_blame_commit(view, line, &blamed);
4346                 string_format(view->ref, "%s %2d%%", view->vid,
4347                               view->lines ? blamed * 100 / view->lines : 0);
4348
4349         } else if (match_blame_header("author ", &line)) {
4350                 string_ncopy(commit->author, line, strlen(line));
4351
4352         } else if (match_blame_header("author-time ", &line)) {
4353                 author_time = (time_t) atol(line);
4354
4355         } else if (match_blame_header("author-tz ", &line)) {
4356                 parse_timezone(&author_time, line);
4357                 gmtime_r(&author_time, &commit->time);
4358
4359         } else if (match_blame_header("summary ", &line)) {
4360                 string_ncopy(commit->title, line, strlen(line));
4361
4362         } else if (match_blame_header("previous ", &line)) {
4363                 commit->has_previous = TRUE;
4364
4365         } else if (match_blame_header("filename ", &line)) {
4366                 string_ncopy(commit->filename, line, strlen(line));
4367                 commit = NULL;
4368         }
4369
4370         return TRUE;
4371 }
4372
4373 static bool
4374 blame_draw(struct view *view, struct line *line, unsigned int lineno)
4375 {
4376         struct blame *blame = line->data;
4377         struct tm *time = NULL;
4378         const char *id = NULL, *author = NULL;
4379         char text[SIZEOF_STR];
4380
4381         if (blame->commit && *blame->commit->filename) {
4382                 id = blame->commit->id;
4383                 author = blame->commit->author;
4384                 time = &blame->commit->time;
4385         }
4386
4387         if (opt_date && draw_date(view, time))
4388                 return TRUE;
4389
4390         if (opt_author && draw_author(view, author))
4391                 return TRUE;
4392
4393         if (draw_field(view, LINE_BLAME_ID, id, ID_COLS, FALSE))
4394                 return TRUE;
4395
4396         if (draw_lineno(view, lineno))
4397                 return TRUE;
4398
4399         string_expand(text, sizeof(text), blame->text, opt_tab_size);
4400         draw_text(view, LINE_DEFAULT, text, TRUE);
4401         return TRUE;
4402 }
4403
4404 static bool
4405 check_blame_commit(struct blame *blame)
4406 {
4407         if (!blame->commit)
4408                 report("Commit data not loaded yet");
4409         else if (!strcmp(blame->commit->id, NULL_ID))
4410                 report("No commit exist for the selected line");
4411         else
4412                 return TRUE;
4413         return FALSE;
4414 }
4415
4416 static enum request
4417 blame_request(struct view *view, enum request request, struct line *line)
4418 {
4419         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4420         struct blame *blame = line->data;
4421
4422         switch (request) {
4423         case REQ_VIEW_BLAME:
4424                 if (check_blame_commit(blame)) {
4425                         string_copy(opt_ref, blame->commit->id);
4426                         string_copy(opt_file, blame->commit->filename);
4427                         if (blame->lineno)
4428                                 view->lineno = blame->lineno;
4429                         open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
4430                 }
4431                 break;
4432
4433         case REQ_PARENT:
4434                 if (check_blame_commit(blame) &&
4435                     select_commit_parent(blame->commit->id, opt_ref))
4436                         open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
4437                 break;
4438
4439         case REQ_ENTER:
4440                 if (!blame->commit) {
4441                         report("No commit loaded yet");
4442                         break;
4443                 }
4444
4445                 if (view_is_displayed(VIEW(REQ_VIEW_DIFF)) &&
4446                     !strcmp(blame->commit->id, VIEW(REQ_VIEW_DIFF)->ref))
4447                         break;
4448
4449                 if (!strcmp(blame->commit->id, NULL_ID)) {
4450                         struct view *diff = VIEW(REQ_VIEW_DIFF);
4451                         const char *diff_index_argv[] = {
4452                                 "git", "diff-index", "--root", "--patch-with-stat",
4453                                         "-C", "-M", "HEAD", "--", view->vid, NULL
4454                         };
4455
4456                         if (!blame->commit->has_previous) {
4457                                 diff_index_argv[1] = "diff";
4458                                 diff_index_argv[2] = "--no-color";
4459                                 diff_index_argv[6] = "--";
4460                                 diff_index_argv[7] = "/dev/null";
4461                         }
4462
4463                         if (!prepare_update(diff, diff_index_argv, NULL, FORMAT_DASH)) {
4464                                 report("Failed to allocate diff command");
4465                                 break;
4466                         }
4467                         flags |= OPEN_PREPARED;
4468                 }
4469
4470                 open_view(view, REQ_VIEW_DIFF, flags);
4471                 if (VIEW(REQ_VIEW_DIFF)->pipe && !strcmp(blame->commit->id, NULL_ID))
4472                         string_copy_rev(VIEW(REQ_VIEW_DIFF)->ref, NULL_ID);
4473                 break;
4474
4475         default:
4476                 return request;
4477         }
4478
4479         return REQ_NONE;
4480 }
4481
4482 static bool
4483 blame_grep(struct view *view, struct line *line)
4484 {
4485         struct blame *blame = line->data;
4486         struct blame_commit *commit = blame->commit;
4487         regmatch_t pmatch;
4488
4489 #define MATCH(text, on)                                                 \
4490         (on && *text && regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4491
4492         if (commit) {
4493                 char buf[DATE_COLS + 1];
4494
4495                 if (MATCH(commit->title, 1) ||
4496                     MATCH(commit->author, opt_author) ||
4497                     MATCH(commit->id, opt_date))
4498                         return TRUE;
4499
4500                 if (strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time) &&
4501                     MATCH(buf, 1))
4502                         return TRUE;
4503         }
4504
4505         return MATCH(blame->text, 1);
4506
4507 #undef MATCH
4508 }
4509
4510 static void
4511 blame_select(struct view *view, struct line *line)
4512 {
4513         struct blame *blame = line->data;
4514         struct blame_commit *commit = blame->commit;
4515
4516         if (!commit)
4517                 return;
4518
4519         if (!strcmp(commit->id, NULL_ID))
4520                 string_ncopy(ref_commit, "HEAD", 4);
4521         else
4522                 string_copy_rev(ref_commit, commit->id);
4523 }
4524
4525 static struct view_ops blame_ops = {
4526         "line",
4527         NULL,
4528         blame_open,
4529         blame_read,
4530         blame_draw,
4531         blame_request,
4532         blame_grep,
4533         blame_select,
4534 };
4535
4536 /*
4537  * Status backend
4538  */
4539
4540 struct status {
4541         char status;
4542         struct {
4543                 mode_t mode;
4544                 char rev[SIZEOF_REV];
4545                 char name[SIZEOF_STR];
4546         } old;
4547         struct {
4548                 mode_t mode;
4549                 char rev[SIZEOF_REV];
4550                 char name[SIZEOF_STR];
4551         } new;
4552 };
4553
4554 static char status_onbranch[SIZEOF_STR];
4555 static struct status stage_status;
4556 static enum line_type stage_line_type;
4557 static size_t stage_chunks;
4558 static int *stage_chunk;
4559
4560 /* This should work even for the "On branch" line. */
4561 static inline bool
4562 status_has_none(struct view *view, struct line *line)
4563 {
4564         return line < view->line + view->lines && !line[1].data;
4565 }
4566
4567 /* Get fields from the diff line:
4568  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
4569  */
4570 static inline bool
4571 status_get_diff(struct status *file, const char *buf, size_t bufsize)
4572 {
4573         const char *old_mode = buf +  1;
4574         const char *new_mode = buf +  8;
4575         const char *old_rev  = buf + 15;
4576         const char *new_rev  = buf + 56;
4577         const char *status   = buf + 97;
4578
4579         if (bufsize < 98 ||
4580             old_mode[-1] != ':' ||
4581             new_mode[-1] != ' ' ||
4582             old_rev[-1]  != ' ' ||
4583             new_rev[-1]  != ' ' ||
4584             status[-1]   != ' ')
4585                 return FALSE;
4586
4587         file->status = *status;
4588
4589         string_copy_rev(file->old.rev, old_rev);
4590         string_copy_rev(file->new.rev, new_rev);
4591
4592         file->old.mode = strtoul(old_mode, NULL, 8);
4593         file->new.mode = strtoul(new_mode, NULL, 8);
4594
4595         file->old.name[0] = file->new.name[0] = 0;
4596
4597         return TRUE;
4598 }
4599
4600 static bool
4601 status_run(struct view *view, const char *argv[], char status, enum line_type type)
4602 {
4603         struct status *unmerged = NULL;
4604         char *buf;
4605         struct io io = {};
4606
4607         if (!run_io(&io, argv, NULL, IO_RD))
4608                 return FALSE;
4609
4610         add_line_data(view, NULL, type);
4611
4612         while ((buf = io_get(&io, 0, TRUE))) {
4613                 struct status *file = unmerged;
4614
4615                 if (!file) {
4616                         file = calloc(1, sizeof(*file));
4617                         if (!file || !add_line_data(view, file, type))
4618                                 goto error_out;
4619                 }
4620
4621                 /* Parse diff info part. */
4622                 if (status) {
4623                         file->status = status;
4624                         if (status == 'A')
4625                                 string_copy(file->old.rev, NULL_ID);
4626
4627                 } else if (!file->status || file == unmerged) {
4628                         if (!status_get_diff(file, buf, strlen(buf)))
4629                                 goto error_out;
4630
4631                         buf = io_get(&io, 0, TRUE);
4632                         if (!buf)
4633                                 break;
4634
4635                         /* Collapse all modified entries that follow an
4636                          * associated unmerged entry. */
4637                         if (unmerged == file) {
4638                                 unmerged->status = 'U';
4639                                 unmerged = NULL;
4640                         } else if (file->status == 'U') {
4641                                 unmerged = file;
4642                         }
4643                 }
4644
4645                 /* Grab the old name for rename/copy. */
4646                 if (!*file->old.name &&
4647                     (file->status == 'R' || file->status == 'C')) {
4648                         string_ncopy(file->old.name, buf, strlen(buf));
4649
4650                         buf = io_get(&io, 0, TRUE);
4651                         if (!buf)
4652                                 break;
4653                 }
4654
4655                 /* git-ls-files just delivers a NUL separated list of
4656                  * file names similar to the second half of the
4657                  * git-diff-* output. */
4658                 string_ncopy(file->new.name, buf, strlen(buf));
4659                 if (!*file->old.name)
4660                         string_copy(file->old.name, file->new.name);
4661                 file = NULL;
4662         }
4663
4664         if (io_error(&io)) {
4665 error_out:
4666                 done_io(&io);
4667                 return FALSE;
4668         }
4669
4670         if (!view->line[view->lines - 1].data)
4671                 add_line_data(view, NULL, LINE_STAT_NONE);
4672
4673         done_io(&io);
4674         return TRUE;
4675 }
4676
4677 /* Don't show unmerged entries in the staged section. */
4678 static const char *status_diff_index_argv[] = {
4679         "git", "diff-index", "-z", "--diff-filter=ACDMRTXB",
4680                              "--cached", "-M", "HEAD", NULL
4681 };
4682
4683 static const char *status_diff_files_argv[] = {
4684         "git", "diff-files", "-z", NULL
4685 };
4686
4687 static const char *status_list_other_argv[] = {
4688         "git", "ls-files", "-z", "--others", "--exclude-standard", NULL
4689 };
4690
4691 static const char *status_list_no_head_argv[] = {
4692         "git", "ls-files", "-z", "--cached", "--exclude-standard", NULL
4693 };
4694
4695 static const char *update_index_argv[] = {
4696         "git", "update-index", "-q", "--unmerged", "--refresh", NULL
4697 };
4698
4699 /* Restore the previous line number to stay in the context or select a
4700  * line with something that can be updated. */
4701 static void
4702 status_restore(struct view *view)
4703 {
4704         if (view->p_lineno >= view->lines)
4705                 view->p_lineno = view->lines - 1;
4706         while (view->p_lineno < view->lines && !view->line[view->p_lineno].data)
4707                 view->p_lineno++;
4708         while (view->p_lineno > 0 && !view->line[view->p_lineno].data)
4709                 view->p_lineno--;
4710
4711         /* If the above fails, always skip the "On branch" line. */
4712         if (view->p_lineno < view->lines)
4713                 view->lineno = view->p_lineno;
4714         else
4715                 view->lineno = 1;
4716
4717         if (view->lineno < view->offset)
4718                 view->offset = view->lineno;
4719         else if (view->offset + view->height <= view->lineno)
4720                 view->offset = view->lineno - view->height + 1;
4721
4722         view->p_restore = FALSE;
4723 }
4724
4725 static void
4726 status_update_onbranch(void)
4727 {
4728         static const char *paths[][2] = {
4729                 { "rebase-apply/rebasing",      "Rebasing" },
4730                 { "rebase-apply/applying",      "Applying mailbox" },
4731                 { "rebase-apply/",              "Rebasing mailbox" },
4732                 { "rebase-merge/interactive",   "Interactive rebase" },
4733                 { "rebase-merge/",              "Rebase merge" },
4734                 { "MERGE_HEAD",                 "Merging" },
4735                 { "BISECT_LOG",                 "Bisecting" },
4736                 { "HEAD",                       "On branch" },
4737         };
4738         char buf[SIZEOF_STR];
4739         struct stat stat;
4740         int i;
4741
4742         if (is_initial_commit()) {
4743                 string_copy(status_onbranch, "Initial commit");
4744                 return;
4745         }
4746
4747         for (i = 0; i < ARRAY_SIZE(paths); i++) {
4748                 char *head = opt_head;
4749
4750                 if (!string_format(buf, "%s/%s", opt_git_dir, paths[i][0]) ||
4751                     lstat(buf, &stat) < 0)
4752                         continue;
4753
4754                 if (!*opt_head) {
4755                         struct io io = {};
4756
4757                         if (string_format(buf, "%s/rebase-merge/head-name", opt_git_dir) &&
4758                             io_open(&io, buf) &&
4759                             io_read_buf(&io, buf, sizeof(buf))) {
4760                                 head = chomp_string(buf);
4761                                 if (!prefixcmp(head, "refs/heads/"))
4762                                         head += STRING_SIZE("refs/heads/");
4763                         }
4764                 }
4765
4766                 if (!string_format(status_onbranch, "%s %s", paths[i][1], head))
4767                         string_copy(status_onbranch, opt_head);
4768                 return;
4769         }
4770
4771         string_copy(status_onbranch, "Not currently on any branch");
4772 }
4773
4774 /* First parse staged info using git-diff-index(1), then parse unstaged
4775  * info using git-diff-files(1), and finally untracked files using
4776  * git-ls-files(1). */
4777 static bool
4778 status_open(struct view *view)
4779 {
4780         reset_view(view);
4781
4782         add_line_data(view, NULL, LINE_STAT_HEAD);
4783         status_update_onbranch();
4784
4785         run_io_bg(update_index_argv);
4786
4787         if (is_initial_commit()) {
4788                 if (!status_run(view, status_list_no_head_argv, 'A', LINE_STAT_STAGED))
4789                         return FALSE;
4790         } else if (!status_run(view, status_diff_index_argv, 0, LINE_STAT_STAGED)) {
4791                 return FALSE;
4792         }
4793
4794         if (!status_run(view, status_diff_files_argv, 0, LINE_STAT_UNSTAGED) ||
4795             !status_run(view, status_list_other_argv, '?', LINE_STAT_UNTRACKED))
4796                 return FALSE;
4797
4798         /* Restore the exact position or use the specialized restore
4799          * mode? */
4800         if (!view->p_restore)
4801                 status_restore(view);
4802         return TRUE;
4803 }
4804
4805 static bool
4806 status_draw(struct view *view, struct line *line, unsigned int lineno)
4807 {
4808         struct status *status = line->data;
4809         enum line_type type;
4810         const char *text;
4811
4812         if (!status) {
4813                 switch (line->type) {
4814                 case LINE_STAT_STAGED:
4815                         type = LINE_STAT_SECTION;
4816                         text = "Changes to be committed:";
4817                         break;
4818
4819                 case LINE_STAT_UNSTAGED:
4820                         type = LINE_STAT_SECTION;
4821                         text = "Changed but not updated:";
4822                         break;
4823
4824                 case LINE_STAT_UNTRACKED:
4825                         type = LINE_STAT_SECTION;
4826                         text = "Untracked files:";
4827                         break;
4828
4829                 case LINE_STAT_NONE:
4830                         type = LINE_DEFAULT;
4831                         text = "  (no files)";
4832                         break;
4833
4834                 case LINE_STAT_HEAD:
4835                         type = LINE_STAT_HEAD;
4836                         text = status_onbranch;
4837                         break;
4838
4839                 default:
4840                         return FALSE;
4841                 }
4842         } else {
4843                 static char buf[] = { '?', ' ', ' ', ' ', 0 };
4844
4845                 buf[0] = status->status;
4846                 if (draw_text(view, line->type, buf, TRUE))
4847                         return TRUE;
4848                 type = LINE_DEFAULT;
4849                 text = status->new.name;
4850         }
4851
4852         draw_text(view, type, text, TRUE);
4853         return TRUE;
4854 }
4855
4856 static enum request
4857 status_enter(struct view *view, struct line *line)
4858 {
4859         struct status *status = line->data;
4860         const char *oldpath = status ? status->old.name : NULL;
4861         /* Diffs for unmerged entries are empty when passing the new
4862          * path, so leave it empty. */
4863         const char *newpath = status && status->status != 'U' ? status->new.name : NULL;
4864         const char *info;
4865         enum open_flags split;
4866         struct view *stage = VIEW(REQ_VIEW_STAGE);
4867
4868         if (line->type == LINE_STAT_NONE ||
4869             (!status && line[1].type == LINE_STAT_NONE)) {
4870                 report("No file to diff");
4871                 return REQ_NONE;
4872         }
4873
4874         switch (line->type) {
4875         case LINE_STAT_STAGED:
4876                 if (is_initial_commit()) {
4877                         const char *no_head_diff_argv[] = {
4878                                 "git", "diff", "--no-color", "--patch-with-stat",
4879                                         "--", "/dev/null", newpath, NULL
4880                         };
4881
4882                         if (!prepare_update(stage, no_head_diff_argv, opt_cdup, FORMAT_DASH))
4883                                 return REQ_QUIT;
4884                 } else {
4885                         const char *index_show_argv[] = {
4886                                 "git", "diff-index", "--root", "--patch-with-stat",
4887                                         "-C", "-M", "--cached", "HEAD", "--",
4888                                         oldpath, newpath, NULL
4889                         };
4890
4891                         if (!prepare_update(stage, index_show_argv, opt_cdup, FORMAT_DASH))
4892                                 return REQ_QUIT;
4893                 }
4894
4895                 if (status)
4896                         info = "Staged changes to %s";
4897                 else
4898                         info = "Staged changes";
4899                 break;
4900
4901         case LINE_STAT_UNSTAGED:
4902         {
4903                 const char *files_show_argv[] = {
4904                         "git", "diff-files", "--root", "--patch-with-stat",
4905                                 "-C", "-M", "--", oldpath, newpath, NULL
4906                 };
4907
4908                 if (!prepare_update(stage, files_show_argv, opt_cdup, FORMAT_DASH))
4909                         return REQ_QUIT;
4910                 if (status)
4911                         info = "Unstaged changes to %s";
4912                 else
4913                         info = "Unstaged changes";
4914                 break;
4915         }
4916         case LINE_STAT_UNTRACKED:
4917                 if (!newpath) {
4918                         report("No file to show");
4919                         return REQ_NONE;
4920                 }
4921
4922                 if (!suffixcmp(status->new.name, -1, "/")) {
4923                         report("Cannot display a directory");
4924                         return REQ_NONE;
4925                 }
4926
4927                 if (!prepare_update_file(stage, newpath))
4928                         return REQ_QUIT;
4929                 info = "Untracked file %s";
4930                 break;
4931
4932         case LINE_STAT_HEAD:
4933                 return REQ_NONE;
4934
4935         default:
4936                 die("line type %d not handled in switch", line->type);
4937         }
4938
4939         split = view_is_displayed(view) ? OPEN_SPLIT : 0;
4940         open_view(view, REQ_VIEW_STAGE, OPEN_PREPARED | split);
4941         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
4942                 if (status) {
4943                         stage_status = *status;
4944                 } else {
4945                         memset(&stage_status, 0, sizeof(stage_status));
4946                 }
4947
4948                 stage_line_type = line->type;
4949                 stage_chunks = 0;
4950                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
4951         }
4952
4953         return REQ_NONE;
4954 }
4955
4956 static bool
4957 status_exists(struct status *status, enum line_type type)
4958 {
4959         struct view *view = VIEW(REQ_VIEW_STATUS);
4960         unsigned long lineno;
4961
4962         for (lineno = 0; lineno < view->lines; lineno++) {
4963                 struct line *line = &view->line[lineno];
4964                 struct status *pos = line->data;
4965
4966                 if (line->type != type)
4967                         continue;
4968                 if (!pos && (!status || !status->status) && line[1].data) {
4969                         select_view_line(view, lineno);
4970                         return TRUE;
4971                 }
4972                 if (pos && !strcmp(status->new.name, pos->new.name)) {
4973                         select_view_line(view, lineno);
4974                         return TRUE;
4975                 }
4976         }
4977
4978         return FALSE;
4979 }
4980
4981
4982 static bool
4983 status_update_prepare(struct io *io, enum line_type type)
4984 {
4985         const char *staged_argv[] = {
4986                 "git", "update-index", "-z", "--index-info", NULL
4987         };
4988         const char *others_argv[] = {
4989                 "git", "update-index", "-z", "--add", "--remove", "--stdin", NULL
4990         };
4991
4992         switch (type) {
4993         case LINE_STAT_STAGED:
4994                 return run_io(io, staged_argv, opt_cdup, IO_WR);
4995
4996         case LINE_STAT_UNSTAGED:
4997                 return run_io(io, others_argv, opt_cdup, IO_WR);
4998
4999         case LINE_STAT_UNTRACKED:
5000                 return run_io(io, others_argv, NULL, IO_WR);
5001
5002         default:
5003                 die("line type %d not handled in switch", type);
5004                 return FALSE;
5005         }
5006 }
5007
5008 static bool
5009 status_update_write(struct io *io, struct status *status, enum line_type type)
5010 {
5011         char buf[SIZEOF_STR];
5012         size_t bufsize = 0;
5013
5014         switch (type) {
5015         case LINE_STAT_STAGED:
5016                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
5017                                         status->old.mode,
5018                                         status->old.rev,
5019                                         status->old.name, 0))
5020                         return FALSE;
5021                 break;
5022
5023         case LINE_STAT_UNSTAGED:
5024         case LINE_STAT_UNTRACKED:
5025                 if (!string_format_from(buf, &bufsize, "%s%c", status->new.name, 0))
5026                         return FALSE;
5027                 break;
5028
5029         default:
5030                 die("line type %d not handled in switch", type);
5031         }
5032
5033         return io_write(io, buf, bufsize);
5034 }
5035
5036 static bool
5037 status_update_file(struct status *status, enum line_type type)
5038 {
5039         struct io io = {};
5040         bool result;
5041
5042         if (!status_update_prepare(&io, type))
5043                 return FALSE;
5044
5045         result = status_update_write(&io, status, type);
5046         done_io(&io);
5047         return result;
5048 }
5049
5050 static bool
5051 status_update_files(struct view *view, struct line *line)
5052 {
5053         struct io io = {};
5054         bool result = TRUE;
5055         struct line *pos = view->line + view->lines;
5056         int files = 0;
5057         int file, done;
5058
5059         if (!status_update_prepare(&io, line->type))
5060                 return FALSE;
5061
5062         for (pos = line; pos < view->line + view->lines && pos->data; pos++)
5063                 files++;
5064
5065         for (file = 0, done = 0; result && file < files; line++, file++) {
5066                 int almost_done = file * 100 / files;
5067
5068                 if (almost_done > done) {
5069                         done = almost_done;
5070                         string_format(view->ref, "updating file %u of %u (%d%% done)",
5071                                       file, files, done);
5072                         update_view_title(view);
5073                 }
5074                 result = status_update_write(&io, line->data, line->type);
5075         }
5076
5077         done_io(&io);
5078         return result;
5079 }
5080
5081 static bool
5082 status_update(struct view *view)
5083 {
5084         struct line *line = &view->line[view->lineno];
5085
5086         assert(view->lines);
5087
5088         if (!line->data) {
5089                 /* This should work even for the "On branch" line. */
5090                 if (line < view->line + view->lines && !line[1].data) {
5091                         report("Nothing to update");
5092                         return FALSE;
5093                 }
5094
5095                 if (!status_update_files(view, line + 1)) {
5096                         report("Failed to update file status");
5097                         return FALSE;
5098                 }
5099
5100         } else if (!status_update_file(line->data, line->type)) {
5101                 report("Failed to update file status");
5102                 return FALSE;
5103         }
5104
5105         return TRUE;
5106 }
5107
5108 static bool
5109 status_revert(struct status *status, enum line_type type, bool has_none)
5110 {
5111         if (!status || type != LINE_STAT_UNSTAGED) {
5112                 if (type == LINE_STAT_STAGED) {
5113                         report("Cannot revert changes to staged files");
5114                 } else if (type == LINE_STAT_UNTRACKED) {
5115                         report("Cannot revert changes to untracked files");
5116                 } else if (has_none) {
5117                         report("Nothing to revert");
5118                 } else {
5119                         report("Cannot revert changes to multiple files");
5120                 }
5121                 return FALSE;
5122
5123         } else {
5124                 char mode[10] = "100644";
5125                 const char *reset_argv[] = {
5126                         "git", "update-index", "--cacheinfo", mode,
5127                                 status->old.rev, status->old.name, NULL
5128                 };
5129                 const char *checkout_argv[] = {
5130                         "git", "checkout", "--", status->old.name, NULL
5131                 };
5132
5133                 if (!prompt_yesno("Are you sure you want to overwrite any changes?"))
5134                         return FALSE;
5135                 string_format(mode, "%o", status->old.mode);
5136                 return (status->status != 'U' || run_io_fg(reset_argv, opt_cdup)) &&
5137                         run_io_fg(checkout_argv, opt_cdup);
5138         }
5139 }
5140
5141 static enum request
5142 status_request(struct view *view, enum request request, struct line *line)
5143 {
5144         struct status *status = line->data;
5145
5146         switch (request) {
5147         case REQ_STATUS_UPDATE:
5148                 if (!status_update(view))
5149                         return REQ_NONE;
5150                 break;
5151
5152         case REQ_STATUS_REVERT:
5153                 if (!status_revert(status, line->type, status_has_none(view, line)))
5154                         return REQ_NONE;
5155                 break;
5156
5157         case REQ_STATUS_MERGE:
5158                 if (!status || status->status != 'U') {
5159                         report("Merging only possible for files with unmerged status ('U').");
5160                         return REQ_NONE;
5161                 }
5162                 open_mergetool(status->new.name);
5163                 break;
5164
5165         case REQ_EDIT:
5166                 if (!status)
5167                         return request;
5168                 if (status->status == 'D') {
5169                         report("File has been deleted.");
5170                         return REQ_NONE;
5171                 }
5172
5173                 open_editor(status->status != '?', status->new.name);
5174                 break;
5175
5176         case REQ_VIEW_BLAME:
5177                 if (status) {
5178                         string_copy(opt_file, status->new.name);
5179                         opt_ref[0] = 0;
5180                 }
5181                 return request;
5182
5183         case REQ_ENTER:
5184                 /* After returning the status view has been split to
5185                  * show the stage view. No further reloading is
5186                  * necessary. */
5187                 status_enter(view, line);
5188                 return REQ_NONE;
5189
5190         case REQ_REFRESH:
5191                 /* Simply reload the view. */
5192                 break;
5193
5194         default:
5195                 return request;
5196         }
5197
5198         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
5199
5200         return REQ_NONE;
5201 }
5202
5203 static void
5204 status_select(struct view *view, struct line *line)
5205 {
5206         struct status *status = line->data;
5207         char file[SIZEOF_STR] = "all files";
5208         const char *text;
5209         const char *key;
5210
5211         if (status && !string_format(file, "'%s'", status->new.name))
5212                 return;
5213
5214         if (!status && line[1].type == LINE_STAT_NONE)
5215                 line++;
5216
5217         switch (line->type) {
5218         case LINE_STAT_STAGED:
5219                 text = "Press %s to unstage %s for commit";
5220                 break;
5221
5222         case LINE_STAT_UNSTAGED:
5223                 text = "Press %s to stage %s for commit";
5224                 break;
5225
5226         case LINE_STAT_UNTRACKED:
5227                 text = "Press %s to stage %s for addition";
5228                 break;
5229
5230         case LINE_STAT_HEAD:
5231         case LINE_STAT_NONE:
5232                 text = "Nothing to update";
5233                 break;
5234
5235         default:
5236                 die("line type %d not handled in switch", line->type);
5237         }
5238
5239         if (status && status->status == 'U') {
5240                 text = "Press %s to resolve conflict in %s";
5241                 key = get_key(REQ_STATUS_MERGE);
5242
5243         } else {
5244                 key = get_key(REQ_STATUS_UPDATE);
5245         }
5246
5247         string_format(view->ref, text, key, file);
5248 }
5249
5250 static bool
5251 status_grep(struct view *view, struct line *line)
5252 {
5253         struct status *status = line->data;
5254         enum { S_STATUS, S_NAME, S_END } state;
5255         char buf[2] = "?";
5256         regmatch_t pmatch;
5257
5258         if (!status)
5259                 return FALSE;
5260
5261         for (state = S_STATUS; state < S_END; state++) {
5262                 const char *text;
5263
5264                 switch (state) {
5265                 case S_NAME:    text = status->new.name;        break;
5266                 case S_STATUS:
5267                         buf[0] = status->status;
5268                         text = buf;
5269                         break;
5270
5271                 default:
5272                         return FALSE;
5273                 }
5274
5275                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5276                         return TRUE;
5277         }
5278
5279         return FALSE;
5280 }
5281
5282 static struct view_ops status_ops = {
5283         "file",
5284         NULL,
5285         status_open,
5286         NULL,
5287         status_draw,
5288         status_request,
5289         status_grep,
5290         status_select,
5291 };
5292
5293
5294 static bool
5295 stage_diff_write(struct io *io, struct line *line, struct line *end)
5296 {
5297         while (line < end) {
5298                 if (!io_write(io, line->data, strlen(line->data)) ||
5299                     !io_write(io, "\n", 1))
5300                         return FALSE;
5301                 line++;
5302                 if (line->type == LINE_DIFF_CHUNK ||
5303                     line->type == LINE_DIFF_HEADER)
5304                         break;
5305         }
5306
5307         return TRUE;
5308 }
5309
5310 static struct line *
5311 stage_diff_find(struct view *view, struct line *line, enum line_type type)
5312 {
5313         for (; view->line < line; line--)
5314                 if (line->type == type)
5315                         return line;
5316
5317         return NULL;
5318 }
5319
5320 static bool
5321 stage_apply_chunk(struct view *view, struct line *chunk, bool revert)
5322 {
5323         const char *apply_argv[SIZEOF_ARG] = {
5324                 "git", "apply", "--whitespace=nowarn", NULL
5325         };
5326         struct line *diff_hdr;
5327         struct io io = {};
5328         int argc = 3;
5329
5330         diff_hdr = stage_diff_find(view, chunk, LINE_DIFF_HEADER);
5331         if (!diff_hdr)
5332                 return FALSE;
5333
5334         if (!revert)
5335                 apply_argv[argc++] = "--cached";
5336         if (revert || stage_line_type == LINE_STAT_STAGED)
5337                 apply_argv[argc++] = "-R";
5338         apply_argv[argc++] = "-";
5339         apply_argv[argc++] = NULL;
5340         if (!run_io(&io, apply_argv, opt_cdup, IO_WR))
5341                 return FALSE;
5342
5343         if (!stage_diff_write(&io, diff_hdr, chunk) ||
5344             !stage_diff_write(&io, chunk, view->line + view->lines))
5345                 chunk = NULL;
5346
5347         done_io(&io);
5348         run_io_bg(update_index_argv);
5349
5350         return chunk ? TRUE : FALSE;
5351 }
5352
5353 static bool
5354 stage_update(struct view *view, struct line *line)
5355 {
5356         struct line *chunk = NULL;
5357
5358         if (!is_initial_commit() && stage_line_type != LINE_STAT_UNTRACKED)
5359                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
5360
5361         if (chunk) {
5362                 if (!stage_apply_chunk(view, chunk, FALSE)) {
5363                         report("Failed to apply chunk");
5364                         return FALSE;
5365                 }
5366
5367         } else if (!stage_status.status) {
5368                 view = VIEW(REQ_VIEW_STATUS);
5369
5370                 for (line = view->line; line < view->line + view->lines; line++)
5371                         if (line->type == stage_line_type)
5372                                 break;
5373
5374                 if (!status_update_files(view, line + 1)) {
5375                         report("Failed to update files");
5376                         return FALSE;
5377                 }
5378
5379         } else if (!status_update_file(&stage_status, stage_line_type)) {
5380                 report("Failed to update file");
5381                 return FALSE;
5382         }
5383
5384         return TRUE;
5385 }
5386
5387 static bool
5388 stage_revert(struct view *view, struct line *line)
5389 {
5390         struct line *chunk = NULL;
5391
5392         if (!is_initial_commit() && stage_line_type == LINE_STAT_UNSTAGED)
5393                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
5394
5395         if (chunk) {
5396                 if (!prompt_yesno("Are you sure you want to revert changes?"))
5397                         return FALSE;
5398
5399                 if (!stage_apply_chunk(view, chunk, TRUE)) {
5400                         report("Failed to revert chunk");
5401                         return FALSE;
5402                 }
5403                 return TRUE;
5404
5405         } else {
5406                 return status_revert(stage_status.status ? &stage_status : NULL,
5407                                      stage_line_type, FALSE);
5408         }
5409 }
5410
5411
5412 static void
5413 stage_next(struct view *view, struct line *line)
5414 {
5415         int i;
5416
5417         if (!stage_chunks) {
5418                 static size_t alloc = 0;
5419                 int *tmp;
5420
5421                 for (line = view->line; line < view->line + view->lines; line++) {
5422                         if (line->type != LINE_DIFF_CHUNK)
5423                                 continue;
5424
5425                         tmp = realloc_items(stage_chunk, &alloc,
5426                                             stage_chunks, sizeof(*tmp));
5427                         if (!tmp) {
5428                                 report("Allocation failure");
5429                                 return;
5430                         }
5431
5432                         stage_chunk = tmp;
5433                         stage_chunk[stage_chunks++] = line - view->line;
5434                 }
5435         }
5436
5437         for (i = 0; i < stage_chunks; i++) {
5438                 if (stage_chunk[i] > view->lineno) {
5439                         do_scroll_view(view, stage_chunk[i] - view->lineno);
5440                         report("Chunk %d of %d", i + 1, stage_chunks);
5441                         return;
5442                 }
5443         }
5444
5445         report("No next chunk found");
5446 }
5447
5448 static enum request
5449 stage_request(struct view *view, enum request request, struct line *line)
5450 {
5451         switch (request) {
5452         case REQ_STATUS_UPDATE:
5453                 if (!stage_update(view, line))
5454                         return REQ_NONE;
5455                 break;
5456
5457         case REQ_STATUS_REVERT:
5458                 if (!stage_revert(view, line))
5459                         return REQ_NONE;
5460                 break;
5461
5462         case REQ_STAGE_NEXT:
5463                 if (stage_line_type == LINE_STAT_UNTRACKED) {
5464                         report("File is untracked; press %s to add",
5465                                get_key(REQ_STATUS_UPDATE));
5466                         return REQ_NONE;
5467                 }
5468                 stage_next(view, line);
5469                 return REQ_NONE;
5470
5471         case REQ_EDIT:
5472                 if (!stage_status.new.name[0])
5473                         return request;
5474                 if (stage_status.status == 'D') {
5475                         report("File has been deleted.");
5476                         return REQ_NONE;
5477                 }
5478
5479                 open_editor(stage_status.status != '?', stage_status.new.name);
5480                 break;
5481
5482         case REQ_REFRESH:
5483                 /* Reload everything ... */
5484                 break;
5485
5486         case REQ_VIEW_BLAME:
5487                 if (stage_status.new.name[0]) {
5488                         string_copy(opt_file, stage_status.new.name);
5489                         opt_ref[0] = 0;
5490                 }
5491                 return request;
5492
5493         case REQ_ENTER:
5494                 return pager_request(view, request, line);
5495
5496         default:
5497                 return request;
5498         }
5499
5500         VIEW(REQ_VIEW_STATUS)->p_restore = TRUE;
5501         open_view(view, REQ_VIEW_STATUS, OPEN_REFRESH);
5502
5503         /* Check whether the staged entry still exists, and close the
5504          * stage view if it doesn't. */
5505         if (!status_exists(&stage_status, stage_line_type)) {
5506                 status_restore(VIEW(REQ_VIEW_STATUS));
5507                 return REQ_VIEW_CLOSE;
5508         }
5509
5510         if (stage_line_type == LINE_STAT_UNTRACKED) {
5511                 if (!suffixcmp(stage_status.new.name, -1, "/")) {
5512                         report("Cannot display a directory");
5513                         return REQ_NONE;
5514                 }
5515
5516                 if (!prepare_update_file(view, stage_status.new.name)) {
5517                         report("Failed to open file: %s", strerror(errno));
5518                         return REQ_NONE;
5519                 }
5520         }
5521         open_view(view, REQ_VIEW_STAGE, OPEN_REFRESH);
5522
5523         return REQ_NONE;
5524 }
5525
5526 static struct view_ops stage_ops = {
5527         "line",
5528         NULL,
5529         NULL,
5530         pager_read,
5531         pager_draw,
5532         stage_request,
5533         pager_grep,
5534         pager_select,
5535 };
5536
5537
5538 /*
5539  * Revision graph
5540  */
5541
5542 struct commit {
5543         char id[SIZEOF_REV];            /* SHA1 ID. */
5544         char title[128];                /* First line of the commit message. */
5545         char author[75];                /* Author of the commit. */
5546         struct tm time;                 /* Date from the author ident. */
5547         struct ref **refs;              /* Repository references. */
5548         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
5549         size_t graph_size;              /* The width of the graph array. */
5550         bool has_parents;               /* Rewritten --parents seen. */
5551 };
5552
5553 /* Size of rev graph with no  "padding" columns */
5554 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
5555
5556 struct rev_graph {
5557         struct rev_graph *prev, *next, *parents;
5558         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
5559         size_t size;
5560         struct commit *commit;
5561         size_t pos;
5562         unsigned int boundary:1;
5563 };
5564
5565 /* Parents of the commit being visualized. */
5566 static struct rev_graph graph_parents[4];
5567
5568 /* The current stack of revisions on the graph. */
5569 static struct rev_graph graph_stacks[4] = {
5570         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
5571         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
5572         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
5573         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
5574 };
5575
5576 static inline bool
5577 graph_parent_is_merge(struct rev_graph *graph)
5578 {
5579         return graph->parents->size > 1;
5580 }
5581
5582 static inline void
5583 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
5584 {
5585         struct commit *commit = graph->commit;
5586
5587         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
5588                 commit->graph[commit->graph_size++] = symbol;
5589 }
5590
5591 static void
5592 clear_rev_graph(struct rev_graph *graph)
5593 {
5594         graph->boundary = 0;
5595         graph->size = graph->pos = 0;
5596         graph->commit = NULL;
5597         memset(graph->parents, 0, sizeof(*graph->parents));
5598 }
5599
5600 static void
5601 done_rev_graph(struct rev_graph *graph)
5602 {
5603         if (graph_parent_is_merge(graph) &&
5604             graph->pos < graph->size - 1 &&
5605             graph->next->size == graph->size + graph->parents->size - 1) {
5606                 size_t i = graph->pos + graph->parents->size - 1;
5607
5608                 graph->commit->graph_size = i * 2;
5609                 while (i < graph->next->size - 1) {
5610                         append_to_rev_graph(graph, ' ');
5611                         append_to_rev_graph(graph, '\\');
5612                         i++;
5613                 }
5614         }
5615
5616         clear_rev_graph(graph);
5617 }
5618
5619 static void
5620 push_rev_graph(struct rev_graph *graph, const char *parent)
5621 {
5622         int i;
5623
5624         /* "Collapse" duplicate parents lines.
5625          *
5626          * FIXME: This needs to also update update the drawn graph but
5627          * for now it just serves as a method for pruning graph lines. */
5628         for (i = 0; i < graph->size; i++)
5629                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
5630                         return;
5631
5632         if (graph->size < SIZEOF_REVITEMS) {
5633                 string_copy_rev(graph->rev[graph->size++], parent);
5634         }
5635 }
5636
5637 static chtype
5638 get_rev_graph_symbol(struct rev_graph *graph)
5639 {
5640         chtype symbol;
5641
5642         if (graph->boundary)
5643                 symbol = REVGRAPH_BOUND;
5644         else if (graph->parents->size == 0)
5645                 symbol = REVGRAPH_INIT;
5646         else if (graph_parent_is_merge(graph))
5647                 symbol = REVGRAPH_MERGE;
5648         else if (graph->pos >= graph->size)
5649                 symbol = REVGRAPH_BRANCH;
5650         else
5651                 symbol = REVGRAPH_COMMIT;
5652
5653         return symbol;
5654 }
5655
5656 static void
5657 draw_rev_graph(struct rev_graph *graph)
5658 {
5659         struct rev_filler {
5660                 chtype separator, line;
5661         };
5662         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
5663         static struct rev_filler fillers[] = {
5664                 { ' ',  '|' },
5665                 { '`',  '.' },
5666                 { '\'', ' ' },
5667                 { '/',  ' ' },
5668         };
5669         chtype symbol = get_rev_graph_symbol(graph);
5670         struct rev_filler *filler;
5671         size_t i;
5672
5673         if (opt_line_graphics)
5674                 fillers[DEFAULT].line = line_graphics[LINE_GRAPHIC_VLINE];
5675
5676         filler = &fillers[DEFAULT];
5677
5678         for (i = 0; i < graph->pos; i++) {
5679                 append_to_rev_graph(graph, filler->line);
5680                 if (graph_parent_is_merge(graph->prev) &&
5681                     graph->prev->pos == i)
5682                         filler = &fillers[RSHARP];
5683
5684                 append_to_rev_graph(graph, filler->separator);
5685         }
5686
5687         /* Place the symbol for this revision. */
5688         append_to_rev_graph(graph, symbol);
5689
5690         if (graph->prev->size > graph->size)
5691                 filler = &fillers[RDIAG];
5692         else
5693                 filler = &fillers[DEFAULT];
5694
5695         i++;
5696
5697         for (; i < graph->size; i++) {
5698                 append_to_rev_graph(graph, filler->separator);
5699                 append_to_rev_graph(graph, filler->line);
5700                 if (graph_parent_is_merge(graph->prev) &&
5701                     i < graph->prev->pos + graph->parents->size)
5702                         filler = &fillers[RSHARP];
5703                 if (graph->prev->size > graph->size)
5704                         filler = &fillers[LDIAG];
5705         }
5706
5707         if (graph->prev->size > graph->size) {
5708                 append_to_rev_graph(graph, filler->separator);
5709                 if (filler->line != ' ')
5710                         append_to_rev_graph(graph, filler->line);
5711         }
5712 }
5713
5714 /* Prepare the next rev graph */
5715 static void
5716 prepare_rev_graph(struct rev_graph *graph)
5717 {
5718         size_t i;
5719
5720         /* First, traverse all lines of revisions up to the active one. */
5721         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
5722                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
5723                         break;
5724
5725                 push_rev_graph(graph->next, graph->rev[graph->pos]);
5726         }
5727
5728         /* Interleave the new revision parent(s). */
5729         for (i = 0; !graph->boundary && i < graph->parents->size; i++)
5730                 push_rev_graph(graph->next, graph->parents->rev[i]);
5731
5732         /* Lastly, put any remaining revisions. */
5733         for (i = graph->pos + 1; i < graph->size; i++)
5734                 push_rev_graph(graph->next, graph->rev[i]);
5735 }
5736
5737 static void
5738 update_rev_graph(struct view *view, struct rev_graph *graph)
5739 {
5740         /* If this is the finalizing update ... */
5741         if (graph->commit)
5742                 prepare_rev_graph(graph);
5743
5744         /* Graph visualization needs a one rev look-ahead,
5745          * so the first update doesn't visualize anything. */
5746         if (!graph->prev->commit)
5747                 return;
5748
5749         if (view->lines > 2)
5750                 view->line[view->lines - 3].dirty = 1;
5751         if (view->lines > 1)
5752                 view->line[view->lines - 2].dirty = 1;
5753         draw_rev_graph(graph->prev);
5754         done_rev_graph(graph->prev->prev);
5755 }
5756
5757
5758 /*
5759  * Main view backend
5760  */
5761
5762 static const char *main_argv[SIZEOF_ARG] = {
5763         "git", "log", "--no-color", "--pretty=raw", "--parents",
5764                       "--topo-order", "%(head)", NULL
5765 };
5766
5767 static bool
5768 main_draw(struct view *view, struct line *line, unsigned int lineno)
5769 {
5770         struct commit *commit = line->data;
5771
5772         if (!*commit->author)
5773                 return FALSE;
5774
5775         if (opt_date && draw_date(view, &commit->time))
5776                 return TRUE;
5777
5778         if (opt_author && draw_author(view, commit->author))
5779                 return TRUE;
5780
5781         if (opt_rev_graph && commit->graph_size &&
5782             draw_graphic(view, LINE_MAIN_REVGRAPH, commit->graph, commit->graph_size))
5783                 return TRUE;
5784
5785         if (opt_show_refs && commit->refs) {
5786                 size_t i = 0;
5787
5788                 do {
5789                         enum line_type type;
5790
5791                         if (commit->refs[i]->head)
5792                                 type = LINE_MAIN_HEAD;
5793                         else if (commit->refs[i]->ltag)
5794                                 type = LINE_MAIN_LOCAL_TAG;
5795                         else if (commit->refs[i]->tag)
5796                                 type = LINE_MAIN_TAG;
5797                         else if (commit->refs[i]->tracked)
5798                                 type = LINE_MAIN_TRACKED;
5799                         else if (commit->refs[i]->remote)
5800                                 type = LINE_MAIN_REMOTE;
5801                         else
5802                                 type = LINE_MAIN_REF;
5803
5804                         if (draw_text(view, type, "[", TRUE) ||
5805                             draw_text(view, type, commit->refs[i]->name, TRUE) ||
5806                             draw_text(view, type, "]", TRUE))
5807                                 return TRUE;
5808
5809                         if (draw_text(view, LINE_DEFAULT, " ", TRUE))
5810                                 return TRUE;
5811                 } while (commit->refs[i++]->next);
5812         }
5813
5814         draw_text(view, LINE_DEFAULT, commit->title, TRUE);
5815         return TRUE;
5816 }
5817
5818 /* Reads git log --pretty=raw output and parses it into the commit struct. */
5819 static bool
5820 main_read(struct view *view, char *line)
5821 {
5822         static struct rev_graph *graph = graph_stacks;
5823         enum line_type type;
5824         struct commit *commit;
5825
5826         if (!line) {
5827                 int i;
5828
5829                 if (!view->lines && !view->parent)
5830                         die("No revisions match the given arguments.");
5831                 if (view->lines > 0) {
5832                         commit = view->line[view->lines - 1].data;
5833                         view->line[view->lines - 1].dirty = 1;
5834                         if (!*commit->author) {
5835                                 view->lines--;
5836                                 free(commit);
5837                                 graph->commit = NULL;
5838                         }
5839                 }
5840                 update_rev_graph(view, graph);
5841
5842                 for (i = 0; i < ARRAY_SIZE(graph_stacks); i++)
5843                         clear_rev_graph(&graph_stacks[i]);
5844                 return TRUE;
5845         }
5846
5847         type = get_line_type(line);
5848         if (type == LINE_COMMIT) {
5849                 commit = calloc(1, sizeof(struct commit));
5850                 if (!commit)
5851                         return FALSE;
5852
5853                 line += STRING_SIZE("commit ");
5854                 if (*line == '-') {
5855                         graph->boundary = 1;
5856                         line++;
5857                 }
5858
5859                 string_copy_rev(commit->id, line);
5860                 commit->refs = get_refs(commit->id);
5861                 graph->commit = commit;
5862                 add_line_data(view, commit, LINE_MAIN_COMMIT);
5863
5864                 while ((line = strchr(line, ' '))) {
5865                         line++;
5866                         push_rev_graph(graph->parents, line);
5867                         commit->has_parents = TRUE;
5868                 }
5869                 return TRUE;
5870         }
5871
5872         if (!view->lines)
5873                 return TRUE;
5874         commit = view->line[view->lines - 1].data;
5875
5876         switch (type) {
5877         case LINE_PARENT:
5878                 if (commit->has_parents)
5879                         break;
5880                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
5881                 break;
5882
5883         case LINE_AUTHOR:
5884                 parse_author_line(line + STRING_SIZE("author "),
5885                                   commit->author, sizeof(commit->author),
5886                                   &commit->time);
5887                 update_rev_graph(view, graph);
5888                 graph = graph->next;
5889                 break;
5890
5891         default:
5892                 /* Fill in the commit title if it has not already been set. */
5893                 if (commit->title[0])
5894                         break;
5895
5896                 /* Require titles to start with a non-space character at the
5897                  * offset used by git log. */
5898                 if (strncmp(line, "    ", 4))
5899                         break;
5900                 line += 4;
5901                 /* Well, if the title starts with a whitespace character,
5902                  * try to be forgiving.  Otherwise we end up with no title. */
5903                 while (isspace(*line))
5904                         line++;
5905                 if (*line == '\0')
5906                         break;
5907                 /* FIXME: More graceful handling of titles; append "..." to
5908                  * shortened titles, etc. */
5909
5910                 string_expand(commit->title, sizeof(commit->title), line, 1);
5911                 view->line[view->lines - 1].dirty = 1;
5912         }
5913
5914         return TRUE;
5915 }
5916
5917 static enum request
5918 main_request(struct view *view, enum request request, struct line *line)
5919 {
5920         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
5921
5922         switch (request) {
5923         case REQ_ENTER:
5924                 open_view(view, REQ_VIEW_DIFF, flags);
5925                 break;
5926         case REQ_REFRESH:
5927                 load_refs();
5928                 open_view(view, REQ_VIEW_MAIN, OPEN_REFRESH);
5929                 break;
5930         default:
5931                 return request;
5932         }
5933
5934         return REQ_NONE;
5935 }
5936
5937 static bool
5938 grep_refs(struct ref **refs, regex_t *regex)
5939 {
5940         regmatch_t pmatch;
5941         size_t i = 0;
5942
5943         if (!refs)
5944                 return FALSE;
5945         do {
5946                 if (regexec(regex, refs[i]->name, 1, &pmatch, 0) != REG_NOMATCH)
5947                         return TRUE;
5948         } while (refs[i++]->next);
5949
5950         return FALSE;
5951 }
5952
5953 static bool
5954 main_grep(struct view *view, struct line *line)
5955 {
5956         struct commit *commit = line->data;
5957         enum { S_TITLE, S_AUTHOR, S_DATE, S_REFS, S_END } state;
5958         char buf[DATE_COLS + 1];
5959         regmatch_t pmatch;
5960
5961         for (state = S_TITLE; state < S_END; state++) {
5962                 char *text;
5963
5964                 switch (state) {
5965                 case S_TITLE:   text = commit->title;   break;
5966                 case S_AUTHOR:
5967                         if (!opt_author)
5968                                 continue;
5969                         text = commit->author;
5970                         break;
5971                 case S_DATE:
5972                         if (!opt_date)
5973                                 continue;
5974                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
5975                                 continue;
5976                         text = buf;
5977                         break;
5978                 case S_REFS:
5979                         if (!opt_show_refs)
5980                                 continue;
5981                         if (grep_refs(commit->refs, view->regex) == TRUE)
5982                                 return TRUE;
5983                         continue;
5984                 default:
5985                         return FALSE;
5986                 }
5987
5988                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5989                         return TRUE;
5990         }
5991
5992         return FALSE;
5993 }
5994
5995 static void
5996 main_select(struct view *view, struct line *line)
5997 {
5998         struct commit *commit = line->data;
5999
6000         string_copy_rev(view->ref, commit->id);
6001         string_copy_rev(ref_commit, view->ref);
6002 }
6003
6004 static struct view_ops main_ops = {
6005         "commit",
6006         main_argv,
6007         NULL,
6008         main_read,
6009         main_draw,
6010         main_request,
6011         main_grep,
6012         main_select,
6013 };
6014
6015
6016 /*
6017  * Unicode / UTF-8 handling
6018  *
6019  * NOTE: Much of the following code for dealing with Unicode is derived from
6020  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
6021  * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
6022  */
6023
6024 static inline int
6025 unicode_width(unsigned long c)
6026 {
6027         if (c >= 0x1100 &&
6028            (c <= 0x115f                         /* Hangul Jamo */
6029             || c == 0x2329
6030             || c == 0x232a
6031             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
6032                                                 /* CJK ... Yi */
6033             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
6034             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
6035             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
6036             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
6037             || (c >= 0xffe0  && c <= 0xffe6)
6038             || (c >= 0x20000 && c <= 0x2fffd)
6039             || (c >= 0x30000 && c <= 0x3fffd)))
6040                 return 2;
6041
6042         if (c == '\t')
6043                 return opt_tab_size;
6044
6045         return 1;
6046 }
6047
6048 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
6049  * Illegal bytes are set one. */
6050 static const unsigned char utf8_bytes[256] = {
6051         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,
6052         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,
6053         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,
6054         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,
6055         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,
6056         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,
6057         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,
6058         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,
6059 };
6060
6061 /* Decode UTF-8 multi-byte representation into a Unicode character. */
6062 static inline unsigned long
6063 utf8_to_unicode(const char *string, size_t length)
6064 {
6065         unsigned long unicode;
6066
6067         switch (length) {
6068         case 1:
6069                 unicode  =   string[0];
6070                 break;
6071         case 2:
6072                 unicode  =  (string[0] & 0x1f) << 6;
6073                 unicode +=  (string[1] & 0x3f);
6074                 break;
6075         case 3:
6076                 unicode  =  (string[0] & 0x0f) << 12;
6077                 unicode += ((string[1] & 0x3f) << 6);
6078                 unicode +=  (string[2] & 0x3f);
6079                 break;
6080         case 4:
6081                 unicode  =  (string[0] & 0x0f) << 18;
6082                 unicode += ((string[1] & 0x3f) << 12);
6083                 unicode += ((string[2] & 0x3f) << 6);
6084                 unicode +=  (string[3] & 0x3f);
6085                 break;
6086         case 5:
6087                 unicode  =  (string[0] & 0x0f) << 24;
6088                 unicode += ((string[1] & 0x3f) << 18);
6089                 unicode += ((string[2] & 0x3f) << 12);
6090                 unicode += ((string[3] & 0x3f) << 6);
6091                 unicode +=  (string[4] & 0x3f);
6092                 break;
6093         case 6:
6094                 unicode  =  (string[0] & 0x01) << 30;
6095                 unicode += ((string[1] & 0x3f) << 24);
6096                 unicode += ((string[2] & 0x3f) << 18);
6097                 unicode += ((string[3] & 0x3f) << 12);
6098                 unicode += ((string[4] & 0x3f) << 6);
6099                 unicode +=  (string[5] & 0x3f);
6100                 break;
6101         default:
6102                 die("Invalid Unicode length");
6103         }
6104
6105         /* Invalid characters could return the special 0xfffd value but NUL
6106          * should be just as good. */
6107         return unicode > 0xffff ? 0 : unicode;
6108 }
6109
6110 /* Calculates how much of string can be shown within the given maximum width
6111  * and sets trimmed parameter to non-zero value if all of string could not be
6112  * shown. If the reserve flag is TRUE, it will reserve at least one
6113  * trailing character, which can be useful when drawing a delimiter.
6114  *
6115  * Returns the number of bytes to output from string to satisfy max_width. */
6116 static size_t
6117 utf8_length(const char **start, size_t skip, int *width, size_t max_width, int *trimmed, bool reserve)
6118 {
6119         const char *string = *start;
6120         const char *end = strchr(string, '\0');
6121         unsigned char last_bytes = 0;
6122         size_t last_ucwidth = 0;
6123
6124         *width = 0;
6125         *trimmed = 0;
6126
6127         while (string < end) {
6128                 int c = *(unsigned char *) string;
6129                 unsigned char bytes = utf8_bytes[c];
6130                 size_t ucwidth;
6131                 unsigned long unicode;
6132
6133                 if (string + bytes > end)
6134                         break;
6135
6136                 /* Change representation to figure out whether
6137                  * it is a single- or double-width character. */
6138
6139                 unicode = utf8_to_unicode(string, bytes);
6140                 /* FIXME: Graceful handling of invalid Unicode character. */
6141                 if (!unicode)
6142                         break;
6143
6144                 ucwidth = unicode_width(unicode);
6145                 if (skip > 0) {
6146                         skip -= ucwidth <= skip ? ucwidth : skip;
6147                         *start += bytes;
6148                 }
6149                 *width  += ucwidth;
6150                 if (*width > max_width) {
6151                         *trimmed = 1;
6152                         *width -= ucwidth;
6153                         if (reserve && *width == max_width) {
6154                                 string -= last_bytes;
6155                                 *width -= last_ucwidth;
6156                         }
6157                         break;
6158                 }
6159
6160                 string  += bytes;
6161                 last_bytes = ucwidth ? bytes : 0;
6162                 last_ucwidth = ucwidth;
6163         }
6164
6165         return string - *start;
6166 }
6167
6168
6169 /*
6170  * Status management
6171  */
6172
6173 /* Whether or not the curses interface has been initialized. */
6174 static bool cursed = FALSE;
6175
6176 /* Terminal hacks and workarounds. */
6177 static bool use_scroll_redrawwin;
6178 static bool use_scroll_status_wclear;
6179
6180 /* The status window is used for polling keystrokes. */
6181 static WINDOW *status_win;
6182
6183 /* Reading from the prompt? */
6184 static bool input_mode = FALSE;
6185
6186 static bool status_empty = FALSE;
6187
6188 /* Update status and title window. */
6189 static void
6190 report(const char *msg, ...)
6191 {
6192         struct view *view = display[current_view];
6193
6194         if (input_mode)
6195                 return;
6196
6197         if (!view) {
6198                 char buf[SIZEOF_STR];
6199                 va_list args;
6200
6201                 va_start(args, msg);
6202                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
6203                         buf[sizeof(buf) - 1] = 0;
6204                         buf[sizeof(buf) - 2] = '.';
6205                         buf[sizeof(buf) - 3] = '.';
6206                         buf[sizeof(buf) - 4] = '.';
6207                 }
6208                 va_end(args);
6209                 die("%s", buf);
6210         }
6211
6212         if (!status_empty || *msg) {
6213                 va_list args;
6214
6215                 va_start(args, msg);
6216
6217                 wmove(status_win, 0, 0);
6218                 if (view->has_scrolled && use_scroll_status_wclear)
6219                         wclear(status_win);
6220                 if (*msg) {
6221                         vwprintw(status_win, msg, args);
6222                         status_empty = FALSE;
6223                 } else {
6224                         status_empty = TRUE;
6225                 }
6226                 wclrtoeol(status_win);
6227                 wnoutrefresh(status_win);
6228
6229                 va_end(args);
6230         }
6231
6232         update_view_title(view);
6233 }
6234
6235 /* Controls when nodelay should be in effect when polling user input. */
6236 static void
6237 set_nonblocking_input(bool loading)
6238 {
6239         static unsigned int loading_views;
6240
6241         if ((loading == FALSE && loading_views-- == 1) ||
6242             (loading == TRUE  && loading_views++ == 0))
6243                 nodelay(status_win, loading);
6244 }
6245
6246 static void
6247 init_display(void)
6248 {
6249         const char *term;
6250         int x, y;
6251
6252         /* Initialize the curses library */
6253         if (isatty(STDIN_FILENO)) {
6254                 cursed = !!initscr();
6255                 opt_tty = stdin;
6256         } else {
6257                 /* Leave stdin and stdout alone when acting as a pager. */
6258                 opt_tty = fopen("/dev/tty", "r+");
6259                 if (!opt_tty)
6260                         die("Failed to open /dev/tty");
6261                 cursed = !!newterm(NULL, opt_tty, opt_tty);
6262         }
6263
6264         if (!cursed)
6265                 die("Failed to initialize curses");
6266
6267         nonl();         /* Disable conversion and detect newlines from input. */
6268         cbreak();       /* Take input chars one at a time, no wait for \n */
6269         noecho();       /* Don't echo input */
6270         leaveok(stdscr, FALSE);
6271
6272         if (has_colors())
6273                 init_colors();
6274
6275         getmaxyx(stdscr, y, x);
6276         status_win = newwin(1, 0, y - 1, 0);
6277         if (!status_win)
6278                 die("Failed to create status window");
6279
6280         /* Enable keyboard mapping */
6281         keypad(status_win, TRUE);
6282         wbkgdset(status_win, get_line_attr(LINE_STATUS));
6283
6284         TABSIZE = opt_tab_size;
6285         if (opt_line_graphics) {
6286                 line_graphics[LINE_GRAPHIC_VLINE] = ACS_VLINE;
6287         }
6288
6289         term = getenv("XTERM_VERSION") ? NULL : getenv("COLORTERM");
6290         if (term && !strcmp(term, "gnome-terminal")) {
6291                 /* In the gnome-terminal-emulator, the message from
6292                  * scrolling up one line when impossible followed by
6293                  * scrolling down one line causes corruption of the
6294                  * status line. This is fixed by calling wclear. */
6295                 use_scroll_status_wclear = TRUE;
6296                 use_scroll_redrawwin = FALSE;
6297
6298         } else if (term && !strcmp(term, "xrvt-xpm")) {
6299                 /* No problems with full optimizations in xrvt-(unicode)
6300                  * and aterm. */
6301                 use_scroll_status_wclear = use_scroll_redrawwin = FALSE;
6302
6303         } else {
6304                 /* When scrolling in (u)xterm the last line in the
6305                  * scrolling direction will update slowly. */
6306                 use_scroll_redrawwin = TRUE;
6307                 use_scroll_status_wclear = FALSE;
6308         }
6309 }
6310
6311 static int
6312 get_input(int prompt_position)
6313 {
6314         struct view *view;
6315         int i, key, cursor_y, cursor_x;
6316
6317         if (prompt_position)
6318                 input_mode = TRUE;
6319
6320         while (TRUE) {
6321                 foreach_view (view, i) {
6322                         update_view(view);
6323                         if (view_is_displayed(view) && view->has_scrolled &&
6324                             use_scroll_redrawwin)
6325                                 redrawwin(view->win);
6326                         view->has_scrolled = FALSE;
6327                 }
6328
6329                 /* Update the cursor position. */
6330                 if (prompt_position) {
6331                         getbegyx(status_win, cursor_y, cursor_x);
6332                         cursor_x = prompt_position;
6333                 } else {
6334                         view = display[current_view];
6335                         getbegyx(view->win, cursor_y, cursor_x);
6336                         cursor_x = view->width - 1;
6337                         cursor_y += view->lineno - view->offset;
6338                 }
6339                 setsyx(cursor_y, cursor_x);
6340
6341                 /* Refresh, accept single keystroke of input */
6342                 doupdate();
6343                 key = wgetch(status_win);
6344
6345                 /* wgetch() with nodelay() enabled returns ERR when
6346                  * there's no input. */
6347                 if (key == ERR) {
6348
6349                 } else if (key == KEY_RESIZE) {
6350                         int height, width;
6351
6352                         getmaxyx(stdscr, height, width);
6353
6354                         wresize(status_win, 1, width);
6355                         mvwin(status_win, height - 1, 0);
6356                         wnoutrefresh(status_win);
6357                         resize_display();
6358                         redraw_display(TRUE);
6359
6360                 } else {
6361                         input_mode = FALSE;
6362                         return key;
6363                 }
6364         }
6365 }
6366
6367 static char *
6368 prompt_input(const char *prompt, input_handler handler, void *data)
6369 {
6370         enum input_status status = INPUT_OK;
6371         static char buf[SIZEOF_STR];
6372         size_t pos = 0;
6373
6374         buf[pos] = 0;
6375
6376         while (status == INPUT_OK || status == INPUT_SKIP) {
6377                 int key;
6378
6379                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
6380                 wclrtoeol(status_win);
6381
6382                 key = get_input(pos + 1);
6383                 switch (key) {
6384                 case KEY_RETURN:
6385                 case KEY_ENTER:
6386                 case '\n':
6387                         status = pos ? INPUT_STOP : INPUT_CANCEL;
6388                         break;
6389
6390                 case KEY_BACKSPACE:
6391                         if (pos > 0)
6392                                 buf[--pos] = 0;
6393                         else
6394                                 status = INPUT_CANCEL;
6395                         break;
6396
6397                 case KEY_ESC:
6398                         status = INPUT_CANCEL;
6399                         break;
6400
6401                 default:
6402                         if (pos >= sizeof(buf)) {
6403                                 report("Input string too long");
6404                                 return NULL;
6405                         }
6406
6407                         status = handler(data, buf, key);
6408                         if (status == INPUT_OK)
6409                                 buf[pos++] = (char) key;
6410                 }
6411         }
6412
6413         /* Clear the status window */
6414         status_empty = FALSE;
6415         report("");
6416
6417         if (status == INPUT_CANCEL)
6418                 return NULL;
6419
6420         buf[pos++] = 0;
6421
6422         return buf;
6423 }
6424
6425 static enum input_status
6426 prompt_yesno_handler(void *data, char *buf, int c)
6427 {
6428         if (c == 'y' || c == 'Y')
6429                 return INPUT_STOP;
6430         if (c == 'n' || c == 'N')
6431                 return INPUT_CANCEL;
6432         return INPUT_SKIP;
6433 }
6434
6435 static bool
6436 prompt_yesno(const char *prompt)
6437 {
6438         char prompt2[SIZEOF_STR];
6439
6440         if (!string_format(prompt2, "%s [Yy/Nn]", prompt))
6441                 return FALSE;
6442
6443         return !!prompt_input(prompt2, prompt_yesno_handler, NULL);
6444 }
6445
6446 static enum input_status
6447 read_prompt_handler(void *data, char *buf, int c)
6448 {
6449         return isprint(c) ? INPUT_OK : INPUT_SKIP;
6450 }
6451
6452 static char *
6453 read_prompt(const char *prompt)
6454 {
6455         return prompt_input(prompt, read_prompt_handler, NULL);
6456 }
6457
6458 /*
6459  * Repository properties
6460  */
6461
6462 static struct ref *refs = NULL;
6463 static size_t refs_alloc = 0;
6464 static size_t refs_size = 0;
6465
6466 /* Id <-> ref store */
6467 static struct ref ***id_refs = NULL;
6468 static size_t id_refs_alloc = 0;
6469 static size_t id_refs_size = 0;
6470
6471 static int
6472 compare_refs(const void *ref1_, const void *ref2_)
6473 {
6474         const struct ref *ref1 = *(const struct ref **)ref1_;
6475         const struct ref *ref2 = *(const struct ref **)ref2_;
6476
6477         if (ref1->tag != ref2->tag)
6478                 return ref2->tag - ref1->tag;
6479         if (ref1->ltag != ref2->ltag)
6480                 return ref2->ltag - ref2->ltag;
6481         if (ref1->head != ref2->head)
6482                 return ref2->head - ref1->head;
6483         if (ref1->tracked != ref2->tracked)
6484                 return ref2->tracked - ref1->tracked;
6485         if (ref1->remote != ref2->remote)
6486                 return ref2->remote - ref1->remote;
6487         return strcmp(ref1->name, ref2->name);
6488 }
6489
6490 static struct ref **
6491 get_refs(const char *id)
6492 {
6493         struct ref ***tmp_id_refs;
6494         struct ref **ref_list = NULL;
6495         size_t ref_list_alloc = 0;
6496         size_t ref_list_size = 0;
6497         size_t i;
6498
6499         for (i = 0; i < id_refs_size; i++)
6500                 if (!strcmp(id, id_refs[i][0]->id))
6501                         return id_refs[i];
6502
6503         tmp_id_refs = realloc_items(id_refs, &id_refs_alloc, id_refs_size + 1,
6504                                     sizeof(*id_refs));
6505         if (!tmp_id_refs)
6506                 return NULL;
6507
6508         id_refs = tmp_id_refs;
6509
6510         for (i = 0; i < refs_size; i++) {
6511                 struct ref **tmp;
6512
6513                 if (strcmp(id, refs[i].id))
6514                         continue;
6515
6516                 tmp = realloc_items(ref_list, &ref_list_alloc,
6517                                     ref_list_size + 1, sizeof(*ref_list));
6518                 if (!tmp) {
6519                         if (ref_list)
6520                                 free(ref_list);
6521                         return NULL;
6522                 }
6523
6524                 ref_list = tmp;
6525                 ref_list[ref_list_size] = &refs[i];
6526                 /* XXX: The properties of the commit chains ensures that we can
6527                  * safely modify the shared ref. The repo references will
6528                  * always be similar for the same id. */
6529                 ref_list[ref_list_size]->next = 1;
6530
6531                 ref_list_size++;
6532         }
6533
6534         if (ref_list) {
6535                 qsort(ref_list, ref_list_size, sizeof(*ref_list), compare_refs);
6536                 ref_list[ref_list_size - 1]->next = 0;
6537                 id_refs[id_refs_size++] = ref_list;
6538         }
6539
6540         return ref_list;
6541 }
6542
6543 static int
6544 read_ref(char *id, size_t idlen, char *name, size_t namelen)
6545 {
6546         struct ref *ref;
6547         bool tag = FALSE;
6548         bool ltag = FALSE;
6549         bool remote = FALSE;
6550         bool tracked = FALSE;
6551         bool check_replace = FALSE;
6552         bool head = FALSE;
6553
6554         if (!prefixcmp(name, "refs/tags/")) {
6555                 if (!suffixcmp(name, namelen, "^{}")) {
6556                         namelen -= 3;
6557                         name[namelen] = 0;
6558                         if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
6559                                 check_replace = TRUE;
6560                 } else {
6561                         ltag = TRUE;
6562                 }
6563
6564                 tag = TRUE;
6565                 namelen -= STRING_SIZE("refs/tags/");
6566                 name    += STRING_SIZE("refs/tags/");
6567
6568         } else if (!prefixcmp(name, "refs/remotes/")) {
6569                 remote = TRUE;
6570                 namelen -= STRING_SIZE("refs/remotes/");
6571                 name    += STRING_SIZE("refs/remotes/");
6572                 tracked  = !strcmp(opt_remote, name);
6573
6574         } else if (!prefixcmp(name, "refs/heads/")) {
6575                 namelen -= STRING_SIZE("refs/heads/");
6576                 name    += STRING_SIZE("refs/heads/");
6577                 head     = !strncmp(opt_head, name, namelen);
6578
6579         } else if (!strcmp(name, "HEAD")) {
6580                 string_ncopy(opt_head_rev, id, idlen);
6581                 return OK;
6582         }
6583
6584         if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
6585                 /* it's an annotated tag, replace the previous SHA1 with the
6586                  * resolved commit id; relies on the fact git-ls-remote lists
6587                  * the commit id of an annotated tag right before the commit id
6588                  * it points to. */
6589                 refs[refs_size - 1].ltag = ltag;
6590                 string_copy_rev(refs[refs_size - 1].id, id);
6591
6592                 return OK;
6593         }
6594         refs = realloc_items(refs, &refs_alloc, refs_size + 1, sizeof(*refs));
6595         if (!refs)
6596                 return ERR;
6597
6598         ref = &refs[refs_size++];
6599         ref->name = malloc(namelen + 1);
6600         if (!ref->name)
6601                 return ERR;
6602
6603         strncpy(ref->name, name, namelen);
6604         ref->name[namelen] = 0;
6605         ref->head = head;
6606         ref->tag = tag;
6607         ref->ltag = ltag;
6608         ref->remote = remote;
6609         ref->tracked = tracked;
6610         string_copy_rev(ref->id, id);
6611
6612         return OK;
6613 }
6614
6615 static int
6616 load_refs(void)
6617 {
6618         static const char *ls_remote_argv[SIZEOF_ARG] = {
6619                 "git", "ls-remote", ".", NULL
6620         };
6621         static bool init = FALSE;
6622
6623         if (!init) {
6624                 argv_from_env(ls_remote_argv, "TIG_LS_REMOTE");
6625                 init = TRUE;
6626         }
6627
6628         if (!*opt_git_dir)
6629                 return OK;
6630
6631         while (refs_size > 0)
6632                 free(refs[--refs_size].name);
6633         while (id_refs_size > 0)
6634                 free(id_refs[--id_refs_size]);
6635
6636         return run_io_load(ls_remote_argv, "\t", read_ref);
6637 }
6638
6639 static void
6640 set_repo_config_option(char *name, char *value, int (*cmd)(int, const char **))
6641 {
6642         const char *argv[SIZEOF_ARG] = { name, "=" };
6643         int argc = 1 + (cmd == option_set_command);
6644         int error = ERR;
6645
6646         if (!argv_from_string(argv, &argc, value))
6647                 config_msg = "Too many option arguments";
6648         else
6649                 error = cmd(argc, argv);
6650
6651         if (error == ERR)
6652                 warn("Option 'tig.%s': %s", name, config_msg);
6653 }
6654
6655 static int
6656 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
6657 {
6658         if (!strcmp(name, "i18n.commitencoding"))
6659                 string_ncopy(opt_encoding, value, valuelen);
6660
6661         if (!strcmp(name, "core.editor"))
6662                 string_ncopy(opt_editor, value, valuelen);
6663
6664         if (!prefixcmp(name, "tig.color."))
6665                 set_repo_config_option(name + 10, value, option_color_command);
6666
6667         else if (!prefixcmp(name, "tig.bind."))
6668                 set_repo_config_option(name + 9, value, option_bind_command);
6669
6670         else if (!prefixcmp(name, "tig."))
6671                 set_repo_config_option(name + 4, value, option_set_command);
6672
6673         /* branch.<head>.remote */
6674         if (*opt_head &&
6675             !strncmp(name, "branch.", 7) &&
6676             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
6677             !strcmp(name + 7 + strlen(opt_head), ".remote"))
6678                 string_ncopy(opt_remote, value, valuelen);
6679
6680         if (*opt_head && *opt_remote &&
6681             !strncmp(name, "branch.", 7) &&
6682             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
6683             !strcmp(name + 7 + strlen(opt_head), ".merge")) {
6684                 size_t from = strlen(opt_remote);
6685
6686                 if (!prefixcmp(value, "refs/heads/")) {
6687                         value += STRING_SIZE("refs/heads/");
6688                         valuelen -= STRING_SIZE("refs/heads/");
6689                 }
6690
6691                 if (!string_format_from(opt_remote, &from, "/%s", value))
6692                         opt_remote[0] = 0;
6693         }
6694
6695         return OK;
6696 }
6697
6698 static int
6699 load_git_config(void)
6700 {
6701         const char *config_list_argv[] = { "git", GIT_CONFIG, "--list", NULL };
6702
6703         return run_io_load(config_list_argv, "=", read_repo_config_option);
6704 }
6705
6706 static int
6707 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
6708 {
6709         if (!opt_git_dir[0]) {
6710                 string_ncopy(opt_git_dir, name, namelen);
6711
6712         } else if (opt_is_inside_work_tree == -1) {
6713                 /* This can be 3 different values depending on the
6714                  * version of git being used. If git-rev-parse does not
6715                  * understand --is-inside-work-tree it will simply echo
6716                  * the option else either "true" or "false" is printed.
6717                  * Default to true for the unknown case. */
6718                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
6719
6720         } else if (*name == '.') {
6721                 string_ncopy(opt_cdup, name, namelen);
6722
6723         } else {
6724                 string_ncopy(opt_prefix, name, namelen);
6725         }
6726
6727         return OK;
6728 }
6729
6730 static int
6731 load_repo_info(void)
6732 {
6733         const char *head_argv[] = {
6734                 "git", "symbolic-ref", "HEAD", NULL
6735         };
6736         const char *rev_parse_argv[] = {
6737                 "git", "rev-parse", "--git-dir", "--is-inside-work-tree",
6738                         "--show-cdup", "--show-prefix", NULL
6739         };
6740
6741         if (run_io_buf(head_argv, opt_head, sizeof(opt_head))) {
6742                 chomp_string(opt_head);
6743                 if (!prefixcmp(opt_head, "refs/heads/")) {
6744                         char *offset = opt_head + STRING_SIZE("refs/heads/");
6745
6746                         memmove(opt_head, offset, strlen(offset) + 1);
6747                 }
6748         }
6749
6750         return run_io_load(rev_parse_argv, "=", read_repo_info);
6751 }
6752
6753
6754 /*
6755  * Main
6756  */
6757
6758 static const char usage[] =
6759 "tig " TIG_VERSION " (" __DATE__ ")\n"
6760 "\n"
6761 "Usage: tig        [options] [revs] [--] [paths]\n"
6762 "   or: tig show   [options] [revs] [--] [paths]\n"
6763 "   or: tig blame  [rev] path\n"
6764 "   or: tig status\n"
6765 "   or: tig <      [git command output]\n"
6766 "\n"
6767 "Options:\n"
6768 "  -v, --version   Show version and exit\n"
6769 "  -h, --help      Show help message and exit";
6770
6771 static void __NORETURN
6772 quit(int sig)
6773 {
6774         /* XXX: Restore tty modes and let the OS cleanup the rest! */
6775         if (cursed)
6776                 endwin();
6777         exit(0);
6778 }
6779
6780 static void __NORETURN
6781 die(const char *err, ...)
6782 {
6783         va_list args;
6784
6785         endwin();
6786
6787         va_start(args, err);
6788         fputs("tig: ", stderr);
6789         vfprintf(stderr, err, args);
6790         fputs("\n", stderr);
6791         va_end(args);
6792
6793         exit(1);
6794 }
6795
6796 static void
6797 warn(const char *msg, ...)
6798 {
6799         va_list args;
6800
6801         va_start(args, msg);
6802         fputs("tig warning: ", stderr);
6803         vfprintf(stderr, msg, args);
6804         fputs("\n", stderr);
6805         va_end(args);
6806 }
6807
6808 static enum request
6809 parse_options(int argc, const char *argv[])
6810 {
6811         enum request request = REQ_VIEW_MAIN;
6812         const char *subcommand;
6813         bool seen_dashdash = FALSE;
6814         /* XXX: This is vulnerable to the user overriding options
6815          * required for the main view parser. */
6816         const char *custom_argv[SIZEOF_ARG] = {
6817                 "git", "log", "--no-color", "--pretty=raw", "--parents",
6818                         "--topo-order", NULL
6819         };
6820         int i, j = 6;
6821
6822         if (!isatty(STDIN_FILENO)) {
6823                 io_open(&VIEW(REQ_VIEW_PAGER)->io, "");
6824                 return REQ_VIEW_PAGER;
6825         }
6826
6827         if (argc <= 1)
6828                 return REQ_NONE;
6829
6830         subcommand = argv[1];
6831         if (!strcmp(subcommand, "status")) {
6832                 if (argc > 2)
6833                         warn("ignoring arguments after `%s'", subcommand);
6834                 return REQ_VIEW_STATUS;
6835
6836         } else if (!strcmp(subcommand, "blame")) {
6837                 if (argc <= 2 || argc > 4)
6838                         die("invalid number of options to blame\n\n%s", usage);
6839
6840                 i = 2;
6841                 if (argc == 4) {
6842                         string_ncopy(opt_ref, argv[i], strlen(argv[i]));
6843                         i++;
6844                 }
6845
6846                 string_ncopy(opt_file, argv[i], strlen(argv[i]));
6847                 return REQ_VIEW_BLAME;
6848
6849         } else if (!strcmp(subcommand, "show")) {
6850                 request = REQ_VIEW_DIFF;
6851
6852         } else {
6853                 subcommand = NULL;
6854         }
6855
6856         if (subcommand) {
6857                 custom_argv[1] = subcommand;
6858                 j = 2;
6859         }
6860
6861         for (i = 1 + !!subcommand; i < argc; i++) {
6862                 const char *opt = argv[i];
6863
6864                 if (seen_dashdash || !strcmp(opt, "--")) {
6865                         seen_dashdash = TRUE;
6866
6867                 } else if (!strcmp(opt, "-v") || !strcmp(opt, "--version")) {
6868                         printf("tig version %s\n", TIG_VERSION);
6869                         quit(0);
6870
6871                 } else if (!strcmp(opt, "-h") || !strcmp(opt, "--help")) {
6872                         printf("%s\n", usage);
6873                         quit(0);
6874                 }
6875
6876                 custom_argv[j++] = opt;
6877                 if (j >= ARRAY_SIZE(custom_argv))
6878                         die("command too long");
6879         }
6880
6881         if (!prepare_update(VIEW(request), custom_argv, NULL, FORMAT_NONE))                                                                        
6882                 die("Failed to format arguments"); 
6883
6884         return request;
6885 }
6886
6887 int
6888 main(int argc, const char *argv[])
6889 {
6890         enum request request = parse_options(argc, argv);
6891         struct view *view;
6892         size_t i;
6893
6894         signal(SIGINT, quit);
6895
6896         if (setlocale(LC_ALL, "")) {
6897                 char *codeset = nl_langinfo(CODESET);
6898
6899                 string_ncopy(opt_codeset, codeset, strlen(codeset));
6900         }
6901
6902         if (load_repo_info() == ERR)
6903                 die("Failed to load repo info.");
6904
6905         if (load_options() == ERR)
6906                 die("Failed to load user config.");
6907
6908         if (load_git_config() == ERR)
6909                 die("Failed to load repo config.");
6910
6911         /* Require a git repository unless when running in pager mode. */
6912         if (!opt_git_dir[0] && request != REQ_VIEW_PAGER)
6913                 die("Not a git repository");
6914
6915         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
6916                 opt_utf8 = FALSE;
6917
6918         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
6919                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
6920                 if (opt_iconv == ICONV_NONE)
6921                         die("Failed to initialize character set conversion");
6922         }
6923
6924         if (load_refs() == ERR)
6925                 die("Failed to load refs.");
6926
6927         foreach_view (view, i)
6928                 argv_from_env(view->ops->argv, view->cmd_env);
6929
6930         init_display();
6931
6932         if (request != REQ_NONE)
6933                 open_view(NULL, request, OPEN_PREPARED);
6934         request = request == REQ_NONE ? REQ_VIEW_MAIN : REQ_NONE;
6935
6936         while (view_driver(display[current_view], request)) {
6937                 int key = get_input(0);
6938
6939                 view = display[current_view];
6940                 request = get_keybinding(view->keymap, key);
6941
6942                 /* Some low-level request handling. This keeps access to
6943                  * status_win restricted. */
6944                 switch (request) {
6945                 case REQ_PROMPT:
6946                 {
6947                         char *cmd = read_prompt(":");
6948
6949                         if (cmd && isdigit(*cmd)) {
6950                                 int lineno = view->lineno + 1;
6951
6952                                 if (parse_int(&lineno, cmd, 1, view->lines + 1) == OK) {
6953                                         select_view_line(view, lineno - 1);
6954                                         report("");
6955                                 } else {
6956                                         report("Unable to parse '%s' as a line number", cmd);
6957                                 }
6958
6959                         } else if (cmd) {
6960                                 struct view *next = VIEW(REQ_VIEW_PAGER);
6961                                 const char *argv[SIZEOF_ARG] = { "git" };
6962                                 int argc = 1;
6963
6964                                 /* When running random commands, initially show the
6965                                  * command in the title. However, it maybe later be
6966                                  * overwritten if a commit line is selected. */
6967                                 string_ncopy(next->ref, cmd, strlen(cmd));
6968
6969                                 if (!argv_from_string(argv, &argc, cmd)) {
6970                                         report("Too many arguments");
6971                                 } else if (!prepare_update(next, argv, NULL, FORMAT_DASH)) {
6972                                         report("Failed to format command");
6973                                 } else {
6974                                         open_view(view, REQ_VIEW_PAGER, OPEN_PREPARED);
6975                                 }
6976                         }
6977
6978                         request = REQ_NONE;
6979                         break;
6980                 }
6981                 case REQ_SEARCH:
6982                 case REQ_SEARCH_BACK:
6983                 {
6984                         const char *prompt = request == REQ_SEARCH ? "/" : "?";
6985                         char *search = read_prompt(prompt);
6986
6987                         if (search)
6988                                 string_ncopy(opt_search, search, strlen(search));
6989                         else if (*opt_search)
6990                                 request = request == REQ_SEARCH ?
6991                                         REQ_FIND_NEXT :
6992                                         REQ_FIND_PREV;
6993                         else
6994                                 request = REQ_NONE;
6995                         break;
6996                 }
6997                 default:
6998                         break;
6999                 }
7000         }
7001
7002         quit(0);
7003
7004         return 0;
7005 }