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