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