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