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