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