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