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