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