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