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