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