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