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