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