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