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