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