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