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