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