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