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