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