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