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