Add release script documenting the release procedure
[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_GREEN,    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(MODE,         "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
931 LINE(LINE_NUMBER,  "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
932 LINE(TITLE_BLUR,   "",                  COLOR_WHITE,    COLOR_BLUE,     0), \
933 LINE(TITLE_FOCUS,  "",                  COLOR_WHITE,    COLOR_BLUE,     A_BOLD), \
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_HEAD,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_BOLD), \
943 LINE(TREE_DIR,     "",                  COLOR_YELLOW,   COLOR_DEFAULT,  A_NORMAL), \
944 LINE(TREE_FILE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
945 LINE(STAT_HEAD,    "",                  COLOR_YELLOW,   COLOR_DEFAULT,  0), \
946 LINE(STAT_SECTION, "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
947 LINE(STAT_NONE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
948 LINE(STAT_STAGED,  "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
949 LINE(STAT_UNSTAGED,"",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
950 LINE(STAT_UNTRACKED,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
951 LINE(BLAME_ID,     "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0)
952
953 enum line_type {
954 #define LINE(type, line, fg, bg, attr) \
955         LINE_##type
956         LINE_INFO,
957         LINE_NONE
958 #undef  LINE
959 };
960
961 struct line_info {
962         const char *name;       /* Option name. */
963         int namelen;            /* Size of option name. */
964         const char *line;       /* The start of line to match. */
965         int linelen;            /* Size of string to match. */
966         int fg, bg, attr;       /* Color and text attributes for the lines. */
967 };
968
969 static struct line_info line_info[] = {
970 #define LINE(type, line, fg, bg, attr) \
971         { #type, STRING_SIZE(#type), (line), STRING_SIZE(line), (fg), (bg), (attr) }
972         LINE_INFO
973 #undef  LINE
974 };
975
976 static enum line_type
977 get_line_type(const char *line)
978 {
979         int linelen = strlen(line);
980         enum line_type type;
981
982         for (type = 0; type < ARRAY_SIZE(line_info); type++)
983                 /* Case insensitive search matches Signed-off-by lines better. */
984                 if (linelen >= line_info[type].linelen &&
985                     !strncasecmp(line_info[type].line, line, line_info[type].linelen))
986                         return type;
987
988         return LINE_DEFAULT;
989 }
990
991 static inline int
992 get_line_attr(enum line_type type)
993 {
994         assert(type < ARRAY_SIZE(line_info));
995         return COLOR_PAIR(type) | line_info[type].attr;
996 }
997
998 static struct line_info *
999 get_line_info(const char *name)
1000 {
1001         size_t namelen = strlen(name);
1002         enum line_type type;
1003
1004         for (type = 0; type < ARRAY_SIZE(line_info); type++)
1005                 if (namelen == line_info[type].namelen &&
1006                     !string_enum_compare(line_info[type].name, name, namelen))
1007                         return &line_info[type];
1008
1009         return NULL;
1010 }
1011
1012 static void
1013 init_colors(void)
1014 {
1015         int default_bg = line_info[LINE_DEFAULT].bg;
1016         int default_fg = line_info[LINE_DEFAULT].fg;
1017         enum line_type type;
1018
1019         start_color();
1020
1021         if (assume_default_colors(default_fg, default_bg) == ERR) {
1022                 default_bg = COLOR_BLACK;
1023                 default_fg = COLOR_WHITE;
1024         }
1025
1026         for (type = 0; type < ARRAY_SIZE(line_info); type++) {
1027                 struct line_info *info = &line_info[type];
1028                 int bg = info->bg == COLOR_DEFAULT ? default_bg : info->bg;
1029                 int fg = info->fg == COLOR_DEFAULT ? default_fg : info->fg;
1030
1031                 init_pair(type, fg, bg);
1032         }
1033 }
1034
1035 struct line {
1036         enum line_type type;
1037
1038         /* State flags */
1039         unsigned int selected:1;
1040         unsigned int dirty:1;
1041         unsigned int cleareol:1;
1042
1043         void *data;             /* User data */
1044 };
1045
1046
1047 /*
1048  * Keys
1049  */
1050
1051 struct keybinding {
1052         int alias;
1053         enum request request;
1054 };
1055
1056 static struct keybinding default_keybindings[] = {
1057         /* View switching */
1058         { 'm',          REQ_VIEW_MAIN },
1059         { 'd',          REQ_VIEW_DIFF },
1060         { 'l',          REQ_VIEW_LOG },
1061         { 't',          REQ_VIEW_TREE },
1062         { 'f',          REQ_VIEW_BLOB },
1063         { 'B',          REQ_VIEW_BLAME },
1064         { 'p',          REQ_VIEW_PAGER },
1065         { 'h',          REQ_VIEW_HELP },
1066         { 'S',          REQ_VIEW_STATUS },
1067         { 'c',          REQ_VIEW_STAGE },
1068
1069         /* View manipulation */
1070         { 'q',          REQ_VIEW_CLOSE },
1071         { KEY_TAB,      REQ_VIEW_NEXT },
1072         { KEY_RETURN,   REQ_ENTER },
1073         { KEY_UP,       REQ_PREVIOUS },
1074         { KEY_DOWN,     REQ_NEXT },
1075         { 'R',          REQ_REFRESH },
1076         { KEY_F(5),     REQ_REFRESH },
1077         { 'O',          REQ_MAXIMIZE },
1078
1079         /* Cursor navigation */
1080         { 'k',          REQ_MOVE_UP },
1081         { 'j',          REQ_MOVE_DOWN },
1082         { KEY_HOME,     REQ_MOVE_FIRST_LINE },
1083         { KEY_END,      REQ_MOVE_LAST_LINE },
1084         { KEY_NPAGE,    REQ_MOVE_PAGE_DOWN },
1085         { ' ',          REQ_MOVE_PAGE_DOWN },
1086         { KEY_PPAGE,    REQ_MOVE_PAGE_UP },
1087         { 'b',          REQ_MOVE_PAGE_UP },
1088         { '-',          REQ_MOVE_PAGE_UP },
1089
1090         /* Scrolling */
1091         { KEY_LEFT,     REQ_SCROLL_LEFT },
1092         { KEY_RIGHT,    REQ_SCROLL_RIGHT },
1093         { KEY_IC,       REQ_SCROLL_LINE_UP },
1094         { KEY_DC,       REQ_SCROLL_LINE_DOWN },
1095         { 'w',          REQ_SCROLL_PAGE_UP },
1096         { 's',          REQ_SCROLL_PAGE_DOWN },
1097
1098         /* Searching */
1099         { '/',          REQ_SEARCH },
1100         { '?',          REQ_SEARCH_BACK },
1101         { 'n',          REQ_FIND_NEXT },
1102         { 'N',          REQ_FIND_PREV },
1103
1104         /* Misc */
1105         { 'Q',          REQ_QUIT },
1106         { 'z',          REQ_STOP_LOADING },
1107         { 'v',          REQ_SHOW_VERSION },
1108         { 'r',          REQ_SCREEN_REDRAW },
1109         { '.',          REQ_TOGGLE_LINENO },
1110         { 'D',          REQ_TOGGLE_DATE },
1111         { 'A',          REQ_TOGGLE_AUTHOR },
1112         { 'g',          REQ_TOGGLE_REV_GRAPH },
1113         { 'F',          REQ_TOGGLE_REFS },
1114         { ':',          REQ_PROMPT },
1115         { 'u',          REQ_STATUS_UPDATE },
1116         { '!',          REQ_STATUS_REVERT },
1117         { 'M',          REQ_STATUS_MERGE },
1118         { '@',          REQ_STAGE_NEXT },
1119         { ',',          REQ_PARENT },
1120         { 'e',          REQ_EDIT },
1121 };
1122
1123 #define KEYMAP_INFO \
1124         KEYMAP_(GENERIC), \
1125         KEYMAP_(MAIN), \
1126         KEYMAP_(DIFF), \
1127         KEYMAP_(LOG), \
1128         KEYMAP_(TREE), \
1129         KEYMAP_(BLOB), \
1130         KEYMAP_(BLAME), \
1131         KEYMAP_(PAGER), \
1132         KEYMAP_(HELP), \
1133         KEYMAP_(STATUS), \
1134         KEYMAP_(STAGE)
1135
1136 enum keymap {
1137 #define KEYMAP_(name) KEYMAP_##name
1138         KEYMAP_INFO
1139 #undef  KEYMAP_
1140 };
1141
1142 static struct int_map keymap_table[] = {
1143 #define KEYMAP_(name) { #name, STRING_SIZE(#name), KEYMAP_##name }
1144         KEYMAP_INFO
1145 #undef  KEYMAP_
1146 };
1147
1148 #define set_keymap(map, name) \
1149         set_from_int_map(keymap_table, ARRAY_SIZE(keymap_table), map, name, strlen(name))
1150
1151 struct keybinding_table {
1152         struct keybinding *data;
1153         size_t size;
1154 };
1155
1156 static struct keybinding_table keybindings[ARRAY_SIZE(keymap_table)];
1157
1158 static void
1159 add_keybinding(enum keymap keymap, enum request request, int key)
1160 {
1161         struct keybinding_table *table = &keybindings[keymap];
1162
1163         table->data = realloc(table->data, (table->size + 1) * sizeof(*table->data));
1164         if (!table->data)
1165                 die("Failed to allocate keybinding");
1166         table->data[table->size].alias = key;
1167         table->data[table->size++].request = request;
1168 }
1169
1170 /* Looks for a key binding first in the given map, then in the generic map, and
1171  * lastly in the default keybindings. */
1172 static enum request
1173 get_keybinding(enum keymap keymap, int key)
1174 {
1175         size_t i;
1176
1177         for (i = 0; i < keybindings[keymap].size; i++)
1178                 if (keybindings[keymap].data[i].alias == key)
1179                         return keybindings[keymap].data[i].request;
1180
1181         for (i = 0; i < keybindings[KEYMAP_GENERIC].size; i++)
1182                 if (keybindings[KEYMAP_GENERIC].data[i].alias == key)
1183                         return keybindings[KEYMAP_GENERIC].data[i].request;
1184
1185         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++)
1186                 if (default_keybindings[i].alias == key)
1187                         return default_keybindings[i].request;
1188
1189         return (enum request) key;
1190 }
1191
1192
1193 struct key {
1194         const char *name;
1195         int value;
1196 };
1197
1198 static struct key key_table[] = {
1199         { "Enter",      KEY_RETURN },
1200         { "Space",      ' ' },
1201         { "Backspace",  KEY_BACKSPACE },
1202         { "Tab",        KEY_TAB },
1203         { "Escape",     KEY_ESC },
1204         { "Left",       KEY_LEFT },
1205         { "Right",      KEY_RIGHT },
1206         { "Up",         KEY_UP },
1207         { "Down",       KEY_DOWN },
1208         { "Insert",     KEY_IC },
1209         { "Delete",     KEY_DC },
1210         { "Hash",       '#' },
1211         { "Home",       KEY_HOME },
1212         { "End",        KEY_END },
1213         { "PageUp",     KEY_PPAGE },
1214         { "PageDown",   KEY_NPAGE },
1215         { "F1",         KEY_F(1) },
1216         { "F2",         KEY_F(2) },
1217         { "F3",         KEY_F(3) },
1218         { "F4",         KEY_F(4) },
1219         { "F5",         KEY_F(5) },
1220         { "F6",         KEY_F(6) },
1221         { "F7",         KEY_F(7) },
1222         { "F8",         KEY_F(8) },
1223         { "F9",         KEY_F(9) },
1224         { "F10",        KEY_F(10) },
1225         { "F11",        KEY_F(11) },
1226         { "F12",        KEY_F(12) },
1227 };
1228
1229 static int
1230 get_key_value(const char *name)
1231 {
1232         int i;
1233
1234         for (i = 0; i < ARRAY_SIZE(key_table); i++)
1235                 if (!strcasecmp(key_table[i].name, name))
1236                         return key_table[i].value;
1237
1238         if (strlen(name) == 1 && isprint(*name))
1239                 return (int) *name;
1240
1241         return ERR;
1242 }
1243
1244 static const char *
1245 get_key_name(int key_value)
1246 {
1247         static char key_char[] = "'X'";
1248         const char *seq = NULL;
1249         int key;
1250
1251         for (key = 0; key < ARRAY_SIZE(key_table); key++)
1252                 if (key_table[key].value == key_value)
1253                         seq = key_table[key].name;
1254
1255         if (seq == NULL &&
1256             key_value < 127 &&
1257             isprint(key_value)) {
1258                 key_char[1] = (char) key_value;
1259                 seq = key_char;
1260         }
1261
1262         return seq ? seq : "(no key)";
1263 }
1264
1265 static const char *
1266 get_key(enum request request)
1267 {
1268         static char buf[BUFSIZ];
1269         size_t pos = 0;
1270         char *sep = "";
1271         int i;
1272
1273         buf[pos] = 0;
1274
1275         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
1276                 struct keybinding *keybinding = &default_keybindings[i];
1277
1278                 if (keybinding->request != request)
1279                         continue;
1280
1281                 if (!string_format_from(buf, &pos, "%s%s", sep,
1282                                         get_key_name(keybinding->alias)))
1283                         return "Too many keybindings!";
1284                 sep = ", ";
1285         }
1286
1287         return buf;
1288 }
1289
1290 struct run_request {
1291         enum keymap keymap;
1292         int key;
1293         const char *argv[SIZEOF_ARG];
1294 };
1295
1296 static struct run_request *run_request;
1297 static size_t run_requests;
1298
1299 static enum request
1300 add_run_request(enum keymap keymap, int key, int argc, const char **argv)
1301 {
1302         struct run_request *req;
1303
1304         if (argc >= ARRAY_SIZE(req->argv) - 1)
1305                 return REQ_NONE;
1306
1307         req = realloc(run_request, (run_requests + 1) * sizeof(*run_request));
1308         if (!req)
1309                 return REQ_NONE;
1310
1311         run_request = req;
1312         req = &run_request[run_requests];
1313         req->keymap = keymap;
1314         req->key = key;
1315         req->argv[0] = NULL;
1316
1317         if (!format_argv(req->argv, argv, FORMAT_NONE))
1318                 return REQ_NONE;
1319
1320         return REQ_NONE + ++run_requests;
1321 }
1322
1323 static struct run_request *
1324 get_run_request(enum request request)
1325 {
1326         if (request <= REQ_NONE)
1327                 return NULL;
1328         return &run_request[request - REQ_NONE - 1];
1329 }
1330
1331 static void
1332 add_builtin_run_requests(void)
1333 {
1334         const char *cherry_pick[] = { "git", "cherry-pick", "%(commit)", NULL };
1335         const char *gc[] = { "git", "gc", NULL };
1336         struct {
1337                 enum keymap keymap;
1338                 int key;
1339                 int argc;
1340                 const char **argv;
1341         } reqs[] = {
1342                 { KEYMAP_MAIN,    'C', ARRAY_SIZE(cherry_pick) - 1, cherry_pick },
1343                 { KEYMAP_GENERIC, 'G', ARRAY_SIZE(gc) - 1, gc },
1344         };
1345         int i;
1346
1347         for (i = 0; i < ARRAY_SIZE(reqs); i++) {
1348                 enum request req;
1349
1350                 req = add_run_request(reqs[i].keymap, reqs[i].key, reqs[i].argc, reqs[i].argv);
1351                 if (req != REQ_NONE)
1352                         add_keybinding(reqs[i].keymap, req, reqs[i].key);
1353         }
1354 }
1355
1356 /*
1357  * User config file handling.
1358  */
1359
1360 static struct int_map color_map[] = {
1361 #define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name }
1362         COLOR_MAP(DEFAULT),
1363         COLOR_MAP(BLACK),
1364         COLOR_MAP(BLUE),
1365         COLOR_MAP(CYAN),
1366         COLOR_MAP(GREEN),
1367         COLOR_MAP(MAGENTA),
1368         COLOR_MAP(RED),
1369         COLOR_MAP(WHITE),
1370         COLOR_MAP(YELLOW),
1371 };
1372
1373 #define set_color(color, name) \
1374         set_from_int_map(color_map, ARRAY_SIZE(color_map), color, name, strlen(name))
1375
1376 static struct int_map attr_map[] = {
1377 #define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name }
1378         ATTR_MAP(NORMAL),
1379         ATTR_MAP(BLINK),
1380         ATTR_MAP(BOLD),
1381         ATTR_MAP(DIM),
1382         ATTR_MAP(REVERSE),
1383         ATTR_MAP(STANDOUT),
1384         ATTR_MAP(UNDERLINE),
1385 };
1386
1387 #define set_attribute(attr, name) \
1388         set_from_int_map(attr_map, ARRAY_SIZE(attr_map), attr, name, strlen(name))
1389
1390 static int   config_lineno;
1391 static bool  config_errors;
1392 static const char *config_msg;
1393
1394 /* Wants: object fgcolor bgcolor [attribute] */
1395 static int
1396 option_color_command(int argc, const char *argv[])
1397 {
1398         struct line_info *info;
1399
1400         if (argc != 3 && argc != 4) {
1401                 config_msg = "Wrong number of arguments given to color command";
1402                 return ERR;
1403         }
1404
1405         info = get_line_info(argv[0]);
1406         if (!info) {
1407                 if (!string_enum_compare(argv[0], "main-delim", strlen("main-delim"))) {
1408                         info = get_line_info("delimiter");
1409
1410                 } else if (!string_enum_compare(argv[0], "main-date", strlen("main-date"))) {
1411                         info = get_line_info("date");
1412
1413                 } else if (!string_enum_compare(argv[0], "main-author", strlen("main-author"))) {
1414                         info = get_line_info("author");
1415
1416                 } else {
1417                         config_msg = "Unknown color name";
1418                         return ERR;
1419                 }
1420         }
1421
1422         if (set_color(&info->fg, argv[1]) == ERR ||
1423             set_color(&info->bg, argv[2]) == ERR) {
1424                 config_msg = "Unknown color";
1425                 return ERR;
1426         }
1427
1428         if (argc == 4 && set_attribute(&info->attr, argv[3]) == ERR) {
1429                 config_msg = "Unknown attribute";
1430                 return ERR;
1431         }
1432
1433         return OK;
1434 }
1435
1436 static bool parse_bool(const char *s)
1437 {
1438         return (!strcmp(s, "1") || !strcmp(s, "true") ||
1439                 !strcmp(s, "yes")) ? TRUE : FALSE;
1440 }
1441
1442 static int
1443 parse_int(const char *s, int default_value, int min, int max)
1444 {
1445         int value = atoi(s);
1446
1447         return (value < min || value > max) ? default_value : value;
1448 }
1449
1450 /* Wants: name = value */
1451 static int
1452 option_set_command(int argc, const char *argv[])
1453 {
1454         if (argc != 3) {
1455                 config_msg = "Wrong number of arguments given to set command";
1456                 return ERR;
1457         }
1458
1459         if (strcmp(argv[1], "=")) {
1460                 config_msg = "No value assigned";
1461                 return ERR;
1462         }
1463
1464         if (!strcmp(argv[0], "show-author")) {
1465                 opt_author = parse_bool(argv[2]);
1466                 return OK;
1467         }
1468
1469         if (!strcmp(argv[0], "show-date")) {
1470                 opt_date = parse_bool(argv[2]);
1471                 return OK;
1472         }
1473
1474         if (!strcmp(argv[0], "show-rev-graph")) {
1475                 opt_rev_graph = parse_bool(argv[2]);
1476                 return OK;
1477         }
1478
1479         if (!strcmp(argv[0], "show-refs")) {
1480                 opt_show_refs = parse_bool(argv[2]);
1481                 return OK;
1482         }
1483
1484         if (!strcmp(argv[0], "show-line-numbers")) {
1485                 opt_line_number = parse_bool(argv[2]);
1486                 return OK;
1487         }
1488
1489         if (!strcmp(argv[0], "line-graphics")) {
1490                 opt_line_graphics = parse_bool(argv[2]);
1491                 return OK;
1492         }
1493
1494         if (!strcmp(argv[0], "line-number-interval")) {
1495                 opt_num_interval = parse_int(argv[2], opt_num_interval, 1, 1024);
1496                 return OK;
1497         }
1498
1499         if (!strcmp(argv[0], "author-width")) {
1500                 opt_author_cols = parse_int(argv[2], opt_author_cols, 0, 1024);
1501                 return OK;
1502         }
1503
1504         if (!strcmp(argv[0], "tab-size")) {
1505                 opt_tab_size = parse_int(argv[2], opt_tab_size, 1, 1024);
1506                 return OK;
1507         }
1508
1509         if (!strcmp(argv[0], "commit-encoding")) {
1510                 const char *arg = argv[2];
1511                 int arglen = strlen(arg);
1512
1513                 switch (arg[0]) {
1514                 case '\"':
1515                 case '\'':
1516                         if (arglen == 1 || arg[arglen - 1] != arg[0]) {
1517                                 config_msg = "Unmatched quotation";
1518                                 return ERR;
1519                         }
1520                         arg += 1; arglen -= 2;
1521                 default:
1522                         string_ncopy(opt_encoding, arg, strlen(arg));
1523                         return OK;
1524                 }
1525         }
1526
1527         config_msg = "Unknown variable name";
1528         return ERR;
1529 }
1530
1531 /* Wants: mode request key */
1532 static int
1533 option_bind_command(int argc, const char *argv[])
1534 {
1535         enum request request;
1536         int keymap;
1537         int key;
1538
1539         if (argc < 3) {
1540                 config_msg = "Wrong number of arguments given to bind command";
1541                 return ERR;
1542         }
1543
1544         if (set_keymap(&keymap, argv[0]) == ERR) {
1545                 config_msg = "Unknown key map";
1546                 return ERR;
1547         }
1548
1549         key = get_key_value(argv[1]);
1550         if (key == ERR) {
1551                 config_msg = "Unknown key";
1552                 return ERR;
1553         }
1554
1555         request = get_request(argv[2]);
1556         if (request == REQ_NONE) {
1557                 struct {
1558                         const char *name;
1559                         enum request request;
1560                 } obsolete[] = {
1561                         { "cherry-pick",        REQ_NONE },
1562                         { "screen-resize",      REQ_NONE },
1563                         { "tree-parent",        REQ_PARENT },
1564                 };
1565                 size_t namelen = strlen(argv[2]);
1566                 int i;
1567
1568                 for (i = 0; i < ARRAY_SIZE(obsolete); i++) {
1569                         if (namelen != strlen(obsolete[i].name) ||
1570                             string_enum_compare(obsolete[i].name, argv[2], namelen))
1571                                 continue;
1572                         if (obsolete[i].request != REQ_NONE)
1573                                 add_keybinding(keymap, obsolete[i].request, key);
1574                         config_msg = "Obsolete request name";
1575                         return ERR;
1576                 }
1577         }
1578         if (request == REQ_NONE && *argv[2]++ == '!')
1579                 request = add_run_request(keymap, key, argc - 2, argv + 2);
1580         if (request == REQ_NONE) {
1581                 config_msg = "Unknown request name";
1582                 return ERR;
1583         }
1584
1585         add_keybinding(keymap, request, key);
1586
1587         return OK;
1588 }
1589
1590 static int
1591 set_option(const char *opt, char *value)
1592 {
1593         const char *argv[SIZEOF_ARG];
1594         int argc = 0;
1595
1596         if (!argv_from_string(argv, &argc, value)) {
1597                 config_msg = "Too many option arguments";
1598                 return ERR;
1599         }
1600
1601         if (!strcmp(opt, "color"))
1602                 return option_color_command(argc, argv);
1603
1604         if (!strcmp(opt, "set"))
1605                 return option_set_command(argc, argv);
1606
1607         if (!strcmp(opt, "bind"))
1608                 return option_bind_command(argc, argv);
1609
1610         config_msg = "Unknown option command";
1611         return ERR;
1612 }
1613
1614 static int
1615 read_option(char *opt, size_t optlen, char *value, size_t valuelen)
1616 {
1617         int status = OK;
1618
1619         config_lineno++;
1620         config_msg = "Internal error";
1621
1622         /* Check for comment markers, since read_properties() will
1623          * only ensure opt and value are split at first " \t". */
1624         optlen = strcspn(opt, "#");
1625         if (optlen == 0)
1626                 return OK;
1627
1628         if (opt[optlen] != 0) {
1629                 config_msg = "No option value";
1630                 status = ERR;
1631
1632         }  else {
1633                 /* Look for comment endings in the value. */
1634                 size_t len = strcspn(value, "#");
1635
1636                 if (len < valuelen) {
1637                         valuelen = len;
1638                         value[valuelen] = 0;
1639                 }
1640
1641                 status = set_option(opt, value);
1642         }
1643
1644         if (status == ERR) {
1645                 warn("Error on line %d, near '%.*s': %s",
1646                      config_lineno, (int) optlen, opt, config_msg);
1647                 config_errors = TRUE;
1648         }
1649
1650         /* Always keep going if errors are encountered. */
1651         return OK;
1652 }
1653
1654 static void
1655 load_option_file(const char *path)
1656 {
1657         struct io io = {};
1658
1659         /* It's OK that the file doesn't exist. */
1660         if (!io_open(&io, path))
1661                 return;
1662
1663         config_lineno = 0;
1664         config_errors = FALSE;
1665
1666         if (io_load(&io, " \t", read_option) == ERR ||
1667             config_errors == TRUE)
1668                 warn("Errors while loading %s.", path);
1669 }
1670
1671 static int
1672 load_options(void)
1673 {
1674         const char *home = getenv("HOME");
1675         const char *tigrc_user = getenv("TIGRC_USER");
1676         const char *tigrc_system = getenv("TIGRC_SYSTEM");
1677         char buf[SIZEOF_STR];
1678
1679         add_builtin_run_requests();
1680
1681         if (!tigrc_system) {
1682                 if (!string_format(buf, "%s/tigrc", SYSCONFDIR))
1683                         return ERR;
1684                 tigrc_system = buf;
1685         }
1686         load_option_file(tigrc_system);
1687
1688         if (!tigrc_user) {
1689                 if (!home || !string_format(buf, "%s/.tigrc", home))
1690                         return ERR;
1691                 tigrc_user = buf;
1692         }
1693         load_option_file(tigrc_user);
1694
1695         return OK;
1696 }
1697
1698
1699 /*
1700  * The viewer
1701  */
1702
1703 struct view;
1704 struct view_ops;
1705
1706 /* The display array of active views and the index of the current view. */
1707 static struct view *display[2];
1708 static unsigned int current_view;
1709
1710 #define foreach_displayed_view(view, i) \
1711         for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
1712
1713 #define displayed_views()       (display[1] != NULL ? 2 : 1)
1714
1715 /* Current head and commit ID */
1716 static char ref_blob[SIZEOF_REF]        = "";
1717 static char ref_commit[SIZEOF_REF]      = "HEAD";
1718 static char ref_head[SIZEOF_REF]        = "HEAD";
1719
1720 struct view {
1721         const char *name;       /* View name */
1722         const char *cmd_env;    /* Command line set via environment */
1723         const char *id;         /* Points to either of ref_{head,commit,blob} */
1724
1725         struct view_ops *ops;   /* View operations */
1726
1727         enum keymap keymap;     /* What keymap does this view have */
1728         bool git_dir;           /* Whether the view requires a git directory. */
1729
1730         char ref[SIZEOF_REF];   /* Hovered commit reference */
1731         char vid[SIZEOF_REF];   /* View ID. Set to id member when updating. */
1732
1733         int height, width;      /* The width and height of the main window */
1734         WINDOW *win;            /* The main window */
1735         WINDOW *title;          /* The title window living below the main window */
1736
1737         /* Navigation */
1738         unsigned long offset;   /* Offset of the window top */
1739         unsigned long yoffset;  /* Offset from the window side. */
1740         unsigned long lineno;   /* Current line number */
1741         unsigned long p_offset; /* Previous offset of the window top */
1742         unsigned long p_yoffset;/* Previous offset from the window side */
1743         unsigned long p_lineno; /* Previous current line number */
1744         bool p_restore;         /* Should the previous position be restored. */
1745
1746         /* Searching */
1747         char grep[SIZEOF_STR];  /* Search string */
1748         regex_t *regex;         /* Pre-compiled regexp */
1749
1750         /* If non-NULL, points to the view that opened this view. If this view
1751          * is closed tig will switch back to the parent view. */
1752         struct view *parent;
1753
1754         /* Buffering */
1755         size_t lines;           /* Total number of lines */
1756         struct line *line;      /* Line index */
1757         size_t line_alloc;      /* Total number of allocated lines */
1758         unsigned int digits;    /* Number of digits in the lines member. */
1759
1760         /* Drawing */
1761         struct line *curline;   /* Line currently being drawn. */
1762         enum line_type curtype; /* Attribute currently used for drawing. */
1763         unsigned long col;      /* Column when drawing. */
1764         bool has_scrolled;      /* View was scrolled. */
1765         bool can_hscroll;       /* View can be scrolled horizontally. */
1766
1767         /* Loading */
1768         struct io io;
1769         struct io *pipe;
1770         time_t start_time;
1771         time_t update_secs;
1772 };
1773
1774 struct view_ops {
1775         /* What type of content being displayed. Used in the title bar. */
1776         const char *type;
1777         /* Default command arguments. */
1778         const char **argv;
1779         /* Open and reads in all view content. */
1780         bool (*open)(struct view *view);
1781         /* Read one line; updates view->line. */
1782         bool (*read)(struct view *view, char *data);
1783         /* Draw one line; @lineno must be < view->height. */
1784         bool (*draw)(struct view *view, struct line *line, unsigned int lineno);
1785         /* Depending on view handle a special requests. */
1786         enum request (*request)(struct view *view, enum request request, struct line *line);
1787         /* Search for regexp in a line. */
1788         bool (*grep)(struct view *view, struct line *line);
1789         /* Select line */
1790         void (*select)(struct view *view, struct line *line);
1791 };
1792
1793 static struct view_ops blame_ops;
1794 static struct view_ops blob_ops;
1795 static struct view_ops diff_ops;
1796 static struct view_ops help_ops;
1797 static struct view_ops log_ops;
1798 static struct view_ops main_ops;
1799 static struct view_ops pager_ops;
1800 static struct view_ops stage_ops;
1801 static struct view_ops status_ops;
1802 static struct view_ops tree_ops;
1803
1804 #define VIEW_STR(name, env, ref, ops, map, git) \
1805         { name, #env, ref, ops, map, git }
1806
1807 #define VIEW_(id, name, ops, git, ref) \
1808         VIEW_STR(name, TIG_##id##_CMD, ref, ops, KEYMAP_##id, git)
1809
1810
1811 static struct view views[] = {
1812         VIEW_(MAIN,   "main",   &main_ops,   TRUE,  ref_head),
1813         VIEW_(DIFF,   "diff",   &diff_ops,   TRUE,  ref_commit),
1814         VIEW_(LOG,    "log",    &log_ops,    TRUE,  ref_head),
1815         VIEW_(TREE,   "tree",   &tree_ops,   TRUE,  ref_commit),
1816         VIEW_(BLOB,   "blob",   &blob_ops,   TRUE,  ref_blob),
1817         VIEW_(BLAME,  "blame",  &blame_ops,  TRUE,  ref_commit),
1818         VIEW_(HELP,   "help",   &help_ops,   FALSE, ""),
1819         VIEW_(PAGER,  "pager",  &pager_ops,  FALSE, "stdin"),
1820         VIEW_(STATUS, "status", &status_ops, TRUE,  ""),
1821         VIEW_(STAGE,  "stage",  &stage_ops,  TRUE,  ""),
1822 };
1823
1824 #define VIEW(req)       (&views[(req) - REQ_OFFSET - 1])
1825 #define VIEW_REQ(view)  ((view) - views + REQ_OFFSET + 1)
1826
1827 #define foreach_view(view, i) \
1828         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
1829
1830 #define view_is_displayed(view) \
1831         (view == display[0] || view == display[1])
1832
1833
1834 enum line_graphic {
1835         LINE_GRAPHIC_VLINE
1836 };
1837
1838 static int line_graphics[] = {
1839         /* LINE_GRAPHIC_VLINE: */ '|'
1840 };
1841
1842 static inline void
1843 set_view_attr(struct view *view, enum line_type type)
1844 {
1845         if (!view->curline->selected && view->curtype != type) {
1846                 wattrset(view->win, get_line_attr(type));
1847                 wchgat(view->win, -1, 0, type, NULL);
1848                 view->curtype = type;
1849         }
1850 }
1851
1852 static int
1853 draw_chars(struct view *view, enum line_type type, const char *string,
1854            int max_len, bool use_tilde)
1855 {
1856         int len = 0;
1857         int col = 0;
1858         int trimmed = FALSE;
1859         size_t skip = view->yoffset > view->col ? view->yoffset - view->col : 0;
1860
1861         if (max_len <= 0)
1862                 return 0;
1863
1864         if (opt_utf8) {
1865                 len = utf8_length(&string, skip, &col, max_len, &trimmed, use_tilde);
1866         } else {
1867                 col = len = strlen(string);
1868                 if (len > max_len) {
1869                         if (use_tilde) {
1870                                 max_len -= 1;
1871                         }
1872                         col = len = max_len;
1873                         trimmed = TRUE;
1874                 }
1875         }
1876
1877         set_view_attr(view, type);
1878         if (len > 0)
1879                 waddnstr(view->win, string, len);
1880         if (trimmed && use_tilde) {
1881                 set_view_attr(view, LINE_DELIMITER);
1882                 waddch(view->win, '~');
1883                 col++;
1884         }
1885
1886         if (view->col + col >= view->width + view->yoffset)
1887                 view->can_hscroll = TRUE;
1888
1889         return col;
1890 }
1891
1892 static int
1893 draw_space(struct view *view, enum line_type type, int max, int spaces)
1894 {
1895         static char space[] = "                    ";
1896         int col = 0;
1897
1898         spaces = MIN(max, spaces);
1899
1900         while (spaces > 0) {
1901                 int len = MIN(spaces, sizeof(space) - 1);
1902
1903                 col += draw_chars(view, type, space, spaces, FALSE);
1904                 spaces -= len;
1905         }
1906
1907         return col;
1908 }
1909
1910 static bool
1911 draw_lineno(struct view *view, unsigned int lineno)
1912 {
1913         size_t skip = view->yoffset > view->col ? view->yoffset - view->col : 0;
1914         char number[10];
1915         int digits3 = view->digits < 3 ? 3 : view->digits;
1916         int max_number = MIN(digits3, STRING_SIZE(number));
1917         int max = view->width - view->col;
1918         int col;
1919
1920         if (max < max_number)
1921                 max_number = max;
1922
1923         lineno += view->offset + 1;
1924         if (lineno == 1 || (lineno % opt_num_interval) == 0) {
1925                 static char fmt[] = "%1ld";
1926
1927                 if (view->digits <= 9)
1928                         fmt[1] = '0' + digits3;
1929
1930                 if (!string_format(number, fmt, lineno))
1931                         number[0] = 0;
1932                 col = draw_chars(view, LINE_LINE_NUMBER, number, max_number, TRUE);
1933         } else {
1934                 col = draw_space(view, LINE_LINE_NUMBER, max_number, max_number);
1935         }
1936
1937         if (col < max && skip <= col) {
1938                 set_view_attr(view, LINE_DEFAULT);
1939                 waddch(view->win, line_graphics[LINE_GRAPHIC_VLINE]);
1940         }
1941         col++;
1942
1943         view->col += col;
1944         if (col < max && skip <= col)
1945                 col = draw_space(view, LINE_DEFAULT, max - col, 1);
1946         view->col++;
1947
1948         return view->width + view->yoffset <= view->col;
1949 }
1950
1951 static bool
1952 draw_text(struct view *view, enum line_type type, const char *string, bool trim)
1953 {
1954         view->col += draw_chars(view, type, string, view->width + view->yoffset - view->col, trim);
1955         return view->width - view->col <= 0;
1956 }
1957
1958 static bool
1959 draw_graphic(struct view *view, enum line_type type, chtype graphic[], size_t size)
1960 {
1961         size_t skip = view->yoffset > view->col ? view->yoffset - view->col : 0;
1962         int max = view->width - view->col;
1963         int i;
1964
1965         if (max < size)
1966                 size = max;
1967
1968         set_view_attr(view, type);
1969         /* Using waddch() instead of waddnstr() ensures that
1970          * they'll be rendered correctly for the cursor line. */
1971         for (i = skip; i < size; i++)
1972                 waddch(view->win, graphic[i]);
1973
1974         view->col += size;
1975         if (size < max && skip <= size)
1976                 waddch(view->win, ' ');
1977         view->col++;
1978
1979         return view->width - view->col <= 0;
1980 }
1981
1982 static bool
1983 draw_field(struct view *view, enum line_type type, const char *text, int len, bool trim)
1984 {
1985         int max = MIN(view->width - view->col, len);
1986         int col;
1987
1988         if (text)
1989                 col = draw_chars(view, type, text, max - 1, trim);
1990         else
1991                 col = draw_space(view, type, max - 1, max - 1);
1992
1993         view->col += col;
1994         view->col += draw_space(view, LINE_DEFAULT, max - col, max - col);
1995         return view->width + view->yoffset <= view->col;
1996 }
1997
1998 static bool
1999 draw_date(struct view *view, struct tm *time)
2000 {
2001         char buf[DATE_COLS];
2002         char *date;
2003         int timelen = 0;
2004
2005         if (time)
2006                 timelen = strftime(buf, sizeof(buf), DATE_FORMAT, time);
2007         date = timelen ? buf : NULL;
2008
2009         return draw_field(view, LINE_DATE, date, DATE_COLS, FALSE);
2010 }
2011
2012 static bool
2013 draw_author(struct view *view, const char *author)
2014 {
2015         bool trim = opt_author_cols == 0 || opt_author_cols > 5 || !author;
2016
2017         if (!trim) {
2018                 static char initials[10];
2019                 size_t pos;
2020
2021 #define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@')
2022
2023                 memset(initials, 0, sizeof(initials));
2024                 for (pos = 0; *author && pos < opt_author_cols - 1; author++, pos++) {
2025                         while (is_initial_sep(*author))
2026                                 author++;
2027                         strncpy(&initials[pos], author, sizeof(initials) - 1 - pos);
2028                         while (*author && !is_initial_sep(author[1]))
2029                                 author++;
2030                 }
2031
2032                 author = initials;
2033         }
2034
2035         return draw_field(view, LINE_AUTHOR, author, opt_author_cols, trim);
2036 }
2037
2038 static bool
2039 draw_mode(struct view *view, mode_t mode)
2040 {
2041         static const char dir_mode[]    = "drwxr-xr-x";
2042         static const char link_mode[]   = "lrwxrwxrwx";
2043         static const char exe_mode[]    = "-rwxr-xr-x";
2044         static const char file_mode[]   = "-rw-r--r--";
2045         const char *str;
2046
2047         if (S_ISDIR(mode))
2048                 str = dir_mode;
2049         else if (S_ISLNK(mode))
2050                 str = link_mode;
2051         else if (mode & S_IXUSR)
2052                 str = exe_mode;
2053         else
2054                 str = file_mode;
2055
2056         return draw_field(view, LINE_MODE, str, sizeof(file_mode), FALSE);
2057 }
2058
2059 static bool
2060 draw_view_line(struct view *view, unsigned int lineno)
2061 {
2062         struct line *line;
2063         bool selected = (view->offset + lineno == view->lineno);
2064
2065         assert(view_is_displayed(view));
2066
2067         if (view->offset + lineno >= view->lines)
2068                 return FALSE;
2069
2070         line = &view->line[view->offset + lineno];
2071
2072         wmove(view->win, lineno, 0);
2073         if (line->cleareol)
2074                 wclrtoeol(view->win);
2075         view->col = 0;
2076         view->curline = line;
2077         view->curtype = LINE_NONE;
2078         line->selected = FALSE;
2079         line->dirty = line->cleareol = 0;
2080
2081         if (selected) {
2082                 set_view_attr(view, LINE_CURSOR);
2083                 line->selected = TRUE;
2084                 view->ops->select(view, line);
2085         }
2086
2087         return view->ops->draw(view, line, lineno);
2088 }
2089
2090 static void
2091 redraw_view_dirty(struct view *view)
2092 {
2093         bool dirty = FALSE;
2094         int lineno;
2095
2096         for (lineno = 0; lineno < view->height; lineno++) {
2097                 if (view->offset + lineno >= view->lines)
2098                         break;
2099                 if (!view->line[view->offset + lineno].dirty)
2100                         continue;
2101                 dirty = TRUE;
2102                 if (!draw_view_line(view, lineno))
2103                         break;
2104         }
2105
2106         if (!dirty)
2107                 return;
2108         wnoutrefresh(view->win);
2109 }
2110
2111 static void
2112 redraw_view_from(struct view *view, int lineno)
2113 {
2114         assert(0 <= lineno && lineno < view->height);
2115
2116         if (lineno == 0)
2117                 view->can_hscroll = FALSE;
2118
2119         for (; lineno < view->height; lineno++) {
2120                 if (!draw_view_line(view, lineno))
2121                         break;
2122         }
2123
2124         wnoutrefresh(view->win);
2125 }
2126
2127 static void
2128 redraw_view(struct view *view)
2129 {
2130         werase(view->win);
2131         redraw_view_from(view, 0);
2132 }
2133
2134
2135 static void
2136 update_view_title(struct view *view)
2137 {
2138         char buf[SIZEOF_STR];
2139         char state[SIZEOF_STR];
2140         size_t bufpos = 0, statelen = 0;
2141
2142         assert(view_is_displayed(view));
2143
2144         if (view != VIEW(REQ_VIEW_STATUS) && view->lines) {
2145                 unsigned int view_lines = view->offset + view->height;
2146                 unsigned int lines = view->lines
2147                                    ? MIN(view_lines, view->lines) * 100 / view->lines
2148                                    : 0;
2149
2150                 string_format_from(state, &statelen, " - %s %d of %d (%d%%)",
2151                                    view->ops->type,
2152                                    view->lineno + 1,
2153                                    view->lines,
2154                                    lines);
2155
2156         }
2157
2158         if (view->pipe) {
2159                 time_t secs = time(NULL) - view->start_time;
2160
2161                 /* Three git seconds are a long time ... */
2162                 if (secs > 2)
2163                         string_format_from(state, &statelen, " loading %lds", secs);
2164         }
2165
2166         string_format_from(buf, &bufpos, "[%s]", view->name);
2167         if (*view->ref && bufpos < view->width) {
2168                 size_t refsize = strlen(view->ref);
2169                 size_t minsize = bufpos + 1 + /* abbrev= */ 7 + 1 + statelen;
2170
2171                 if (minsize < view->width)
2172                         refsize = view->width - minsize + 7;
2173                 string_format_from(buf, &bufpos, " %.*s", (int) refsize, view->ref);
2174         }
2175
2176         if (statelen && bufpos < view->width) {
2177                 string_format_from(buf, &bufpos, "%s", state);
2178         }
2179
2180         if (view == display[current_view])
2181                 wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
2182         else
2183                 wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
2184
2185         mvwaddnstr(view->title, 0, 0, buf, bufpos);
2186         wclrtoeol(view->title);
2187         wnoutrefresh(view->title);
2188 }
2189
2190 static void
2191 resize_display(void)
2192 {
2193         int offset, i;
2194         struct view *base = display[0];
2195         struct view *view = display[1] ? display[1] : display[0];
2196
2197         /* Setup window dimensions */
2198
2199         getmaxyx(stdscr, base->height, base->width);
2200
2201         /* Make room for the status window. */
2202         base->height -= 1;
2203
2204         if (view != base) {
2205                 /* Horizontal split. */
2206                 view->width   = base->width;
2207                 view->height  = SCALE_SPLIT_VIEW(base->height);
2208                 base->height -= view->height;
2209
2210                 /* Make room for the title bar. */
2211                 view->height -= 1;
2212         }
2213
2214         /* Make room for the title bar. */
2215         base->height -= 1;
2216
2217         offset = 0;
2218
2219         foreach_displayed_view (view, i) {
2220                 if (!view->win) {
2221                         view->win = newwin(view->height, 0, offset, 0);
2222                         if (!view->win)
2223                                 die("Failed to create %s view", view->name);
2224
2225                         scrollok(view->win, FALSE);
2226
2227                         view->title = newwin(1, 0, offset + view->height, 0);
2228                         if (!view->title)
2229                                 die("Failed to create title window");
2230
2231                 } else {
2232                         wresize(view->win, view->height, view->width);
2233                         mvwin(view->win,   offset, 0);
2234                         mvwin(view->title, offset + view->height, 0);
2235                 }
2236
2237                 offset += view->height + 1;
2238         }
2239 }
2240
2241 static void
2242 redraw_display(bool clear)
2243 {
2244         struct view *view;
2245         int i;
2246
2247         foreach_displayed_view (view, i) {
2248                 if (clear)
2249                         wclear(view->win);
2250                 redraw_view(view);
2251                 update_view_title(view);
2252         }
2253 }
2254
2255 static void
2256 toggle_view_option(bool *option, const char *help)
2257 {
2258         *option = !*option;
2259         redraw_display(FALSE);
2260         report("%sabling %s", *option ? "En" : "Dis", help);
2261 }
2262
2263 /*
2264  * Navigation
2265  */
2266
2267 /* Scrolling backend */
2268 static void
2269 do_scroll_view(struct view *view, int lines)
2270 {
2271         bool redraw_current_line = FALSE;
2272
2273         /* The rendering expects the new offset. */
2274         view->offset += lines;
2275
2276         assert(0 <= view->offset && view->offset < view->lines);
2277         assert(lines);
2278
2279         /* Move current line into the view. */
2280         if (view->lineno < view->offset) {
2281                 view->lineno = view->offset;
2282                 redraw_current_line = TRUE;
2283         } else if (view->lineno >= view->offset + view->height) {
2284                 view->lineno = view->offset + view->height - 1;
2285                 redraw_current_line = TRUE;
2286         }
2287
2288         assert(view->offset <= view->lineno && view->lineno < view->lines);
2289
2290         /* Redraw the whole screen if scrolling is pointless. */
2291         if (view->height < ABS(lines)) {
2292                 redraw_view(view);
2293
2294         } else {
2295                 int line = lines > 0 ? view->height - lines : 0;
2296                 int end = line + ABS(lines);
2297
2298                 scrollok(view->win, TRUE);
2299                 wscrl(view->win, lines);
2300                 scrollok(view->win, FALSE);
2301
2302                 while (line < end && draw_view_line(view, line))
2303                         line++;
2304
2305                 if (redraw_current_line)
2306                         draw_view_line(view, view->lineno - view->offset);
2307                 wnoutrefresh(view->win);
2308         }
2309
2310         view->has_scrolled = TRUE;
2311         report("");
2312 }
2313
2314 /* Scroll frontend */
2315 static void
2316 scroll_view(struct view *view, enum request request)
2317 {
2318         int lines = 1;
2319
2320         assert(view_is_displayed(view));
2321
2322         switch (request) {
2323         case REQ_SCROLL_LEFT:
2324                 if (view->yoffset == 0) {
2325                         report("Cannot scroll beyond the first column");
2326                         return;
2327                 }
2328                 if (view->yoffset <= SCROLL_INTERVAL)
2329                         view->yoffset = 0;
2330                 else
2331                         view->yoffset -= SCROLL_INTERVAL;
2332                 redraw_view_from(view, 0);
2333                 report("");
2334                 return;
2335         case REQ_SCROLL_RIGHT:
2336                 if (!view->can_hscroll) {
2337                         report("Cannot scroll beyond the last column");
2338                         return;
2339                 }
2340                 view->yoffset += SCROLL_INTERVAL;
2341                 redraw_view(view);
2342                 report("");
2343                 return;
2344         case REQ_SCROLL_PAGE_DOWN:
2345                 lines = view->height;
2346         case REQ_SCROLL_LINE_DOWN:
2347                 if (view->offset + lines > view->lines)
2348                         lines = view->lines - view->offset;
2349
2350                 if (lines == 0 || view->offset + view->height >= view->lines) {
2351                         report("Cannot scroll beyond the last line");
2352                         return;
2353                 }
2354                 break;
2355
2356         case REQ_SCROLL_PAGE_UP:
2357                 lines = view->height;
2358         case REQ_SCROLL_LINE_UP:
2359                 if (lines > view->offset)
2360                         lines = view->offset;
2361
2362                 if (lines == 0) {
2363                         report("Cannot scroll beyond the first line");
2364                         return;
2365                 }
2366
2367                 lines = -lines;
2368                 break;
2369
2370         default:
2371                 die("request %d not handled in switch", request);
2372         }
2373
2374         do_scroll_view(view, lines);
2375 }
2376
2377 /* Cursor moving */
2378 static void
2379 move_view(struct view *view, enum request request)
2380 {
2381         int scroll_steps = 0;
2382         int steps;
2383
2384         switch (request) {
2385         case REQ_MOVE_FIRST_LINE:
2386                 steps = -view->lineno;
2387                 break;
2388
2389         case REQ_MOVE_LAST_LINE:
2390                 steps = view->lines - view->lineno - 1;
2391                 break;
2392
2393         case REQ_MOVE_PAGE_UP:
2394                 steps = view->height > view->lineno
2395                       ? -view->lineno : -view->height;
2396                 break;
2397
2398         case REQ_MOVE_PAGE_DOWN:
2399                 steps = view->lineno + view->height >= view->lines
2400                       ? view->lines - view->lineno - 1 : view->height;
2401                 break;
2402
2403         case REQ_MOVE_UP:
2404                 steps = -1;
2405                 break;
2406
2407         case REQ_MOVE_DOWN:
2408                 steps = 1;
2409                 break;
2410
2411         default:
2412                 die("request %d not handled in switch", request);
2413         }
2414
2415         if (steps <= 0 && view->lineno == 0) {
2416                 report("Cannot move beyond the first line");
2417                 return;
2418
2419         } else if (steps >= 0 && view->lineno + 1 >= view->lines) {
2420                 report("Cannot move beyond the last line");
2421                 return;
2422         }
2423
2424         /* Move the current line */
2425         view->lineno += steps;
2426         assert(0 <= view->lineno && view->lineno < view->lines);
2427
2428         /* Check whether the view needs to be scrolled */
2429         if (view->lineno < view->offset ||
2430             view->lineno >= view->offset + view->height) {
2431                 scroll_steps = steps;
2432                 if (steps < 0 && -steps > view->offset) {
2433                         scroll_steps = -view->offset;
2434
2435                 } else if (steps > 0) {
2436                         if (view->lineno == view->lines - 1 &&
2437                             view->lines > view->height) {
2438                                 scroll_steps = view->lines - view->offset - 1;
2439                                 if (scroll_steps >= view->height)
2440                                         scroll_steps -= view->height - 1;
2441                         }
2442                 }
2443         }
2444
2445         if (!view_is_displayed(view)) {
2446                 view->offset += scroll_steps;
2447                 assert(0 <= view->offset && view->offset < view->lines);
2448                 view->ops->select(view, &view->line[view->lineno]);
2449                 return;
2450         }
2451
2452         /* Repaint the old "current" line if we be scrolling */
2453         if (ABS(steps) < view->height)
2454                 draw_view_line(view, view->lineno - steps - view->offset);
2455
2456         if (scroll_steps) {
2457                 do_scroll_view(view, scroll_steps);
2458                 return;
2459         }
2460
2461         /* Draw the current line */
2462         draw_view_line(view, view->lineno - view->offset);
2463
2464         wnoutrefresh(view->win);
2465         report("");
2466 }
2467
2468
2469 /*
2470  * Searching
2471  */
2472
2473 static void search_view(struct view *view, enum request request);
2474
2475 static void
2476 select_view_line(struct view *view, unsigned long lineno)
2477 {
2478         if (lineno - view->offset >= view->height) {
2479                 view->offset = lineno;
2480                 view->lineno = lineno;
2481                 if (view_is_displayed(view))
2482                         redraw_view(view);
2483
2484         } else {
2485                 unsigned long old_lineno = view->lineno - view->offset;
2486
2487                 view->lineno = lineno;
2488                 if (view_is_displayed(view)) {
2489                         draw_view_line(view, old_lineno);
2490                         draw_view_line(view, view->lineno - view->offset);
2491                         wnoutrefresh(view->win);
2492                 } else {
2493                         view->ops->select(view, &view->line[view->lineno]);
2494                 }
2495         }
2496 }
2497
2498 static void
2499 find_next(struct view *view, enum request request)
2500 {
2501         unsigned long lineno = view->lineno;
2502         int direction;
2503
2504         if (!*view->grep) {
2505                 if (!*opt_search)
2506                         report("No previous search");
2507                 else
2508                         search_view(view, request);
2509                 return;
2510         }
2511
2512         switch (request) {
2513         case REQ_SEARCH:
2514         case REQ_FIND_NEXT:
2515                 direction = 1;
2516                 break;
2517
2518         case REQ_SEARCH_BACK:
2519         case REQ_FIND_PREV:
2520                 direction = -1;
2521                 break;
2522
2523         default:
2524                 return;
2525         }
2526
2527         if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
2528                 lineno += direction;
2529
2530         /* Note, lineno is unsigned long so will wrap around in which case it
2531          * will become bigger than view->lines. */
2532         for (; lineno < view->lines; lineno += direction) {
2533                 if (view->ops->grep(view, &view->line[lineno])) {
2534                         select_view_line(view, lineno);
2535                         report("Line %ld matches '%s'", lineno + 1, view->grep);
2536                         return;
2537                 }
2538         }
2539
2540         report("No match found for '%s'", view->grep);
2541 }
2542
2543 static void
2544 search_view(struct view *view, enum request request)
2545 {
2546         int regex_err;
2547
2548         if (view->regex) {
2549                 regfree(view->regex);
2550                 *view->grep = 0;
2551         } else {
2552                 view->regex = calloc(1, sizeof(*view->regex));
2553                 if (!view->regex)
2554                         return;
2555         }
2556
2557         regex_err = regcomp(view->regex, opt_search, REG_EXTENDED);
2558         if (regex_err != 0) {
2559                 char buf[SIZEOF_STR] = "unknown error";
2560
2561                 regerror(regex_err, view->regex, buf, sizeof(buf));
2562                 report("Search failed: %s", buf);
2563                 return;
2564         }
2565
2566         string_copy(view->grep, opt_search);
2567
2568         find_next(view, request);
2569 }
2570
2571 /*
2572  * Incremental updating
2573  */
2574
2575 static void
2576 reset_view(struct view *view)
2577 {
2578         int i;
2579
2580         for (i = 0; i < view->lines; i++)
2581                 free(view->line[i].data);
2582         free(view->line);
2583
2584         view->p_offset = view->offset;
2585         view->p_yoffset = view->yoffset;
2586         view->p_lineno = view->lineno;
2587
2588         view->line = NULL;
2589         view->offset = 0;
2590         view->yoffset = 0;
2591         view->lines  = 0;
2592         view->lineno = 0;
2593         view->line_alloc = 0;
2594         view->vid[0] = 0;
2595         view->update_secs = 0;
2596 }
2597
2598 static void
2599 free_argv(const char *argv[])
2600 {
2601         int argc;
2602
2603         for (argc = 0; argv[argc]; argc++)
2604                 free((void *) argv[argc]);
2605 }
2606
2607 static bool
2608 format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags flags)
2609 {
2610         char buf[SIZEOF_STR];
2611         int argc;
2612         bool noreplace = flags == FORMAT_NONE;
2613
2614         free_argv(dst_argv);
2615
2616         for (argc = 0; src_argv[argc]; argc++) {
2617                 const char *arg = src_argv[argc];
2618                 size_t bufpos = 0;
2619
2620                 while (arg) {
2621                         char *next = strstr(arg, "%(");
2622                         int len = next - arg;
2623                         const char *value;
2624
2625                         if (!next || noreplace) {
2626                                 if (flags == FORMAT_DASH && !strcmp(arg, "--"))
2627                                         noreplace = TRUE;
2628                                 len = strlen(arg);
2629                                 value = "";
2630
2631                         } else if (!prefixcmp(next, "%(directory)")) {
2632                                 value = opt_path;
2633
2634                         } else if (!prefixcmp(next, "%(file)")) {
2635                                 value = opt_file;
2636
2637                         } else if (!prefixcmp(next, "%(ref)")) {
2638                                 value = *opt_ref ? opt_ref : "HEAD";
2639
2640                         } else if (!prefixcmp(next, "%(head)")) {
2641                                 value = ref_head;
2642
2643                         } else if (!prefixcmp(next, "%(commit)")) {
2644                                 value = ref_commit;
2645
2646                         } else if (!prefixcmp(next, "%(blob)")) {
2647                                 value = ref_blob;
2648
2649                         } else {
2650                                 report("Unknown replacement: `%s`", next);
2651                                 return FALSE;
2652                         }
2653
2654                         if (!string_format_from(buf, &bufpos, "%.*s%s", len, arg, value))
2655                                 return FALSE;
2656
2657                         arg = next && !noreplace ? strchr(next, ')') + 1 : NULL;
2658                 }
2659
2660                 dst_argv[argc] = strdup(buf);
2661                 if (!dst_argv[argc])
2662                         break;
2663         }
2664
2665         dst_argv[argc] = NULL;
2666
2667         return src_argv[argc] == NULL;
2668 }
2669
2670 static bool
2671 restore_view_position(struct view *view)
2672 {
2673         if (!view->p_restore || (view->pipe && view->lines <= view->p_lineno))
2674                 return FALSE;
2675
2676         /* Changing the view position cancels the restoring. */
2677         /* FIXME: Changing back to the first line is not detected. */
2678         if (view->offset != 0 || view->lineno != 0) {
2679                 view->p_restore = FALSE;
2680                 return FALSE;
2681         }
2682
2683         if (view->p_lineno >= view->lines) {
2684                 view->p_lineno = view->lines > 0 ? view->lines - 1 : 0;
2685                 if (view->p_offset >= view->p_lineno) {
2686                         unsigned long half = view->height / 2;
2687
2688                         if (view->p_lineno > half)
2689                                 view->p_offset = view->p_lineno - half;
2690                         else
2691                                 view->p_offset = 0;
2692                 }
2693         }
2694
2695         if (view_is_displayed(view) &&
2696             view->offset != view->p_offset &&
2697             view->lineno != view->p_lineno)
2698                 werase(view->win);
2699
2700         view->offset = view->p_offset;
2701         view->yoffset = view->p_yoffset;
2702         view->lineno = view->p_lineno;
2703         view->p_restore = FALSE;
2704
2705         return TRUE;
2706 }
2707
2708 static void
2709 end_update(struct view *view, bool force)
2710 {
2711         if (!view->pipe)
2712                 return;
2713         while (!view->ops->read(view, NULL))
2714                 if (!force)
2715                         return;
2716         set_nonblocking_input(FALSE);
2717         if (force)
2718                 kill_io(view->pipe);
2719         done_io(view->pipe);
2720         view->pipe = NULL;
2721 }
2722
2723 static void
2724 setup_update(struct view *view, const char *vid)
2725 {
2726         set_nonblocking_input(TRUE);
2727         reset_view(view);
2728         string_copy_rev(view->vid, vid);
2729         view->pipe = &view->io;
2730         view->start_time = time(NULL);
2731 }
2732
2733 static bool
2734 prepare_update(struct view *view, const char *argv[], const char *dir,
2735                enum format_flags flags)
2736 {
2737         if (view->pipe)
2738                 end_update(view, TRUE);
2739         return init_io_rd(&view->io, argv, dir, flags);
2740 }
2741
2742 static bool
2743 prepare_update_file(struct view *view, const char *name)
2744 {
2745         if (view->pipe)
2746                 end_update(view, TRUE);
2747         return io_open(&view->io, name);
2748 }
2749
2750 static bool
2751 begin_update(struct view *view, bool refresh)
2752 {
2753         if (view->pipe)
2754                 end_update(view, TRUE);
2755
2756         if (refresh) {
2757                 if (!start_io(&view->io))
2758                         return FALSE;
2759
2760         } else {
2761                 if (view == VIEW(REQ_VIEW_TREE) && strcmp(view->vid, view->id))
2762                         opt_path[0] = 0;
2763
2764                 if (!run_io_rd(&view->io, view->ops->argv, FORMAT_ALL))
2765                         return FALSE;
2766
2767                 /* Put the current ref_* value to the view title ref
2768                  * member. This is needed by the blob view. Most other
2769                  * views sets it automatically after loading because the
2770                  * first line is a commit line. */
2771                 string_copy_rev(view->ref, view->id);
2772         }
2773
2774         setup_update(view, view->id);
2775
2776         return TRUE;
2777 }
2778
2779 #define ITEM_CHUNK_SIZE 256
2780 static void *
2781 realloc_items(void *mem, size_t *size, size_t new_size, size_t item_size)
2782 {
2783         size_t num_chunks = *size / ITEM_CHUNK_SIZE;
2784         size_t num_chunks_new = (new_size + ITEM_CHUNK_SIZE - 1) / ITEM_CHUNK_SIZE;
2785
2786         if (mem == NULL || num_chunks != num_chunks_new) {
2787                 *size = num_chunks_new * ITEM_CHUNK_SIZE;
2788                 mem = realloc(mem, *size * item_size);
2789         }
2790
2791         return mem;
2792 }
2793
2794 static struct line *
2795 realloc_lines(struct view *view, size_t line_size)
2796 {
2797         size_t alloc = view->line_alloc;
2798         struct line *tmp = realloc_items(view->line, &alloc, line_size,
2799                                          sizeof(*view->line));
2800
2801         if (!tmp)
2802                 return NULL;
2803
2804         view->line = tmp;
2805         view->line_alloc = alloc;
2806         return view->line;
2807 }
2808
2809 static bool
2810 update_view(struct view *view)
2811 {
2812         char out_buffer[BUFSIZ * 2];
2813         char *line;
2814         /* Clear the view and redraw everything since the tree sorting
2815          * might have rearranged things. */
2816         bool redraw = view->lines == 0;
2817         bool can_read = TRUE;
2818
2819         if (!view->pipe)
2820                 return TRUE;
2821
2822         if (!io_can_read(view->pipe)) {
2823                 if (view->lines == 0) {
2824                         time_t secs = time(NULL) - view->start_time;
2825
2826                         if (secs > 1 && secs > view->update_secs) {
2827                                 if (view->update_secs == 0)
2828                                         redraw_view(view);
2829                                 update_view_title(view);
2830                                 view->update_secs = secs;
2831                         }
2832                 }
2833                 return TRUE;
2834         }
2835
2836         for (; (line = io_get(view->pipe, '\n', can_read)); can_read = FALSE) {
2837                 if (opt_iconv != ICONV_NONE) {
2838                         ICONV_CONST char *inbuf = line;
2839                         size_t inlen = strlen(line) + 1;
2840
2841                         char *outbuf = out_buffer;
2842                         size_t outlen = sizeof(out_buffer);
2843
2844                         size_t ret;
2845
2846                         ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
2847                         if (ret != (size_t) -1)
2848                                 line = out_buffer;
2849                 }
2850
2851                 if (!view->ops->read(view, line)) {
2852                         report("Allocation failure");
2853                         end_update(view, TRUE);
2854                         return FALSE;
2855                 }
2856         }
2857
2858         {
2859                 unsigned long lines = view->lines;
2860                 int digits;
2861
2862                 for (digits = 0; lines; digits++)
2863                         lines /= 10;
2864
2865                 /* Keep the displayed view in sync with line number scaling. */
2866                 if (digits != view->digits) {
2867                         view->digits = digits;
2868                         if (opt_line_number || view == VIEW(REQ_VIEW_BLAME))
2869                                 redraw = TRUE;
2870                 }
2871         }
2872
2873         if (io_error(view->pipe)) {
2874                 report("Failed to read: %s", io_strerror(view->pipe));
2875                 end_update(view, TRUE);
2876
2877         } else if (io_eof(view->pipe)) {
2878                 report("");
2879                 end_update(view, FALSE);
2880         }
2881
2882         if (restore_view_position(view))
2883                 redraw = TRUE;
2884
2885         if (!view_is_displayed(view))
2886                 return TRUE;
2887
2888         if (redraw)
2889                 redraw_view_from(view, 0);
2890         else
2891                 redraw_view_dirty(view);
2892
2893         /* Update the title _after_ the redraw so that if the redraw picks up a
2894          * commit reference in view->ref it'll be available here. */
2895         update_view_title(view);
2896         return TRUE;
2897 }
2898
2899 static struct line *
2900 add_line_data(struct view *view, void *data, enum line_type type)
2901 {
2902         struct line *line;
2903
2904         if (!realloc_lines(view, view->lines + 1))
2905                 return NULL;
2906
2907         line = &view->line[view->lines++];
2908         memset(line, 0, sizeof(*line));
2909         line->type = type;
2910         line->data = data;
2911         line->dirty = 1;
2912
2913         return line;
2914 }
2915
2916 static struct line *
2917 add_line_text(struct view *view, const char *text, enum line_type type)
2918 {
2919         char *data = text ? strdup(text) : NULL;
2920
2921         return data ? add_line_data(view, data, type) : NULL;
2922 }
2923
2924 static struct line *
2925 add_line_format(struct view *view, enum line_type type, const char *fmt, ...)
2926 {
2927         char buf[SIZEOF_STR];
2928         va_list args;
2929
2930         va_start(args, fmt);
2931         if (vsnprintf(buf, sizeof(buf), fmt, args) >= sizeof(buf))
2932                 buf[0] = 0;
2933         va_end(args);
2934
2935         return buf[0] ? add_line_text(view, buf, type) : NULL;
2936 }
2937
2938 /*
2939  * View opening
2940  */
2941
2942 enum open_flags {
2943         OPEN_DEFAULT = 0,       /* Use default view switching. */
2944         OPEN_SPLIT = 1,         /* Split current view. */
2945         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
2946         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2947         OPEN_NOMAXIMIZE = 8,    /* Do not maximize the current view. */
2948         OPEN_REFRESH = 16,      /* Refresh view using previous command. */
2949         OPEN_PREPARED = 32,     /* Open already prepared command. */
2950 };
2951
2952 static void
2953 open_view(struct view *prev, enum request request, enum open_flags flags)
2954 {
2955         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
2956         bool split = !!(flags & OPEN_SPLIT);
2957         bool reload = !!(flags & (OPEN_RELOAD | OPEN_REFRESH | OPEN_PREPARED));
2958         bool nomaximize = !!(flags & (OPEN_NOMAXIMIZE | OPEN_REFRESH));
2959         struct view *view = VIEW(request);
2960         int nviews = displayed_views();
2961         struct view *base_view = display[0];
2962
2963         if (view == prev && nviews == 1 && !reload) {
2964                 report("Already in %s view", view->name);
2965                 return;
2966         }
2967
2968         if (view->git_dir && !opt_git_dir[0]) {
2969                 report("The %s view is disabled in pager view", view->name);
2970                 return;
2971         }
2972
2973         if (split) {
2974                 display[1] = view;
2975                 if (!backgrounded)
2976                         current_view = 1;
2977         } else if (!nomaximize) {
2978                 /* Maximize the current view. */
2979                 memset(display, 0, sizeof(display));
2980                 current_view = 0;
2981                 display[current_view] = view;
2982         }
2983
2984         /* Resize the view when switching between split- and full-screen,
2985          * or when switching between two different full-screen views. */
2986         if (nviews != displayed_views() ||
2987             (nviews == 1 && base_view != display[0]))
2988                 resize_display();
2989
2990         if (view->ops->open) {
2991                 if (view->pipe)
2992                         end_update(view, TRUE);
2993                 if (!view->ops->open(view)) {
2994                         report("Failed to load %s view", view->name);
2995                         return;
2996                 }
2997                 restore_view_position(view);
2998
2999         } else if ((reload || strcmp(view->vid, view->id)) &&
3000                    !begin_update(view, flags & (OPEN_REFRESH | OPEN_PREPARED))) {
3001                 report("Failed to load %s view", view->name);
3002                 return;
3003         }
3004
3005         if (split && prev->lineno - prev->offset >= prev->height) {
3006                 /* Take the title line into account. */
3007                 int lines = prev->lineno - prev->offset - prev->height + 1;
3008
3009                 /* Scroll the view that was split if the current line is
3010                  * outside the new limited view. */
3011                 do_scroll_view(prev, lines);
3012         }
3013
3014         if (prev && view != prev) {
3015                 if (split && !backgrounded) {
3016                         /* "Blur" the previous view. */
3017                         update_view_title(prev);
3018                 }
3019
3020                 view->parent = prev;
3021         }
3022
3023         if (view->pipe && view->lines == 0) {
3024                 /* Clear the old view and let the incremental updating refill
3025                  * the screen. */
3026                 werase(view->win);
3027                 view->p_restore = flags & (OPEN_RELOAD | OPEN_REFRESH);
3028                 report("");
3029         } else if (view_is_displayed(view)) {
3030                 redraw_view(view);
3031                 report("");
3032         }
3033
3034         /* If the view is backgrounded the above calls to report()
3035          * won't redraw the view title. */
3036         if (backgrounded)
3037                 update_view_title(view);
3038 }
3039
3040 static void
3041 open_external_viewer(const char *argv[], const char *dir)
3042 {
3043         def_prog_mode();           /* save current tty modes */
3044         endwin();                  /* restore original tty modes */
3045         run_io_fg(argv, dir);
3046         fprintf(stderr, "Press Enter to continue");
3047         getc(opt_tty);
3048         reset_prog_mode();
3049         redraw_display(TRUE);
3050 }
3051
3052 static void
3053 open_mergetool(const char *file)
3054 {
3055         const char *mergetool_argv[] = { "git", "mergetool", file, NULL };
3056
3057         open_external_viewer(mergetool_argv, opt_cdup);
3058 }
3059
3060 static void
3061 open_editor(bool from_root, const char *file)
3062 {
3063         const char *editor_argv[] = { "vi", file, NULL };
3064         const char *editor;
3065
3066         editor = getenv("GIT_EDITOR");
3067         if (!editor && *opt_editor)
3068                 editor = opt_editor;
3069         if (!editor)
3070                 editor = getenv("VISUAL");
3071         if (!editor)
3072                 editor = getenv("EDITOR");
3073         if (!editor)
3074                 editor = "vi";
3075
3076         editor_argv[0] = editor;
3077         open_external_viewer(editor_argv, from_root ? opt_cdup : NULL);
3078 }
3079
3080 static void
3081 open_run_request(enum request request)
3082 {
3083         struct run_request *req = get_run_request(request);
3084         const char *argv[ARRAY_SIZE(req->argv)] = { NULL };
3085
3086         if (!req) {
3087                 report("Unknown run request");
3088                 return;
3089         }
3090
3091         if (format_argv(argv, req->argv, FORMAT_ALL))
3092                 open_external_viewer(argv, NULL);
3093         free_argv(argv);
3094 }
3095
3096 /*
3097  * User request switch noodle
3098  */
3099
3100 static int
3101 view_driver(struct view *view, enum request request)
3102 {
3103         int i;
3104
3105         if (request == REQ_NONE) {
3106                 doupdate();
3107                 return TRUE;
3108         }
3109
3110         if (request > REQ_NONE) {
3111                 open_run_request(request);
3112                 /* FIXME: When all views can refresh always do this. */
3113                 if (view == VIEW(REQ_VIEW_STATUS) ||
3114                     view == VIEW(REQ_VIEW_MAIN) ||
3115                     view == VIEW(REQ_VIEW_LOG) ||
3116                     view == VIEW(REQ_VIEW_STAGE))
3117                         request = REQ_REFRESH;
3118                 else
3119                         return TRUE;
3120         }
3121
3122         if (view && view->lines) {
3123                 request = view->ops->request(view, request, &view->line[view->lineno]);
3124                 if (request == REQ_NONE)
3125                         return TRUE;
3126         }
3127
3128         switch (request) {
3129         case REQ_MOVE_UP:
3130         case REQ_MOVE_DOWN:
3131         case REQ_MOVE_PAGE_UP:
3132         case REQ_MOVE_PAGE_DOWN:
3133         case REQ_MOVE_FIRST_LINE:
3134         case REQ_MOVE_LAST_LINE:
3135                 move_view(view, request);
3136                 break;
3137
3138         case REQ_SCROLL_LEFT:
3139         case REQ_SCROLL_RIGHT:
3140         case REQ_SCROLL_LINE_DOWN:
3141         case REQ_SCROLL_LINE_UP:
3142         case REQ_SCROLL_PAGE_DOWN:
3143         case REQ_SCROLL_PAGE_UP:
3144                 scroll_view(view, request);
3145                 break;
3146
3147         case REQ_VIEW_BLAME:
3148                 if (!opt_file[0]) {
3149                         report("No file chosen, press %s to open tree view",
3150                                get_key(REQ_VIEW_TREE));
3151                         break;
3152                 }
3153                 open_view(view, request, OPEN_DEFAULT);
3154                 break;
3155
3156         case REQ_VIEW_BLOB:
3157                 if (!ref_blob[0]) {
3158                         report("No file chosen, press %s to open tree view",
3159                                get_key(REQ_VIEW_TREE));
3160                         break;
3161                 }
3162                 open_view(view, request, OPEN_DEFAULT);
3163                 break;
3164
3165         case REQ_VIEW_PAGER:
3166                 if (!VIEW(REQ_VIEW_PAGER)->pipe && !VIEW(REQ_VIEW_PAGER)->lines) {
3167                         report("No pager content, press %s to run command from prompt",
3168                                get_key(REQ_PROMPT));
3169                         break;
3170                 }
3171                 open_view(view, request, OPEN_DEFAULT);
3172                 break;
3173
3174         case REQ_VIEW_STAGE:
3175                 if (!VIEW(REQ_VIEW_STAGE)->lines) {
3176                         report("No stage content, press %s to open the status view and choose file",
3177                                get_key(REQ_VIEW_STATUS));
3178                         break;
3179                 }
3180                 open_view(view, request, OPEN_DEFAULT);
3181                 break;
3182
3183         case REQ_VIEW_STATUS:
3184                 if (opt_is_inside_work_tree == FALSE) {
3185                         report("The status view requires a working tree");
3186                         break;
3187                 }
3188                 open_view(view, request, OPEN_DEFAULT);
3189                 break;
3190
3191         case REQ_VIEW_MAIN:
3192         case REQ_VIEW_DIFF:
3193         case REQ_VIEW_LOG:
3194         case REQ_VIEW_TREE:
3195         case REQ_VIEW_HELP:
3196                 open_view(view, request, OPEN_DEFAULT);
3197                 break;
3198
3199         case REQ_NEXT:
3200         case REQ_PREVIOUS:
3201                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
3202
3203                 if ((view == VIEW(REQ_VIEW_DIFF) &&
3204                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
3205                    (view == VIEW(REQ_VIEW_DIFF) &&
3206                      view->parent == VIEW(REQ_VIEW_BLAME)) ||
3207                    (view == VIEW(REQ_VIEW_STAGE) &&
3208                      view->parent == VIEW(REQ_VIEW_STATUS)) ||
3209                    (view == VIEW(REQ_VIEW_BLOB) &&
3210                      view->parent == VIEW(REQ_VIEW_TREE))) {
3211                         int line;
3212
3213                         view = view->parent;
3214                         line = view->lineno;
3215                         move_view(view, request);
3216                         if (view_is_displayed(view))
3217                                 update_view_title(view);
3218                         if (line != view->lineno)
3219                                 view->ops->request(view, REQ_ENTER,
3220                                                    &view->line[view->lineno]);
3221
3222                 } else {
3223                         move_view(view, request);
3224                 }
3225                 break;
3226
3227         case REQ_VIEW_NEXT:
3228         {
3229                 int nviews = displayed_views();
3230                 int next_view = (current_view + 1) % nviews;
3231
3232                 if (next_view == current_view) {
3233                         report("Only one view is displayed");
3234                         break;
3235                 }
3236
3237                 current_view = next_view;
3238                 /* Blur out the title of the previous view. */
3239                 update_view_title(view);
3240                 report("");
3241                 break;
3242         }
3243         case REQ_REFRESH:
3244                 report("Refreshing is not yet supported for the %s view", view->name);
3245                 break;
3246
3247         case REQ_MAXIMIZE:
3248                 if (displayed_views() == 2)
3249                         open_view(view, VIEW_REQ(view), OPEN_DEFAULT);
3250                 break;
3251
3252         case REQ_TOGGLE_LINENO:
3253                 toggle_view_option(&opt_line_number, "line numbers");
3254                 break;
3255
3256         case REQ_TOGGLE_DATE:
3257                 toggle_view_option(&opt_date, "date display");
3258                 break;
3259
3260         case REQ_TOGGLE_AUTHOR:
3261                 toggle_view_option(&opt_author, "author display");
3262                 break;
3263
3264         case REQ_TOGGLE_REV_GRAPH:
3265                 toggle_view_option(&opt_rev_graph, "revision graph display");
3266                 break;
3267
3268         case REQ_TOGGLE_REFS:
3269                 toggle_view_option(&opt_show_refs, "reference display");
3270                 break;
3271
3272         case REQ_SEARCH:
3273         case REQ_SEARCH_BACK:
3274                 search_view(view, request);
3275                 break;
3276
3277         case REQ_FIND_NEXT:
3278         case REQ_FIND_PREV:
3279                 find_next(view, request);
3280                 break;
3281
3282         case REQ_STOP_LOADING:
3283                 for (i = 0; i < ARRAY_SIZE(views); i++) {
3284                         view = &views[i];
3285                         if (view->pipe)
3286                                 report("Stopped loading the %s view", view->name),
3287                         end_update(view, TRUE);
3288                 }
3289                 break;
3290
3291         case REQ_SHOW_VERSION:
3292                 report("tig-%s (built %s)", TIG_VERSION, __DATE__);
3293                 return TRUE;
3294
3295         case REQ_SCREEN_REDRAW:
3296                 redraw_display(TRUE);
3297                 break;
3298
3299         case REQ_EDIT:
3300                 report("Nothing to edit");
3301                 break;
3302
3303         case REQ_ENTER:
3304                 report("Nothing to enter");
3305                 break;
3306
3307         case REQ_VIEW_CLOSE:
3308                 /* XXX: Mark closed views by letting view->parent point to the
3309                  * view itself. Parents to closed view should never be
3310                  * followed. */
3311                 if (view->parent &&
3312                     view->parent->parent != view->parent) {
3313                         memset(display, 0, sizeof(display));
3314                         current_view = 0;
3315                         display[current_view] = view->parent;
3316                         view->parent = view;
3317                         resize_display();
3318                         redraw_display(FALSE);
3319                         report("");
3320                         break;
3321                 }
3322                 /* Fall-through */
3323         case REQ_QUIT:
3324                 return FALSE;
3325
3326         default:
3327                 report("Unknown key, press 'h' for help");
3328                 return TRUE;
3329         }
3330
3331         return TRUE;
3332 }
3333
3334
3335 /*
3336  * View backend utilities
3337  */
3338
3339 /* Parse author lines where the name may be empty:
3340  *      author  <email@address.tld> 1138474660 +0100
3341  */
3342 static void
3343 parse_author_line(char *ident, char *author, size_t authorsize, struct tm *tm)
3344 {
3345         char *nameend = strchr(ident, '<');
3346         char *emailend = strchr(ident, '>');
3347
3348         if (nameend && emailend)
3349                 *nameend = *emailend = 0;
3350         ident = chomp_string(ident);
3351         if (!*ident) {
3352                 if (nameend)
3353                         ident = chomp_string(nameend + 1);
3354                 if (!*ident)
3355                         ident = "Unknown";
3356         }
3357
3358         string_ncopy_do(author, authorsize, ident, strlen(ident));
3359
3360         /* Parse epoch and timezone */
3361         if (emailend && emailend[1] == ' ') {
3362                 char *secs = emailend + 2;
3363                 char *zone = strchr(secs, ' ');
3364                 time_t time = (time_t) atol(secs);
3365
3366                 if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
3367                         long tz;
3368
3369                         zone++;
3370                         tz  = ('0' - zone[1]) * 60 * 60 * 10;
3371                         tz += ('0' - zone[2]) * 60 * 60;
3372                         tz += ('0' - zone[3]) * 60;
3373                         tz += ('0' - zone[4]) * 60;
3374
3375                         if (zone[0] == '-')
3376                                 tz = -tz;
3377
3378                         time -= tz;
3379                 }
3380
3381                 gmtime_r(&time, tm);
3382         }
3383 }
3384
3385 static enum input_status
3386 select_commit_parent_handler(void *data, char *buf, int c)
3387 {
3388         size_t parents = *(size_t *) data;
3389         int parent = 0;
3390
3391         if (!isdigit(c))
3392                 return INPUT_SKIP;
3393
3394         if (*buf)
3395                 parent = atoi(buf) * 10;
3396         parent += c - '0';
3397
3398         if (parent > parents)
3399                 return INPUT_SKIP;
3400         return INPUT_OK;
3401 }
3402
3403 static bool
3404 select_commit_parent(const char *id, char rev[SIZEOF_REV])
3405 {
3406         char buf[SIZEOF_STR * 4];
3407         const char *revlist_argv[] = {
3408                 "git", "rev-list", "-1", "--parents", id, NULL
3409         };
3410         int parents;
3411
3412         if (!run_io_buf(revlist_argv, buf, sizeof(buf)) ||
3413             !*chomp_string(buf) ||
3414             (parents = (strlen(buf) / 40) - 1) < 0) {
3415                 report("Failed to get parent information");
3416                 return FALSE;
3417
3418         } else if (parents == 0) {
3419                 report("The selected commit has no parents");
3420                 return FALSE;
3421         }
3422
3423         if (parents > 1) {
3424                 char prompt[SIZEOF_STR];
3425                 char *result;
3426
3427                 if (!string_format(prompt, "Which parent? [1..%d] ", parents))
3428                         return FALSE;
3429                 result = prompt_input(prompt, select_commit_parent_handler, &parents);
3430                 if (!result)
3431                         return FALSE;
3432                 parents = atoi(result);
3433         }
3434
3435         string_copy_rev(rev, &buf[41 * parents]);
3436         return TRUE;
3437 }
3438
3439 /*
3440  * Pager backend
3441  */
3442
3443 static bool
3444 pager_draw(struct view *view, struct line *line, unsigned int lineno)
3445 {
3446         char text[SIZEOF_STR];
3447
3448         if (opt_line_number && draw_lineno(view, lineno))
3449                 return TRUE;
3450
3451         string_expand(text, sizeof(text), line->data, opt_tab_size);
3452         draw_text(view, line->type, text, TRUE);
3453         return TRUE;
3454 }
3455
3456 static bool
3457 add_describe_ref(char *buf, size_t *bufpos, const char *commit_id, const char *sep)
3458 {
3459         const char *describe_argv[] = { "git", "describe", commit_id, NULL };
3460         char refbuf[SIZEOF_STR];
3461         char *ref = NULL;
3462
3463         if (run_io_buf(describe_argv, refbuf, sizeof(refbuf)))
3464                 ref = chomp_string(refbuf);
3465
3466         if (!ref || !*ref)
3467                 return TRUE;
3468
3469         /* This is the only fatal call, since it can "corrupt" the buffer. */
3470         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
3471                 return FALSE;
3472
3473         return TRUE;
3474 }
3475
3476 static void
3477 add_pager_refs(struct view *view, struct line *line)
3478 {
3479         char buf[SIZEOF_STR];
3480         char *commit_id = (char *)line->data + STRING_SIZE("commit ");
3481         struct ref **refs;
3482         size_t bufpos = 0, refpos = 0;
3483         const char *sep = "Refs: ";
3484         bool is_tag = FALSE;
3485
3486         assert(line->type == LINE_COMMIT);
3487
3488         refs = get_refs(commit_id);
3489         if (!refs) {
3490                 if (view == VIEW(REQ_VIEW_DIFF))
3491                         goto try_add_describe_ref;
3492                 return;
3493         }
3494
3495         do {
3496                 struct ref *ref = refs[refpos];
3497                 const char *fmt = ref->tag    ? "%s[%s]" :
3498                                   ref->remote ? "%s<%s>" : "%s%s";
3499
3500                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
3501                         return;
3502                 sep = ", ";
3503                 if (ref->tag)
3504                         is_tag = TRUE;
3505         } while (refs[refpos++]->next);
3506
3507         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
3508 try_add_describe_ref:
3509                 /* Add <tag>-g<commit_id> "fake" reference. */
3510                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
3511                         return;
3512         }
3513
3514         if (bufpos == 0)
3515                 return;
3516
3517         add_line_text(view, buf, LINE_PP_REFS);
3518 }
3519
3520 static bool
3521 pager_read(struct view *view, char *data)
3522 {
3523         struct line *line;
3524
3525         if (!data)
3526                 return TRUE;
3527
3528         line = add_line_text(view, data, get_line_type(data));
3529         if (!line)
3530                 return FALSE;
3531
3532         if (line->type == LINE_COMMIT &&
3533             (view == VIEW(REQ_VIEW_DIFF) ||
3534              view == VIEW(REQ_VIEW_LOG)))
3535                 add_pager_refs(view, line);
3536
3537         return TRUE;
3538 }
3539
3540 static enum request
3541 pager_request(struct view *view, enum request request, struct line *line)
3542 {
3543         int split = 0;
3544
3545         if (request != REQ_ENTER)
3546                 return request;
3547
3548         if (line->type == LINE_COMMIT &&
3549            (view == VIEW(REQ_VIEW_LOG) ||
3550             view == VIEW(REQ_VIEW_PAGER))) {
3551                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
3552                 split = 1;
3553         }
3554
3555         /* Always scroll the view even if it was split. That way
3556          * you can use Enter to scroll through the log view and
3557          * split open each commit diff. */
3558         scroll_view(view, REQ_SCROLL_LINE_DOWN);
3559
3560         /* FIXME: A minor workaround. Scrolling the view will call report("")
3561          * but if we are scrolling a non-current view this won't properly
3562          * update the view title. */
3563         if (split)
3564                 update_view_title(view);
3565
3566         return REQ_NONE;
3567 }
3568
3569 static bool
3570 pager_grep(struct view *view, struct line *line)
3571 {
3572         regmatch_t pmatch;
3573         char *text = line->data;
3574
3575         if (!*text)
3576                 return FALSE;
3577
3578         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
3579                 return FALSE;
3580
3581         return TRUE;
3582 }
3583
3584 static void
3585 pager_select(struct view *view, struct line *line)
3586 {
3587         if (line->type == LINE_COMMIT) {
3588                 char *text = (char *)line->data + STRING_SIZE("commit ");
3589
3590                 if (view != VIEW(REQ_VIEW_PAGER))
3591                         string_copy_rev(view->ref, text);
3592                 string_copy_rev(ref_commit, text);
3593         }
3594 }
3595
3596 static struct view_ops pager_ops = {
3597         "line",
3598         NULL,
3599         NULL,
3600         pager_read,
3601         pager_draw,
3602         pager_request,
3603         pager_grep,
3604         pager_select,
3605 };
3606
3607 static const char *log_argv[SIZEOF_ARG] = {
3608         "git", "log", "--no-color", "--cc", "--stat", "-n100", "%(head)", NULL
3609 };
3610
3611 static enum request
3612 log_request(struct view *view, enum request request, struct line *line)
3613 {
3614         switch (request) {
3615         case REQ_REFRESH:
3616                 load_refs();
3617                 open_view(view, REQ_VIEW_LOG, OPEN_REFRESH);
3618                 return REQ_NONE;
3619         default:
3620                 return pager_request(view, request, line);
3621         }
3622 }
3623
3624 static struct view_ops log_ops = {
3625         "line",
3626         log_argv,
3627         NULL,
3628         pager_read,
3629         pager_draw,
3630         log_request,
3631         pager_grep,
3632         pager_select,
3633 };
3634
3635 static const char *diff_argv[SIZEOF_ARG] = {
3636         "git", "show", "--pretty=fuller", "--no-color", "--root",
3637                 "--patch-with-stat", "--find-copies-harder", "-C", "%(commit)", NULL
3638 };
3639
3640 static struct view_ops diff_ops = {
3641         "line",
3642         diff_argv,
3643         NULL,
3644         pager_read,
3645         pager_draw,
3646         pager_request,
3647         pager_grep,
3648         pager_select,
3649 };
3650
3651 /*
3652  * Help backend
3653  */
3654
3655 static bool
3656 help_open(struct view *view)
3657 {
3658         char buf[SIZEOF_STR];
3659         size_t bufpos;
3660         int i;
3661
3662         if (view->lines > 0)
3663                 return TRUE;
3664
3665         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
3666
3667         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
3668                 const char *key;
3669
3670                 if (req_info[i].request == REQ_NONE)
3671                         continue;
3672
3673                 if (!req_info[i].request) {
3674                         add_line_text(view, "", LINE_DEFAULT);
3675                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
3676                         continue;
3677                 }
3678
3679                 key = get_key(req_info[i].request);
3680                 if (!*key)
3681                         key = "(no key defined)";
3682
3683                 for (bufpos = 0; bufpos <= req_info[i].namelen; bufpos++) {
3684                         buf[bufpos] = tolower(req_info[i].name[bufpos]);
3685                         if (buf[bufpos] == '_')
3686                                 buf[bufpos] = '-';
3687                 }
3688
3689                 add_line_format(view, LINE_DEFAULT, "    %-25s %-20s %s",
3690                                 key, buf, req_info[i].help);
3691         }
3692
3693         if (run_requests) {
3694                 add_line_text(view, "", LINE_DEFAULT);
3695                 add_line_text(view, "External commands:", LINE_DEFAULT);
3696         }
3697
3698         for (i = 0; i < run_requests; i++) {
3699                 struct run_request *req = get_run_request(REQ_NONE + i + 1);
3700                 const char *key;
3701                 int argc;
3702
3703                 if (!req)
3704                         continue;
3705
3706                 key = get_key_name(req->key);
3707                 if (!*key)
3708                         key = "(no key defined)";
3709
3710                 for (bufpos = 0, argc = 0; req->argv[argc]; argc++)
3711                         if (!string_format_from(buf, &bufpos, "%s%s",
3712                                                 argc ? " " : "", req->argv[argc]))
3713                                 return REQ_NONE;
3714
3715                 add_line_format(view, LINE_DEFAULT, "    %-10s %-14s `%s`",
3716                                 keymap_table[req->keymap].name, key, buf);
3717         }
3718
3719         return TRUE;
3720 }
3721
3722 static struct view_ops help_ops = {
3723         "line",
3724         NULL,
3725         help_open,
3726         NULL,
3727         pager_draw,
3728         pager_request,
3729         pager_grep,
3730         pager_select,
3731 };
3732
3733
3734 /*
3735  * Tree backend
3736  */
3737
3738 struct tree_stack_entry {
3739         struct tree_stack_entry *prev;  /* Entry below this in the stack */
3740         unsigned long lineno;           /* Line number to restore */
3741         char *name;                     /* Position of name in opt_path */
3742 };
3743
3744 /* The top of the path stack. */
3745 static struct tree_stack_entry *tree_stack = NULL;
3746 unsigned long tree_lineno = 0;
3747
3748 static void
3749 pop_tree_stack_entry(void)
3750 {
3751         struct tree_stack_entry *entry = tree_stack;
3752
3753         tree_lineno = entry->lineno;
3754         entry->name[0] = 0;
3755         tree_stack = entry->prev;
3756         free(entry);
3757 }
3758
3759 static void
3760 push_tree_stack_entry(const char *name, unsigned long lineno)
3761 {
3762         struct tree_stack_entry *entry = calloc(1, sizeof(*entry));
3763         size_t pathlen = strlen(opt_path);
3764
3765         if (!entry)
3766                 return;
3767
3768         entry->prev = tree_stack;
3769         entry->name = opt_path + pathlen;
3770         tree_stack = entry;
3771
3772         if (!string_format_from(opt_path, &pathlen, "%s/", name)) {
3773                 pop_tree_stack_entry();
3774                 return;
3775         }
3776
3777         /* Move the current line to the first tree entry. */
3778         tree_lineno = 1;
3779         entry->lineno = lineno;
3780 }
3781
3782 /* Parse output from git-ls-tree(1):
3783  *
3784  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
3785  */
3786
3787 #define SIZEOF_TREE_ATTR \
3788         STRING_SIZE("100644 blob f931e1d229c3e185caad4449bf5b66ed72462657\t")
3789
3790 #define SIZEOF_TREE_MODE \
3791         STRING_SIZE("100644 ")
3792
3793 #define TREE_ID_OFFSET \
3794         STRING_SIZE("100644 blob ")
3795
3796 struct tree_entry {
3797         char id[SIZEOF_REV];
3798         mode_t mode;
3799         struct tm time;                 /* Date from the author ident. */
3800         char author[75];                /* Author of the commit. */
3801         char name[1];
3802 };
3803
3804 static const char *
3805 tree_path(struct line *line)
3806 {
3807         return ((struct tree_entry *) line->data)->name;
3808 }
3809
3810
3811 static int
3812 tree_compare_entry(struct line *line1, struct line *line2)
3813 {
3814         if (line1->type != line2->type)
3815                 return line1->type == LINE_TREE_DIR ? -1 : 1;
3816         return strcmp(tree_path(line1), tree_path(line2));
3817 }
3818
3819 static struct line *
3820 tree_entry(struct view *view, enum line_type type, const char *path,
3821            const char *mode, const char *id)
3822 {
3823         struct tree_entry *entry = calloc(1, sizeof(*entry) + strlen(path));
3824         struct line *line = entry ? add_line_data(view, entry, type) : NULL;
3825
3826         if (!entry || !line) {
3827                 free(entry);
3828                 return NULL;
3829         }
3830
3831         strncpy(entry->name, path, strlen(path));
3832         if (mode)
3833                 entry->mode = strtoul(mode, NULL, 8);
3834         if (id)
3835                 string_copy_rev(entry->id, id);
3836
3837         return line;
3838 }
3839
3840 static bool
3841 tree_read_date(struct view *view, char *text, bool *read_date)
3842 {
3843         static char author_name[SIZEOF_STR];
3844         static struct tm author_time;
3845
3846         if (!text && *read_date) {
3847                 *read_date = FALSE;
3848                 return TRUE;
3849
3850         } else if (!text) {
3851                 char *path = *opt_path ? opt_path : ".";
3852                 /* Find next entry to process */
3853                 const char *log_file[] = {
3854                         "git", "log", "--no-color", "--pretty=raw",
3855                                 "--cc", "--raw", view->id, "--", path, NULL
3856                 };
3857                 struct io io = {};
3858
3859                 if (!view->lines) {
3860                         tree_entry(view, LINE_TREE_HEAD, opt_path, NULL, NULL);
3861                         report("Tree is empty");
3862                         return TRUE;
3863                 }
3864
3865                 if (!run_io_rd(&io, log_file, FORMAT_NONE)) {
3866                         report("Failed to load tree data");
3867                         return TRUE;
3868                 }
3869
3870                 done_io(view->pipe);
3871                 view->io = io;
3872                 *read_date = TRUE;
3873                 return FALSE;
3874
3875         } else if (*text == 'a' && get_line_type(text) == LINE_AUTHOR) {
3876                 parse_author_line(text + STRING_SIZE("author "),
3877                                   author_name, sizeof(author_name), &author_time);
3878
3879         } else if (*text == ':') {
3880                 char *pos;
3881                 size_t annotated = 1;
3882                 size_t i;
3883
3884                 pos = strchr(text, '\t');
3885                 if (!pos)
3886                         return TRUE;
3887                 text = pos + 1;
3888                 if (*opt_prefix && !strncmp(text, opt_prefix, strlen(opt_prefix)))
3889                         text += strlen(opt_prefix);
3890                 if (*opt_path && !strncmp(text, opt_path, strlen(opt_path)))
3891                         text += strlen(opt_path);
3892                 pos = strchr(text, '/');
3893                 if (pos)
3894                         *pos = 0;
3895
3896                 for (i = 1; i < view->lines; i++) {
3897                         struct line *line = &view->line[i];
3898                         struct tree_entry *entry = line->data;
3899
3900                         annotated += !!*entry->author;
3901                         if (*entry->author || strcmp(entry->name, text))
3902                                 continue;
3903
3904                         string_copy(entry->author, author_name);
3905                         memcpy(&entry->time, &author_time, sizeof(entry->time));
3906                         line->dirty = 1;
3907                         break;
3908                 }
3909
3910                 if (annotated == view->lines)
3911                         kill_io(view->pipe);
3912         }
3913         return TRUE;
3914 }
3915
3916 static bool
3917 tree_read(struct view *view, char *text)
3918 {
3919         static bool read_date = FALSE;
3920         struct tree_entry *data;
3921         struct line *entry, *line;
3922         enum line_type type;
3923         size_t textlen = text ? strlen(text) : 0;
3924         char *path = text + SIZEOF_TREE_ATTR;
3925
3926         if (read_date || !text)
3927                 return tree_read_date(view, text, &read_date);
3928
3929         if (textlen <= SIZEOF_TREE_ATTR)
3930                 return FALSE;
3931         if (view->lines == 0 &&
3932             !tree_entry(view, LINE_TREE_HEAD, opt_path, NULL, NULL))
3933                 return FALSE;
3934
3935         /* Strip the path part ... */
3936         if (*opt_path) {
3937                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
3938                 size_t striplen = strlen(opt_path);
3939
3940                 if (pathlen > striplen)
3941                         memmove(path, path + striplen,
3942                                 pathlen - striplen + 1);
3943
3944                 /* Insert "link" to parent directory. */
3945                 if (view->lines == 1 &&
3946                     !tree_entry(view, LINE_TREE_DIR, "..", "040000", view->ref))
3947                         return FALSE;
3948         }
3949
3950         type = text[SIZEOF_TREE_MODE] == 't' ? LINE_TREE_DIR : LINE_TREE_FILE;
3951         entry = tree_entry(view, type, path, text, text + TREE_ID_OFFSET);
3952         if (!entry)
3953                 return FALSE;
3954         data = entry->data;
3955
3956         /* Skip "Directory ..." and ".." line. */
3957         for (line = &view->line[1 + !!*opt_path]; line < entry; line++) {
3958                 if (tree_compare_entry(line, entry) <= 0)
3959                         continue;
3960
3961                 memmove(line + 1, line, (entry - line) * sizeof(*entry));
3962
3963                 line->data = data;
3964                 line->type = type;
3965                 for (; line <= entry; line++)
3966                         line->dirty = line->cleareol = 1;
3967                 return TRUE;
3968         }
3969
3970         if (tree_lineno > view->lineno) {
3971                 view->lineno = tree_lineno;
3972                 tree_lineno = 0;
3973         }
3974
3975         return TRUE;
3976 }
3977
3978 static bool
3979 tree_draw(struct view *view, struct line *line, unsigned int lineno)
3980 {
3981         struct tree_entry *entry = line->data;
3982
3983         if (line->type == LINE_TREE_HEAD) {
3984                 if (draw_text(view, line->type, "Directory path /", TRUE))
3985                         return TRUE;
3986         } else {
3987                 if (draw_mode(view, entry->mode))
3988                         return TRUE;
3989
3990                 if (opt_author && draw_author(view, entry->author))
3991                         return TRUE;
3992
3993                 if (opt_date && draw_date(view, *entry->author ? &entry->time : NULL))
3994                         return TRUE;
3995         }
3996         if (draw_text(view, line->type, entry->name, TRUE))
3997                 return TRUE;
3998         return TRUE;
3999 }
4000
4001 static void
4002 open_blob_editor()
4003 {
4004         char file[SIZEOF_STR] = "/tmp/tigblob.XXXXXX";
4005         int fd = mkstemp(file);
4006
4007         if (fd == -1)
4008                 report("Failed to create temporary file");
4009         else if (!run_io_append(blob_ops.argv, FORMAT_ALL, fd))
4010                 report("Failed to save blob data to file");
4011         else
4012                 open_editor(FALSE, file);
4013         if (fd != -1)
4014                 unlink(file);
4015 }
4016
4017 static enum request
4018 tree_request(struct view *view, enum request request, struct line *line)
4019 {
4020         enum open_flags flags;
4021
4022         switch (request) {
4023         case REQ_VIEW_BLAME:
4024                 if (line->type != LINE_TREE_FILE) {
4025                         report("Blame only supported for files");
4026                         return REQ_NONE;
4027                 }
4028
4029                 string_copy(opt_ref, view->vid);
4030                 return request;
4031
4032         case REQ_EDIT:
4033                 if (line->type != LINE_TREE_FILE) {
4034                         report("Edit only supported for files");
4035                 } else if (!is_head_commit(view->vid)) {
4036                         open_blob_editor();
4037                 } else {
4038                         open_editor(TRUE, opt_file);
4039                 }
4040                 return REQ_NONE;
4041
4042         case REQ_PARENT:
4043                 if (!*opt_path) {
4044                         /* quit view if at top of tree */
4045                         return REQ_VIEW_CLOSE;
4046                 }
4047                 /* fake 'cd  ..' */
4048                 line = &view->line[1];
4049                 break;
4050
4051         case REQ_ENTER:
4052                 break;
4053
4054         default:
4055                 return request;
4056         }
4057
4058         /* Cleanup the stack if the tree view is at a different tree. */
4059         while (!*opt_path && tree_stack)
4060                 pop_tree_stack_entry();
4061
4062         switch (line->type) {
4063         case LINE_TREE_DIR:
4064                 /* Depending on whether it is a subdirectory or parent link
4065                  * mangle the path buffer. */
4066                 if (line == &view->line[1] && *opt_path) {
4067                         pop_tree_stack_entry();
4068
4069                 } else {
4070                         const char *basename = tree_path(line);
4071
4072                         push_tree_stack_entry(basename, view->lineno);
4073                 }
4074
4075                 /* Trees and subtrees share the same ID, so they are not not
4076                  * unique like blobs. */
4077                 flags = OPEN_RELOAD;
4078                 request = REQ_VIEW_TREE;
4079                 break;
4080
4081         case LINE_TREE_FILE:
4082                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4083                 request = REQ_VIEW_BLOB;
4084                 break;
4085
4086         default:
4087                 return REQ_NONE;
4088         }
4089
4090         open_view(view, request, flags);
4091         if (request == REQ_VIEW_TREE)
4092                 view->lineno = tree_lineno;
4093
4094         return REQ_NONE;
4095 }
4096
4097 static void
4098 tree_select(struct view *view, struct line *line)
4099 {
4100         struct tree_entry *entry = line->data;
4101
4102         if (line->type == LINE_TREE_FILE) {
4103                 string_copy_rev(ref_blob, entry->id);
4104                 string_format(opt_file, "%s%s", opt_path, tree_path(line));
4105
4106         } else if (line->type != LINE_TREE_DIR) {
4107                 return;
4108         }
4109
4110         string_copy_rev(view->ref, entry->id);
4111 }
4112
4113 static const char *tree_argv[SIZEOF_ARG] = {
4114         "git", "ls-tree", "%(commit)", "%(directory)", NULL
4115 };
4116
4117 static struct view_ops tree_ops = {
4118         "file",
4119         tree_argv,
4120         NULL,
4121         tree_read,
4122         tree_draw,
4123         tree_request,
4124         pager_grep,
4125         tree_select,
4126 };
4127
4128 static bool
4129 blob_read(struct view *view, char *line)
4130 {
4131         if (!line)
4132                 return TRUE;
4133         return add_line_text(view, line, LINE_DEFAULT) != NULL;
4134 }
4135
4136 static enum request
4137 blob_request(struct view *view, enum request request, struct line *line)
4138 {
4139         switch (request) {
4140         case REQ_EDIT:
4141                 open_blob_editor();
4142                 return REQ_NONE;
4143         default:
4144                 return pager_request(view, request, line);
4145         }
4146 }
4147
4148 static const char *blob_argv[SIZEOF_ARG] = {
4149         "git", "cat-file", "blob", "%(blob)", NULL
4150 };
4151
4152 static struct view_ops blob_ops = {
4153         "line",
4154         blob_argv,
4155         NULL,
4156         blob_read,
4157         pager_draw,
4158         blob_request,
4159         pager_grep,
4160         pager_select,
4161 };
4162
4163 /*
4164  * Blame backend
4165  *
4166  * Loading the blame view is a two phase job:
4167  *
4168  *  1. File content is read either using opt_file from the
4169  *     filesystem or using git-cat-file.
4170  *  2. Then blame information is incrementally added by
4171  *     reading output from git-blame.
4172  */
4173
4174 static const char *blame_head_argv[] = {
4175         "git", "blame", "--incremental", "--", "%(file)", NULL
4176 };
4177
4178 static const char *blame_ref_argv[] = {
4179         "git", "blame", "--incremental", "%(ref)", "--", "%(file)", NULL
4180 };
4181
4182 static const char *blame_cat_file_argv[] = {
4183         "git", "cat-file", "blob", "%(ref):%(file)", NULL
4184 };
4185
4186 struct blame_commit {
4187         char id[SIZEOF_REV];            /* SHA1 ID. */
4188         char title[128];                /* First line of the commit message. */
4189         char author[75];                /* Author of the commit. */
4190         struct tm time;                 /* Date from the author ident. */
4191         char filename[128];             /* Name of file. */
4192         bool has_previous;              /* Was a "previous" line detected. */
4193 };
4194
4195 struct blame {
4196         struct blame_commit *commit;
4197         char text[1];
4198 };
4199
4200 static bool
4201 blame_open(struct view *view)
4202 {
4203         if (*opt_ref || !io_open(&view->io, opt_file)) {
4204                 if (!run_io_rd(&view->io, blame_cat_file_argv, FORMAT_ALL))
4205                         return FALSE;
4206         }
4207
4208         setup_update(view, opt_file);
4209         string_format(view->ref, "%s ...", opt_file);
4210
4211         return TRUE;
4212 }
4213
4214 static struct blame_commit *
4215 get_blame_commit(struct view *view, const char *id)
4216 {
4217         size_t i;
4218
4219         for (i = 0; i < view->lines; i++) {
4220                 struct blame *blame = view->line[i].data;
4221
4222                 if (!blame->commit)
4223                         continue;
4224
4225                 if (!strncmp(blame->commit->id, id, SIZEOF_REV - 1))
4226                         return blame->commit;
4227         }
4228
4229         {
4230                 struct blame_commit *commit = calloc(1, sizeof(*commit));
4231
4232                 if (commit)
4233                         string_ncopy(commit->id, id, SIZEOF_REV);
4234                 return commit;
4235         }
4236 }
4237
4238 static bool
4239 parse_number(const char **posref, size_t *number, size_t min, size_t max)
4240 {
4241         const char *pos = *posref;
4242
4243         *posref = NULL;
4244         pos = strchr(pos + 1, ' ');
4245         if (!pos || !isdigit(pos[1]))
4246                 return FALSE;
4247         *number = atoi(pos + 1);
4248         if (*number < min || *number > max)
4249                 return FALSE;
4250
4251         *posref = pos;
4252         return TRUE;
4253 }
4254
4255 static struct blame_commit *
4256 parse_blame_commit(struct view *view, const char *text, int *blamed)
4257 {
4258         struct blame_commit *commit;
4259         struct blame *blame;
4260         const char *pos = text + SIZEOF_REV - 1;
4261         size_t lineno;
4262         size_t group;
4263
4264         if (strlen(text) <= SIZEOF_REV || *pos != ' ')
4265                 return NULL;
4266
4267         if (!parse_number(&pos, &lineno, 1, view->lines) ||
4268             !parse_number(&pos, &group, 1, view->lines - lineno + 1))
4269                 return NULL;
4270
4271         commit = get_blame_commit(view, text);
4272         if (!commit)
4273                 return NULL;
4274
4275         *blamed += group;
4276         while (group--) {
4277                 struct line *line = &view->line[lineno + group - 1];
4278
4279                 blame = line->data;
4280                 blame->commit = commit;
4281                 line->dirty = 1;
4282         }
4283
4284         return commit;
4285 }
4286
4287 static bool
4288 blame_read_file(struct view *view, const char *line, bool *read_file)
4289 {
4290         if (!line) {
4291                 const char **argv = *opt_ref ? blame_ref_argv : blame_head_argv;
4292                 struct io io = {};
4293
4294                 if (view->lines == 0 && !view->parent)
4295                         die("No blame exist for %s", view->vid);
4296
4297                 if (view->lines == 0 || !run_io_rd(&io, argv, FORMAT_ALL)) {
4298                         report("Failed to load blame data");
4299                         return TRUE;
4300                 }
4301
4302                 done_io(view->pipe);
4303                 view->io = io;
4304                 *read_file = FALSE;
4305                 return FALSE;
4306
4307         } else {
4308                 size_t linelen = string_expand_length(line, opt_tab_size);
4309                 struct blame *blame = malloc(sizeof(*blame) + linelen);
4310
4311                 if (!blame)
4312                         return FALSE;
4313
4314                 blame->commit = NULL;
4315                 string_expand(blame->text, linelen + 1, line, opt_tab_size);
4316                 return add_line_data(view, blame, LINE_BLAME_ID) != NULL;
4317         }
4318 }
4319
4320 static bool
4321 match_blame_header(const char *name, char **line)
4322 {
4323         size_t namelen = strlen(name);
4324         bool matched = !strncmp(name, *line, namelen);
4325
4326         if (matched)
4327                 *line += namelen;
4328
4329         return matched;
4330 }
4331
4332 static bool
4333 blame_read(struct view *view, char *line)
4334 {
4335         static struct blame_commit *commit = NULL;
4336         static int blamed = 0;
4337         static time_t author_time;
4338         static bool read_file = TRUE;
4339
4340         if (read_file)
4341                 return blame_read_file(view, line, &read_file);
4342
4343         if (!line) {
4344                 /* Reset all! */
4345                 commit = NULL;
4346                 blamed = 0;
4347                 read_file = TRUE;
4348                 string_format(view->ref, "%s", view->vid);
4349                 if (view_is_displayed(view)) {
4350                         update_view_title(view);
4351                         redraw_view_from(view, 0);
4352                 }
4353                 return TRUE;
4354         }
4355
4356         if (!commit) {
4357                 commit = parse_blame_commit(view, line, &blamed);
4358                 string_format(view->ref, "%s %2d%%", view->vid,
4359                               view->lines ? blamed * 100 / view->lines : 0);
4360
4361         } else if (match_blame_header("author ", &line)) {
4362                 string_ncopy(commit->author, line, strlen(line));
4363
4364         } else if (match_blame_header("author-time ", &line)) {
4365                 author_time = (time_t) atol(line);
4366
4367         } else if (match_blame_header("author-tz ", &line)) {
4368                 long tz;
4369
4370                 tz  = ('0' - line[1]) * 60 * 60 * 10;
4371                 tz += ('0' - line[2]) * 60 * 60;
4372                 tz += ('0' - line[3]) * 60;
4373                 tz += ('0' - line[4]) * 60;
4374
4375                 if (line[0] == '-')
4376                         tz = -tz;
4377
4378                 author_time -= tz;
4379                 gmtime_r(&author_time, &commit->time);
4380
4381         } else if (match_blame_header("summary ", &line)) {
4382                 string_ncopy(commit->title, line, strlen(line));
4383
4384         } else if (match_blame_header("previous ", &line)) {
4385                 commit->has_previous = TRUE;
4386
4387         } else if (match_blame_header("filename ", &line)) {
4388                 string_ncopy(commit->filename, line, strlen(line));
4389                 commit = NULL;
4390         }
4391
4392         return TRUE;
4393 }
4394
4395 static bool
4396 blame_draw(struct view *view, struct line *line, unsigned int lineno)
4397 {
4398         struct blame *blame = line->data;
4399         struct tm *time = NULL;
4400         const char *id = NULL, *author = NULL;
4401
4402         if (blame->commit && *blame->commit->filename) {
4403                 id = blame->commit->id;
4404                 author = blame->commit->author;
4405                 time = &blame->commit->time;
4406         }
4407
4408         if (opt_date && draw_date(view, time))
4409                 return TRUE;
4410
4411         if (opt_author && draw_author(view, author))
4412                 return TRUE;
4413
4414         if (draw_field(view, LINE_BLAME_ID, id, ID_COLS, FALSE))
4415                 return TRUE;
4416
4417         if (draw_lineno(view, lineno))
4418                 return TRUE;
4419
4420         draw_text(view, LINE_DEFAULT, blame->text, TRUE);
4421         return TRUE;
4422 }
4423
4424 static bool
4425 check_blame_commit(struct blame *blame)
4426 {
4427         if (!blame->commit)
4428                 report("Commit data not loaded yet");
4429         else if (!strcmp(blame->commit->id, NULL_ID))
4430                 report("No commit exist for the selected line");
4431         else
4432                 return TRUE;
4433         return FALSE;
4434 }
4435
4436 static enum request
4437 blame_request(struct view *view, enum request request, struct line *line)
4438 {
4439         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4440         struct blame *blame = line->data;
4441
4442         switch (request) {
4443         case REQ_VIEW_BLAME:
4444                 if (check_blame_commit(blame)) {
4445                         string_copy(opt_ref, blame->commit->id);
4446                         open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
4447                 }
4448                 break;
4449
4450         case REQ_PARENT:
4451                 if (check_blame_commit(blame) &&
4452                     select_commit_parent(blame->commit->id, opt_ref))
4453                         open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH);
4454                 break;
4455
4456         case REQ_ENTER:
4457                 if (!blame->commit) {
4458                         report("No commit loaded yet");
4459                         break;
4460                 }
4461
4462                 if (view_is_displayed(VIEW(REQ_VIEW_DIFF)) &&
4463                     !strcmp(blame->commit->id, VIEW(REQ_VIEW_DIFF)->ref))
4464                         break;
4465
4466                 if (!strcmp(blame->commit->id, NULL_ID)) {
4467                         struct view *diff = VIEW(REQ_VIEW_DIFF);
4468                         const char *diff_index_argv[] = {
4469                                 "git", "diff-index", "--root", "--patch-with-stat",
4470                                         "-C", "-M", "HEAD", "--", view->vid, NULL
4471                         };
4472
4473                         if (!blame->commit->has_previous) {
4474                                 diff_index_argv[1] = "diff";
4475                                 diff_index_argv[2] = "--no-color";
4476                                 diff_index_argv[6] = "--";
4477                                 diff_index_argv[7] = "/dev/null";
4478                         }
4479
4480                         if (!prepare_update(diff, diff_index_argv, NULL, FORMAT_DASH)) {
4481                                 report("Failed to allocate diff command");
4482                                 break;
4483                         }
4484                         flags |= OPEN_PREPARED;
4485                 }
4486
4487                 open_view(view, REQ_VIEW_DIFF, flags);
4488                 if (VIEW(REQ_VIEW_DIFF)->pipe && !strcmp(blame->commit->id, NULL_ID))
4489                         string_copy_rev(VIEW(REQ_VIEW_DIFF)->ref, NULL_ID);
4490                 break;
4491
4492         default:
4493                 return request;
4494         }
4495
4496         return REQ_NONE;
4497 }
4498
4499 static bool
4500 blame_grep(struct view *view, struct line *line)
4501 {
4502         struct blame *blame = line->data;
4503         struct blame_commit *commit = blame->commit;
4504         regmatch_t pmatch;
4505
4506 #define MATCH(text, on)                                                 \
4507         (on && *text && regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4508
4509         if (commit) {
4510                 char buf[DATE_COLS + 1];
4511
4512                 if (MATCH(commit->title, 1) ||
4513                     MATCH(commit->author, opt_author) ||
4514                     MATCH(commit->id, opt_date))
4515                         return TRUE;
4516
4517                 if (strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time) &&
4518                     MATCH(buf, 1))
4519                         return TRUE;
4520         }
4521
4522         return MATCH(blame->text, 1);
4523
4524 #undef MATCH
4525 }
4526
4527 static void
4528 blame_select(struct view *view, struct line *line)
4529 {
4530         struct blame *blame = line->data;
4531         struct blame_commit *commit = blame->commit;
4532
4533         if (!commit)
4534                 return;
4535
4536         if (!strcmp(commit->id, NULL_ID))
4537                 string_ncopy(ref_commit, "HEAD", 4);
4538         else
4539                 string_copy_rev(ref_commit, commit->id);
4540 }
4541
4542 static struct view_ops blame_ops = {
4543         "line",
4544         NULL,
4545         blame_open,
4546         blame_read,
4547         blame_draw,
4548         blame_request,
4549         blame_grep,
4550         blame_select,
4551 };
4552
4553 /*
4554  * Status backend
4555  */
4556
4557 struct status {
4558         char status;
4559         struct {
4560                 mode_t mode;
4561                 char rev[SIZEOF_REV];
4562                 char name[SIZEOF_STR];
4563         } old;
4564         struct {
4565                 mode_t mode;
4566                 char rev[SIZEOF_REV];
4567                 char name[SIZEOF_STR];
4568         } new;
4569 };
4570
4571 static char status_onbranch[SIZEOF_STR];
4572 static struct status stage_status;
4573 static enum line_type stage_line_type;
4574 static size_t stage_chunks;
4575 static int *stage_chunk;
4576
4577 /* This should work even for the "On branch" line. */
4578 static inline bool
4579 status_has_none(struct view *view, struct line *line)
4580 {
4581         return line < view->line + view->lines && !line[1].data;
4582 }
4583
4584 /* Get fields from the diff line:
4585  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
4586  */
4587 static inline bool
4588 status_get_diff(struct status *file, const char *buf, size_t bufsize)
4589 {
4590         const char *old_mode = buf +  1;
4591         const char *new_mode = buf +  8;
4592         const char *old_rev  = buf + 15;
4593         const char *new_rev  = buf + 56;
4594         const char *status   = buf + 97;
4595
4596         if (bufsize < 98 ||
4597             old_mode[-1] != ':' ||
4598             new_mode[-1] != ' ' ||
4599             old_rev[-1]  != ' ' ||
4600             new_rev[-1]  != ' ' ||
4601             status[-1]   != ' ')
4602                 return FALSE;
4603
4604         file->status = *status;
4605
4606         string_copy_rev(file->old.rev, old_rev);
4607         string_copy_rev(file->new.rev, new_rev);
4608
4609         file->old.mode = strtoul(old_mode, NULL, 8);
4610         file->new.mode = strtoul(new_mode, NULL, 8);
4611
4612         file->old.name[0] = file->new.name[0] = 0;
4613
4614         return TRUE;
4615 }
4616
4617 static bool
4618 status_run(struct view *view, const char *argv[], char status, enum line_type type)
4619 {
4620         struct status *unmerged = NULL;
4621         char *buf;
4622         struct io io = {};
4623
4624         if (!run_io(&io, argv, NULL, IO_RD))
4625                 return FALSE;
4626
4627         add_line_data(view, NULL, type);
4628
4629         while ((buf = io_get(&io, 0, TRUE))) {
4630                 struct status *file = unmerged;
4631
4632                 if (!file) {
4633                         file = calloc(1, sizeof(*file));
4634                         if (!file || !add_line_data(view, file, type))
4635                                 goto error_out;
4636                 }
4637
4638                 /* Parse diff info part. */
4639                 if (status) {
4640                         file->status = status;
4641                         if (status == 'A')
4642                                 string_copy(file->old.rev, NULL_ID);
4643
4644                 } else if (!file->status || file == unmerged) {
4645                         if (!status_get_diff(file, buf, strlen(buf)))
4646                                 goto error_out;
4647
4648                         buf = io_get(&io, 0, TRUE);
4649                         if (!buf)
4650                                 break;
4651
4652                         /* Collapse all modified entries that follow an
4653                          * associated unmerged entry. */
4654                         if (unmerged == file) {
4655                                 unmerged->status = 'U';
4656                                 unmerged = NULL;
4657                         } else if (file->status == 'U') {
4658                                 unmerged = file;
4659                         }
4660                 }
4661
4662                 /* Grab the old name for rename/copy. */
4663                 if (!*file->old.name &&
4664                     (file->status == 'R' || file->status == 'C')) {
4665                         string_ncopy(file->old.name, buf, strlen(buf));
4666
4667                         buf = io_get(&io, 0, TRUE);
4668                         if (!buf)
4669                                 break;
4670                 }
4671
4672                 /* git-ls-files just delivers a NUL separated list of
4673                  * file names similar to the second half of the
4674                  * git-diff-* output. */
4675                 string_ncopy(file->new.name, buf, strlen(buf));
4676                 if (!*file->old.name)
4677                         string_copy(file->old.name, file->new.name);
4678                 file = NULL;
4679         }
4680
4681         if (io_error(&io)) {
4682 error_out:
4683                 done_io(&io);
4684                 return FALSE;
4685         }
4686
4687         if (!view->line[view->lines - 1].data)
4688                 add_line_data(view, NULL, LINE_STAT_NONE);
4689
4690         done_io(&io);
4691         return TRUE;
4692 }
4693
4694 /* Don't show unmerged entries in the staged section. */
4695 static const char *status_diff_index_argv[] = {
4696         "git", "diff-index", "-z", "--diff-filter=ACDMRTXB",
4697                              "--cached", "-M", "HEAD", NULL
4698 };
4699
4700 static const char *status_diff_files_argv[] = {
4701         "git", "diff-files", "-z", NULL
4702 };
4703
4704 static const char *status_list_other_argv[] = {
4705         "git", "ls-files", "-z", "--others", "--exclude-standard", NULL
4706 };
4707
4708 static const char *status_list_no_head_argv[] = {
4709         "git", "ls-files", "-z", "--cached", "--exclude-standard", NULL
4710 };
4711
4712 static const char *update_index_argv[] = {
4713         "git", "update-index", "-q", "--unmerged", "--refresh", NULL
4714 };
4715
4716 /* Restore the previous line number to stay in the context or select a
4717  * line with something that can be updated. */
4718 static void
4719 status_restore(struct view *view)
4720 {
4721         if (view->p_lineno >= view->lines)
4722                 view->p_lineno = view->lines - 1;
4723         while (view->p_lineno < view->lines && !view->line[view->p_lineno].data)
4724                 view->p_lineno++;
4725         while (view->p_lineno > 0 && !view->line[view->p_lineno].data)
4726                 view->p_lineno--;
4727
4728         /* If the above fails, always skip the "On branch" line. */
4729         if (view->p_lineno < view->lines)
4730                 view->lineno = view->p_lineno;
4731         else
4732                 view->lineno = 1;
4733
4734         if (view->lineno < view->offset)
4735                 view->offset = view->lineno;
4736         else if (view->offset + view->height <= view->lineno)
4737                 view->offset = view->lineno - view->height + 1;
4738
4739         view->p_restore = FALSE;
4740 }
4741
4742 /* First parse staged info using git-diff-index(1), then parse unstaged
4743  * info using git-diff-files(1), and finally untracked files using
4744  * git-ls-files(1). */
4745 static bool
4746 status_open(struct view *view)
4747 {
4748         reset_view(view);
4749
4750         add_line_data(view, NULL, LINE_STAT_HEAD);
4751         if (is_initial_commit())
4752                 string_copy(status_onbranch, "Initial commit");
4753         else if (!*opt_head)
4754                 string_copy(status_onbranch, "Not currently on any branch");
4755         else if (!string_format(status_onbranch, "On branch %s", opt_head))
4756                 return FALSE;
4757
4758         run_io_bg(update_index_argv);
4759
4760         if (is_initial_commit()) {
4761                 if (!status_run(view, status_list_no_head_argv, 'A', LINE_STAT_STAGED))
4762                         return FALSE;
4763         } else if (!status_run(view, status_diff_index_argv, 0, LINE_STAT_STAGED)) {
4764                 return FALSE;
4765         }
4766
4767         if (!status_run(view, status_diff_files_argv, 0, LINE_STAT_UNSTAGED) ||
4768             !status_run(view, status_list_other_argv, '?', LINE_STAT_UNTRACKED))
4769                 return FALSE;
4770
4771         /* Restore the exact position or use the specialized restore
4772          * mode? */
4773         if (!view->p_restore)
4774                 status_restore(view);
4775         return TRUE;
4776 }
4777
4778 static bool
4779 status_draw(struct view *view, struct line *line, unsigned int lineno)
4780 {
4781         struct status *status = line->data;
4782         enum line_type type;
4783         const char *text;
4784
4785         if (!status) {
4786                 switch (line->type) {
4787                 case LINE_STAT_STAGED:
4788                         type = LINE_STAT_SECTION;
4789                         text = "Changes to be committed:";
4790                         break;
4791
4792                 case LINE_STAT_UNSTAGED:
4793                         type = LINE_STAT_SECTION;
4794                         text = "Changed but not updated:";
4795                         break;
4796
4797                 case LINE_STAT_UNTRACKED:
4798                         type = LINE_STAT_SECTION;
4799                         text = "Untracked files:";
4800                         break;
4801
4802                 case LINE_STAT_NONE:
4803                         type = LINE_DEFAULT;
4804                         text = "  (no files)";
4805                         break;
4806
4807                 case LINE_STAT_HEAD:
4808                         type = LINE_STAT_HEAD;
4809                         text = status_onbranch;
4810                         break;
4811
4812                 default:
4813                         return FALSE;
4814                 }
4815         } else {
4816                 static char buf[] = { '?', ' ', ' ', ' ', 0 };
4817
4818                 buf[0] = status->status;
4819                 if (draw_text(view, line->type, buf, TRUE))
4820                         return TRUE;
4821                 type = LINE_DEFAULT;
4822                 text = status->new.name;
4823         }
4824
4825         draw_text(view, type, text, TRUE);
4826         return TRUE;
4827 }
4828
4829 static enum request
4830 status_enter(struct view *view, struct line *line)
4831 {
4832         struct status *status = line->data;
4833         const char *oldpath = status ? status->old.name : NULL;
4834         /* Diffs for unmerged entries are empty when passing the new
4835          * path, so leave it empty. */
4836         const char *newpath = status && status->status != 'U' ? status->new.name : NULL;
4837         const char *info;
4838         enum open_flags split;
4839         struct view *stage = VIEW(REQ_VIEW_STAGE);
4840
4841         if (line->type == LINE_STAT_NONE ||
4842             (!status && line[1].type == LINE_STAT_NONE)) {
4843                 report("No file to diff");
4844                 return REQ_NONE;
4845         }
4846
4847         switch (line->type) {
4848         case LINE_STAT_STAGED:
4849                 if (is_initial_commit()) {
4850                         const char *no_head_diff_argv[] = {
4851                                 "git", "diff", "--no-color", "--patch-with-stat",
4852                                         "--", "/dev/null", newpath, NULL
4853                         };
4854
4855                         if (!prepare_update(stage, no_head_diff_argv, opt_cdup, FORMAT_DASH))
4856                                 return REQ_QUIT;
4857                 } else {
4858                         const char *index_show_argv[] = {
4859                                 "git", "diff-index", "--root", "--patch-with-stat",
4860                                         "-C", "-M", "--cached", "HEAD", "--",
4861                                         oldpath, newpath, NULL
4862                         };
4863
4864                         if (!prepare_update(stage, index_show_argv, opt_cdup, FORMAT_DASH))
4865                                 return REQ_QUIT;
4866                 }
4867
4868                 if (status)
4869                         info = "Staged changes to %s";
4870                 else
4871                         info = "Staged changes";
4872                 break;
4873
4874         case LINE_STAT_UNSTAGED:
4875         {
4876                 const char *files_show_argv[] = {
4877                         "git", "diff-files", "--root", "--patch-with-stat",
4878                                 "-C", "-M", "--", oldpath, newpath, NULL
4879                 };
4880
4881                 if (!prepare_update(stage, files_show_argv, opt_cdup, FORMAT_DASH))
4882                         return REQ_QUIT;
4883                 if (status)
4884                         info = "Unstaged changes to %s";
4885                 else
4886                         info = "Unstaged changes";
4887                 break;
4888         }
4889         case LINE_STAT_UNTRACKED:
4890                 if (!newpath) {
4891                         report("No file to show");
4892                         return REQ_NONE;
4893                 }
4894
4895                 if (!suffixcmp(status->new.name, -1, "/")) {
4896                         report("Cannot display a directory");
4897                         return REQ_NONE;
4898                 }
4899
4900                 if (!prepare_update_file(stage, newpath))
4901                         return REQ_QUIT;
4902                 info = "Untracked file %s";
4903                 break;
4904
4905         case LINE_STAT_HEAD:
4906                 return REQ_NONE;
4907
4908         default:
4909                 die("line type %d not handled in switch", line->type);
4910         }
4911
4912         split = view_is_displayed(view) ? OPEN_SPLIT : 0;
4913         open_view(view, REQ_VIEW_STAGE, OPEN_PREPARED | split);
4914         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
4915                 if (status) {
4916                         stage_status = *status;
4917                 } else {
4918                         memset(&stage_status, 0, sizeof(stage_status));
4919                 }
4920
4921                 stage_line_type = line->type;
4922                 stage_chunks = 0;
4923                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
4924         }
4925
4926         return REQ_NONE;
4927 }
4928
4929 static bool
4930 status_exists(struct status *status, enum line_type type)
4931 {
4932         struct view *view = VIEW(REQ_VIEW_STATUS);
4933         unsigned long lineno;
4934
4935         for (lineno = 0; lineno < view->lines; lineno++) {
4936                 struct line *line = &view->line[lineno];
4937                 struct status *pos = line->data;
4938
4939                 if (line->type != type)
4940                         continue;
4941                 if (!pos && (!status || !status->status) && line[1].data) {
4942                         select_view_line(view, lineno);
4943                         return TRUE;
4944                 }
4945                 if (pos && !strcmp(status->new.name, pos->new.name)) {
4946                         select_view_line(view, lineno);
4947                         return TRUE;
4948                 }
4949         }
4950
4951         return FALSE;
4952 }
4953
4954
4955 static bool
4956 status_update_prepare(struct io *io, enum line_type type)
4957 {
4958         const char *staged_argv[] = {
4959                 "git", "update-index", "-z", "--index-info", NULL
4960         };
4961         const char *others_argv[] = {
4962                 "git", "update-index", "-z", "--add", "--remove", "--stdin", NULL
4963         };
4964
4965         switch (type) {
4966         case LINE_STAT_STAGED:
4967                 return run_io(io, staged_argv, opt_cdup, IO_WR);
4968
4969         case LINE_STAT_UNSTAGED:
4970                 return run_io(io, others_argv, opt_cdup, IO_WR);
4971
4972         case LINE_STAT_UNTRACKED:
4973                 return run_io(io, others_argv, NULL, IO_WR);
4974
4975         default:
4976                 die("line type %d not handled in switch", type);
4977                 return FALSE;
4978         }
4979 }
4980
4981 static bool
4982 status_update_write(struct io *io, struct status *status, enum line_type type)
4983 {
4984         char buf[SIZEOF_STR];
4985         size_t bufsize = 0;
4986
4987         switch (type) {
4988         case LINE_STAT_STAGED:
4989                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
4990                                         status->old.mode,
4991                                         status->old.rev,
4992                                         status->old.name, 0))
4993                         return FALSE;
4994                 break;
4995
4996         case LINE_STAT_UNSTAGED:
4997         case LINE_STAT_UNTRACKED:
4998                 if (!string_format_from(buf, &bufsize, "%s%c", status->new.name, 0))
4999                         return FALSE;
5000                 break;
5001
5002         default:
5003                 die("line type %d not handled in switch", type);
5004         }
5005
5006         return io_write(io, buf, bufsize);
5007 }
5008
5009 static bool
5010 status_update_file(struct status *status, enum line_type type)
5011 {
5012         struct io io = {};
5013         bool result;
5014
5015         if (!status_update_prepare(&io, type))
5016                 return FALSE;
5017
5018         result = status_update_write(&io, status, type);
5019         done_io(&io);
5020         return result;
5021 }
5022
5023 static bool
5024 status_update_files(struct view *view, struct line *line)
5025 {
5026         struct io io = {};
5027         bool result = TRUE;
5028         struct line *pos = view->line + view->lines;
5029         int files = 0;
5030         int file, done;
5031
5032         if (!status_update_prepare(&io, line->type))
5033                 return FALSE;
5034
5035         for (pos = line; pos < view->line + view->lines && pos->data; pos++)
5036                 files++;
5037
5038         for (file = 0, done = 0; result && file < files; line++, file++) {
5039                 int almost_done = file * 100 / files;
5040
5041                 if (almost_done > done) {
5042                         done = almost_done;
5043                         string_format(view->ref, "updating file %u of %u (%d%% done)",
5044                                       file, files, done);
5045                         update_view_title(view);
5046                 }
5047                 result = status_update_write(&io, line->data, line->type);
5048         }
5049
5050         done_io(&io);
5051         return result;
5052 }
5053
5054 static bool
5055 status_update(struct view *view)
5056 {
5057         struct line *line = &view->line[view->lineno];
5058
5059         assert(view->lines);
5060
5061         if (!line->data) {
5062                 /* This should work even for the "On branch" line. */
5063                 if (line < view->line + view->lines && !line[1].data) {
5064                         report("Nothing to update");
5065                         return FALSE;
5066                 }
5067
5068                 if (!status_update_files(view, line + 1)) {
5069                         report("Failed to update file status");
5070                         return FALSE;
5071                 }
5072
5073         } else if (!status_update_file(line->data, line->type)) {
5074                 report("Failed to update file status");
5075                 return FALSE;
5076         }
5077
5078         return TRUE;
5079 }
5080
5081 static bool
5082 status_revert(struct status *status, enum line_type type, bool has_none)
5083 {
5084         if (!status || type != LINE_STAT_UNSTAGED) {
5085                 if (type == LINE_STAT_STAGED) {
5086                         report("Cannot revert changes to staged files");
5087                 } else if (type == LINE_STAT_UNTRACKED) {
5088                         report("Cannot revert changes to untracked files");
5089                 } else if (has_none) {
5090                         report("Nothing to revert");
5091                 } else {
5092                         report("Cannot revert changes to multiple files");
5093                 }
5094                 return FALSE;
5095
5096         } else {
5097                 char mode[10] = "100644";
5098                 const char *reset_argv[] = {
5099                         "git", "update-index", "--cacheinfo", mode,
5100                                 status->old.rev, status->old.name, NULL
5101                 };
5102                 const char *checkout_argv[] = {
5103                         "git", "checkout", "--", status->old.name, NULL
5104                 };
5105
5106                 if (!prompt_yesno("Are you sure you want to overwrite any changes?"))
5107                         return FALSE;
5108                 string_format(mode, "%o", status->old.mode);
5109                 return (status->status != 'U' || run_io_fg(reset_argv, opt_cdup)) &&
5110                         run_io_fg(checkout_argv, opt_cdup);
5111         }
5112 }
5113
5114 static enum request
5115 status_request(struct view *view, enum request request, struct line *line)
5116 {
5117         struct status *status = line->data;
5118
5119         switch (request) {
5120         case REQ_STATUS_UPDATE:
5121                 if (!status_update(view))
5122                         return REQ_NONE;
5123                 break;
5124
5125         case REQ_STATUS_REVERT:
5126                 if (!status_revert(status, line->type, status_has_none(view, line)))
5127                         return REQ_NONE;
5128                 break;
5129
5130         case REQ_STATUS_MERGE:
5131                 if (!status || status->status != 'U') {
5132                         report("Merging only possible for files with unmerged status ('U').");
5133                         return REQ_NONE;
5134                 }
5135                 open_mergetool(status->new.name);
5136                 break;
5137
5138         case REQ_EDIT:
5139                 if (!status)
5140                         return request;
5141                 if (status->status == 'D') {
5142                         report("File has been deleted.");
5143                         return REQ_NONE;
5144                 }
5145
5146                 open_editor(status->status != '?', status->new.name);
5147                 break;
5148
5149         case REQ_VIEW_BLAME:
5150                 if (status) {
5151                         string_copy(opt_file, status->new.name);
5152                         opt_ref[0] = 0;
5153                 }
5154                 return request;
5155
5156         case REQ_ENTER:
5157                 /* After returning the status view has been split to
5158                  * show the stage view. No further reloading is
5159                  * necessary. */
5160                 status_enter(view, line);
5161                 return REQ_NONE;
5162
5163         case REQ_REFRESH:
5164                 /* Simply reload the view. */
5165                 break;
5166
5167         default:
5168                 return request;
5169         }
5170
5171         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
5172
5173         return REQ_NONE;
5174 }
5175
5176 static void
5177 status_select(struct view *view, struct line *line)
5178 {
5179         struct status *status = line->data;
5180         char file[SIZEOF_STR] = "all files";
5181         const char *text;
5182         const char *key;
5183
5184         if (status && !string_format(file, "'%s'", status->new.name))
5185                 return;
5186
5187         if (!status && line[1].type == LINE_STAT_NONE)
5188                 line++;
5189
5190         switch (line->type) {
5191         case LINE_STAT_STAGED:
5192                 text = "Press %s to unstage %s for commit";
5193                 break;
5194
5195         case LINE_STAT_UNSTAGED:
5196                 text = "Press %s to stage %s for commit";
5197                 break;
5198
5199         case LINE_STAT_UNTRACKED:
5200                 text = "Press %s to stage %s for addition";
5201                 break;
5202
5203         case LINE_STAT_HEAD:
5204         case LINE_STAT_NONE:
5205                 text = "Nothing to update";
5206                 break;
5207
5208         default:
5209                 die("line type %d not handled in switch", line->type);
5210         }
5211
5212         if (status && status->status == 'U') {
5213                 text = "Press %s to resolve conflict in %s";
5214                 key = get_key(REQ_STATUS_MERGE);
5215
5216         } else {
5217                 key = get_key(REQ_STATUS_UPDATE);
5218         }
5219
5220         string_format(view->ref, text, key, file);
5221 }
5222
5223 static bool
5224 status_grep(struct view *view, struct line *line)
5225 {
5226         struct status *status = line->data;
5227         enum { S_STATUS, S_NAME, S_END } state;
5228         char buf[2] = "?";
5229         regmatch_t pmatch;
5230
5231         if (!status)
5232                 return FALSE;
5233
5234         for (state = S_STATUS; state < S_END; state++) {
5235                 const char *text;
5236
5237                 switch (state) {
5238                 case S_NAME:    text = status->new.name;        break;
5239                 case S_STATUS:
5240                         buf[0] = status->status;
5241                         text = buf;
5242                         break;
5243
5244                 default:
5245                         return FALSE;
5246                 }
5247
5248                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5249                         return TRUE;
5250         }
5251
5252         return FALSE;
5253 }
5254
5255 static struct view_ops status_ops = {
5256         "file",
5257         NULL,
5258         status_open,
5259         NULL,
5260         status_draw,
5261         status_request,
5262         status_grep,
5263         status_select,
5264 };
5265
5266
5267 static bool
5268 stage_diff_write(struct io *io, struct line *line, struct line *end)
5269 {
5270         while (line < end) {
5271                 if (!io_write(io, line->data, strlen(line->data)) ||
5272                     !io_write(io, "\n", 1))
5273                         return FALSE;
5274                 line++;
5275                 if (line->type == LINE_DIFF_CHUNK ||
5276                     line->type == LINE_DIFF_HEADER)
5277                         break;
5278         }
5279
5280         return TRUE;
5281 }
5282
5283 static struct line *
5284 stage_diff_find(struct view *view, struct line *line, enum line_type type)
5285 {
5286         for (; view->line < line; line--)
5287                 if (line->type == type)
5288                         return line;
5289
5290         return NULL;
5291 }
5292
5293 static bool
5294 stage_apply_chunk(struct view *view, struct line *chunk, bool revert)
5295 {
5296         const char *apply_argv[SIZEOF_ARG] = {
5297                 "git", "apply", "--whitespace=nowarn", NULL
5298         };
5299         struct line *diff_hdr;
5300         struct io io = {};
5301         int argc = 3;
5302
5303         diff_hdr = stage_diff_find(view, chunk, LINE_DIFF_HEADER);
5304         if (!diff_hdr)
5305                 return FALSE;
5306
5307         if (!revert)
5308                 apply_argv[argc++] = "--cached";
5309         if (revert || stage_line_type == LINE_STAT_STAGED)
5310                 apply_argv[argc++] = "-R";
5311         apply_argv[argc++] = "-";
5312         apply_argv[argc++] = NULL;
5313         if (!run_io(&io, apply_argv, opt_cdup, IO_WR))
5314                 return FALSE;
5315
5316         if (!stage_diff_write(&io, diff_hdr, chunk) ||
5317             !stage_diff_write(&io, chunk, view->line + view->lines))
5318                 chunk = NULL;
5319
5320         done_io(&io);
5321         run_io_bg(update_index_argv);
5322
5323         return chunk ? TRUE : FALSE;
5324 }
5325
5326 static bool
5327 stage_update(struct view *view, struct line *line)
5328 {
5329         struct line *chunk = NULL;
5330
5331         if (!is_initial_commit() && stage_line_type != LINE_STAT_UNTRACKED)
5332                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
5333
5334         if (chunk) {
5335                 if (!stage_apply_chunk(view, chunk, FALSE)) {
5336                         report("Failed to apply chunk");
5337                         return FALSE;
5338                 }
5339
5340         } else if (!stage_status.status) {
5341                 view = VIEW(REQ_VIEW_STATUS);
5342
5343                 for (line = view->line; line < view->line + view->lines; line++)
5344                         if (line->type == stage_line_type)
5345                                 break;
5346
5347                 if (!status_update_files(view, line + 1)) {
5348                         report("Failed to update files");
5349                         return FALSE;
5350                 }
5351
5352         } else if (!status_update_file(&stage_status, stage_line_type)) {
5353                 report("Failed to update file");
5354                 return FALSE;
5355         }
5356
5357         return TRUE;
5358 }
5359
5360 static bool
5361 stage_revert(struct view *view, struct line *line)
5362 {
5363         struct line *chunk = NULL;
5364
5365         if (!is_initial_commit() && stage_line_type == LINE_STAT_UNSTAGED)
5366                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
5367
5368         if (chunk) {
5369                 if (!prompt_yesno("Are you sure you want to revert changes?"))
5370                         return FALSE;
5371
5372                 if (!stage_apply_chunk(view, chunk, TRUE)) {
5373                         report("Failed to revert chunk");
5374                         return FALSE;
5375                 }
5376                 return TRUE;
5377
5378         } else {
5379                 return status_revert(stage_status.status ? &stage_status : NULL,
5380                                      stage_line_type, FALSE);
5381         }
5382 }
5383
5384
5385 static void
5386 stage_next(struct view *view, struct line *line)
5387 {
5388         int i;
5389
5390         if (!stage_chunks) {
5391                 static size_t alloc = 0;
5392                 int *tmp;
5393
5394                 for (line = view->line; line < view->line + view->lines; line++) {
5395                         if (line->type != LINE_DIFF_CHUNK)
5396                                 continue;
5397
5398                         tmp = realloc_items(stage_chunk, &alloc,
5399                                             stage_chunks, sizeof(*tmp));
5400                         if (!tmp) {
5401                                 report("Allocation failure");
5402                                 return;
5403                         }
5404
5405                         stage_chunk = tmp;
5406                         stage_chunk[stage_chunks++] = line - view->line;
5407                 }
5408         }
5409
5410         for (i = 0; i < stage_chunks; i++) {
5411                 if (stage_chunk[i] > view->lineno) {
5412                         do_scroll_view(view, stage_chunk[i] - view->lineno);
5413                         report("Chunk %d of %d", i + 1, stage_chunks);
5414                         return;
5415                 }
5416         }
5417
5418         report("No next chunk found");
5419 }
5420
5421 static enum request
5422 stage_request(struct view *view, enum request request, struct line *line)
5423 {
5424         switch (request) {
5425         case REQ_STATUS_UPDATE:
5426                 if (!stage_update(view, line))
5427                         return REQ_NONE;
5428                 break;
5429
5430         case REQ_STATUS_REVERT:
5431                 if (!stage_revert(view, line))
5432                         return REQ_NONE;
5433                 break;
5434
5435         case REQ_STAGE_NEXT:
5436                 if (stage_line_type == LINE_STAT_UNTRACKED) {
5437                         report("File is untracked; press %s to add",
5438                                get_key(REQ_STATUS_UPDATE));
5439                         return REQ_NONE;
5440                 }
5441                 stage_next(view, line);
5442                 return REQ_NONE;
5443
5444         case REQ_EDIT:
5445                 if (!stage_status.new.name[0])
5446                         return request;
5447                 if (stage_status.status == 'D') {
5448                         report("File has been deleted.");
5449                         return REQ_NONE;
5450                 }
5451
5452                 open_editor(stage_status.status != '?', stage_status.new.name);
5453                 break;
5454
5455         case REQ_REFRESH:
5456                 /* Reload everything ... */
5457                 break;
5458
5459         case REQ_VIEW_BLAME:
5460                 if (stage_status.new.name[0]) {
5461                         string_copy(opt_file, stage_status.new.name);
5462                         opt_ref[0] = 0;
5463                 }
5464                 return request;
5465
5466         case REQ_ENTER:
5467                 return pager_request(view, request, line);
5468
5469         default:
5470                 return request;
5471         }
5472
5473         VIEW(REQ_VIEW_STATUS)->p_restore = TRUE;
5474         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD | OPEN_NOMAXIMIZE);
5475
5476         /* Check whether the staged entry still exists, and close the
5477          * stage view if it doesn't. */
5478         if (!status_exists(&stage_status, stage_line_type)) {
5479                 status_restore(VIEW(REQ_VIEW_STATUS));
5480                 return REQ_VIEW_CLOSE;
5481         }
5482
5483         if (stage_line_type == LINE_STAT_UNTRACKED) {
5484                 if (!suffixcmp(stage_status.new.name, -1, "/")) {
5485                         report("Cannot display a directory");
5486                         return REQ_NONE;
5487                 }
5488
5489                 if (!prepare_update_file(view, stage_status.new.name)) {
5490                         report("Failed to open file: %s", strerror(errno));
5491                         return REQ_NONE;
5492                 }
5493         }
5494         open_view(view, REQ_VIEW_STAGE, OPEN_REFRESH);
5495
5496         return REQ_NONE;
5497 }
5498
5499 static struct view_ops stage_ops = {
5500         "line",
5501         NULL,
5502         NULL,
5503         pager_read,
5504         pager_draw,
5505         stage_request,
5506         pager_grep,
5507         pager_select,
5508 };
5509
5510
5511 /*
5512  * Revision graph
5513  */
5514
5515 struct commit {
5516         char id[SIZEOF_REV];            /* SHA1 ID. */
5517         char title[128];                /* First line of the commit message. */
5518         char author[75];                /* Author of the commit. */
5519         struct tm time;                 /* Date from the author ident. */
5520         struct ref **refs;              /* Repository references. */
5521         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
5522         size_t graph_size;              /* The width of the graph array. */
5523         bool has_parents;               /* Rewritten --parents seen. */
5524 };
5525
5526 /* Size of rev graph with no  "padding" columns */
5527 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
5528
5529 struct rev_graph {
5530         struct rev_graph *prev, *next, *parents;
5531         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
5532         size_t size;
5533         struct commit *commit;
5534         size_t pos;
5535         unsigned int boundary:1;
5536 };
5537
5538 /* Parents of the commit being visualized. */
5539 static struct rev_graph graph_parents[4];
5540
5541 /* The current stack of revisions on the graph. */
5542 static struct rev_graph graph_stacks[4] = {
5543         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
5544         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
5545         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
5546         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
5547 };
5548
5549 static inline bool
5550 graph_parent_is_merge(struct rev_graph *graph)
5551 {
5552         return graph->parents->size > 1;
5553 }
5554
5555 static inline void
5556 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
5557 {
5558         struct commit *commit = graph->commit;
5559
5560         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
5561                 commit->graph[commit->graph_size++] = symbol;
5562 }
5563
5564 static void
5565 clear_rev_graph(struct rev_graph *graph)
5566 {
5567         graph->boundary = 0;
5568         graph->size = graph->pos = 0;
5569         graph->commit = NULL;
5570         memset(graph->parents, 0, sizeof(*graph->parents));
5571 }
5572
5573 static void
5574 done_rev_graph(struct rev_graph *graph)
5575 {
5576         if (graph_parent_is_merge(graph) &&
5577             graph->pos < graph->size - 1 &&
5578             graph->next->size == graph->size + graph->parents->size - 1) {
5579                 size_t i = graph->pos + graph->parents->size - 1;
5580
5581                 graph->commit->graph_size = i * 2;
5582                 while (i < graph->next->size - 1) {
5583                         append_to_rev_graph(graph, ' ');
5584                         append_to_rev_graph(graph, '\\');
5585                         i++;
5586                 }
5587         }
5588
5589         clear_rev_graph(graph);
5590 }
5591
5592 static void
5593 push_rev_graph(struct rev_graph *graph, const char *parent)
5594 {
5595         int i;
5596
5597         /* "Collapse" duplicate parents lines.
5598          *
5599          * FIXME: This needs to also update update the drawn graph but
5600          * for now it just serves as a method for pruning graph lines. */
5601         for (i = 0; i < graph->size; i++)
5602                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
5603                         return;
5604
5605         if (graph->size < SIZEOF_REVITEMS) {
5606                 string_copy_rev(graph->rev[graph->size++], parent);
5607         }
5608 }
5609
5610 static chtype
5611 get_rev_graph_symbol(struct rev_graph *graph)
5612 {
5613         chtype symbol;
5614
5615         if (graph->boundary)
5616                 symbol = REVGRAPH_BOUND;
5617         else if (graph->parents->size == 0)
5618                 symbol = REVGRAPH_INIT;
5619         else if (graph_parent_is_merge(graph))
5620                 symbol = REVGRAPH_MERGE;
5621         else if (graph->pos >= graph->size)
5622                 symbol = REVGRAPH_BRANCH;
5623         else
5624                 symbol = REVGRAPH_COMMIT;
5625
5626         return symbol;
5627 }
5628
5629 static void
5630 draw_rev_graph(struct rev_graph *graph)
5631 {
5632         struct rev_filler {
5633                 chtype separator, line;
5634         };
5635         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
5636         static struct rev_filler fillers[] = {
5637                 { ' ',  '|' },
5638                 { '`',  '.' },
5639                 { '\'', ' ' },
5640                 { '/',  ' ' },
5641         };
5642         chtype symbol = get_rev_graph_symbol(graph);
5643         struct rev_filler *filler;
5644         size_t i;
5645
5646         if (opt_line_graphics)
5647                 fillers[DEFAULT].line = line_graphics[LINE_GRAPHIC_VLINE];
5648
5649         filler = &fillers[DEFAULT];
5650
5651         for (i = 0; i < graph->pos; i++) {
5652                 append_to_rev_graph(graph, filler->line);
5653                 if (graph_parent_is_merge(graph->prev) &&
5654                     graph->prev->pos == i)
5655                         filler = &fillers[RSHARP];
5656
5657                 append_to_rev_graph(graph, filler->separator);
5658         }
5659
5660         /* Place the symbol for this revision. */
5661         append_to_rev_graph(graph, symbol);
5662
5663         if (graph->prev->size > graph->size)
5664                 filler = &fillers[RDIAG];
5665         else
5666                 filler = &fillers[DEFAULT];
5667
5668         i++;
5669
5670         for (; i < graph->size; i++) {
5671                 append_to_rev_graph(graph, filler->separator);
5672                 append_to_rev_graph(graph, filler->line);
5673                 if (graph_parent_is_merge(graph->prev) &&
5674                     i < graph->prev->pos + graph->parents->size)
5675                         filler = &fillers[RSHARP];
5676                 if (graph->prev->size > graph->size)
5677                         filler = &fillers[LDIAG];
5678         }
5679
5680         if (graph->prev->size > graph->size) {
5681                 append_to_rev_graph(graph, filler->separator);
5682                 if (filler->line != ' ')
5683                         append_to_rev_graph(graph, filler->line);
5684         }
5685 }
5686
5687 /* Prepare the next rev graph */
5688 static void
5689 prepare_rev_graph(struct rev_graph *graph)
5690 {
5691         size_t i;
5692
5693         /* First, traverse all lines of revisions up to the active one. */
5694         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
5695                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
5696                         break;
5697
5698                 push_rev_graph(graph->next, graph->rev[graph->pos]);
5699         }
5700
5701         /* Interleave the new revision parent(s). */
5702         for (i = 0; !graph->boundary && i < graph->parents->size; i++)
5703                 push_rev_graph(graph->next, graph->parents->rev[i]);
5704
5705         /* Lastly, put any remaining revisions. */
5706         for (i = graph->pos + 1; i < graph->size; i++)
5707                 push_rev_graph(graph->next, graph->rev[i]);
5708 }
5709
5710 static void
5711 update_rev_graph(struct view *view, struct rev_graph *graph)
5712 {
5713         /* If this is the finalizing update ... */
5714         if (graph->commit)
5715                 prepare_rev_graph(graph);
5716
5717         /* Graph visualization needs a one rev look-ahead,
5718          * so the first update doesn't visualize anything. */
5719         if (!graph->prev->commit)
5720                 return;
5721
5722         if (view->lines > 2)
5723                 view->line[view->lines - 3].dirty = 1;
5724         if (view->lines > 1)
5725                 view->line[view->lines - 2].dirty = 1;
5726         draw_rev_graph(graph->prev);
5727         done_rev_graph(graph->prev->prev);
5728 }
5729
5730
5731 /*
5732  * Main view backend
5733  */
5734
5735 static const char *main_argv[SIZEOF_ARG] = {
5736         "git", "log", "--no-color", "--pretty=raw", "--parents",
5737                       "--topo-order", "%(head)", NULL
5738 };
5739
5740 static bool
5741 main_draw(struct view *view, struct line *line, unsigned int lineno)
5742 {
5743         struct commit *commit = line->data;
5744
5745         if (!*commit->author)
5746                 return FALSE;
5747
5748         if (opt_date && draw_date(view, &commit->time))
5749                 return TRUE;
5750
5751         if (opt_author && draw_author(view, commit->author))
5752                 return TRUE;
5753
5754         if (opt_rev_graph && commit->graph_size &&
5755             draw_graphic(view, LINE_MAIN_REVGRAPH, commit->graph, commit->graph_size))
5756                 return TRUE;
5757
5758         if (opt_show_refs && commit->refs) {
5759                 size_t i = 0;
5760
5761                 do {
5762                         enum line_type type;
5763
5764                         if (commit->refs[i]->head)
5765                                 type = LINE_MAIN_HEAD;
5766                         else if (commit->refs[i]->ltag)
5767                                 type = LINE_MAIN_LOCAL_TAG;
5768                         else if (commit->refs[i]->tag)
5769                                 type = LINE_MAIN_TAG;
5770                         else if (commit->refs[i]->tracked)
5771                                 type = LINE_MAIN_TRACKED;
5772                         else if (commit->refs[i]->remote)
5773                                 type = LINE_MAIN_REMOTE;
5774                         else
5775                                 type = LINE_MAIN_REF;
5776
5777                         if (draw_text(view, type, "[", TRUE) ||
5778                             draw_text(view, type, commit->refs[i]->name, TRUE) ||
5779                             draw_text(view, type, "]", TRUE))
5780                                 return TRUE;
5781
5782                         if (draw_text(view, LINE_DEFAULT, " ", TRUE))
5783                                 return TRUE;
5784                 } while (commit->refs[i++]->next);
5785         }
5786
5787         draw_text(view, LINE_DEFAULT, commit->title, TRUE);
5788         return TRUE;
5789 }
5790
5791 /* Reads git log --pretty=raw output and parses it into the commit struct. */
5792 static bool
5793 main_read(struct view *view, char *line)
5794 {
5795         static struct rev_graph *graph = graph_stacks;
5796         enum line_type type;
5797         struct commit *commit;
5798
5799         if (!line) {
5800                 int i;
5801
5802                 if (!view->lines && !view->parent)
5803                         die("No revisions match the given arguments.");
5804                 if (view->lines > 0) {
5805                         commit = view->line[view->lines - 1].data;
5806                         view->line[view->lines - 1].dirty = 1;
5807                         if (!*commit->author) {
5808                                 view->lines--;
5809                                 free(commit);
5810                                 graph->commit = NULL;
5811                         }
5812                 }
5813                 update_rev_graph(view, graph);
5814
5815                 for (i = 0; i < ARRAY_SIZE(graph_stacks); i++)
5816                         clear_rev_graph(&graph_stacks[i]);
5817                 return TRUE;
5818         }
5819
5820         type = get_line_type(line);
5821         if (type == LINE_COMMIT) {
5822                 commit = calloc(1, sizeof(struct commit));
5823                 if (!commit)
5824                         return FALSE;
5825
5826                 line += STRING_SIZE("commit ");
5827                 if (*line == '-') {
5828                         graph->boundary = 1;
5829                         line++;
5830                 }
5831
5832                 string_copy_rev(commit->id, line);
5833                 commit->refs = get_refs(commit->id);
5834                 graph->commit = commit;
5835                 add_line_data(view, commit, LINE_MAIN_COMMIT);
5836
5837                 while ((line = strchr(line, ' '))) {
5838                         line++;
5839                         push_rev_graph(graph->parents, line);
5840                         commit->has_parents = TRUE;
5841                 }
5842                 return TRUE;
5843         }
5844
5845         if (!view->lines)
5846                 return TRUE;
5847         commit = view->line[view->lines - 1].data;
5848
5849         switch (type) {
5850         case LINE_PARENT:
5851                 if (commit->has_parents)
5852                         break;
5853                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
5854                 break;
5855
5856         case LINE_AUTHOR:
5857                 parse_author_line(line + STRING_SIZE("author "),
5858                                   commit->author, sizeof(commit->author),
5859                                   &commit->time);
5860                 update_rev_graph(view, graph);
5861                 graph = graph->next;
5862                 break;
5863
5864         default:
5865                 /* Fill in the commit title if it has not already been set. */
5866                 if (commit->title[0])
5867                         break;
5868
5869                 /* Require titles to start with a non-space character at the
5870                  * offset used by git log. */
5871                 if (strncmp(line, "    ", 4))
5872                         break;
5873                 line += 4;
5874                 /* Well, if the title starts with a whitespace character,
5875                  * try to be forgiving.  Otherwise we end up with no title. */
5876                 while (isspace(*line))
5877                         line++;
5878                 if (*line == '\0')
5879                         break;
5880                 /* FIXME: More graceful handling of titles; append "..." to
5881                  * shortened titles, etc. */
5882
5883                 string_expand(commit->title, sizeof(commit->title), line, 1);
5884                 view->line[view->lines - 1].dirty = 1;
5885         }
5886
5887         return TRUE;
5888 }
5889
5890 static enum request
5891 main_request(struct view *view, enum request request, struct line *line)
5892 {
5893         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
5894
5895         switch (request) {
5896         case REQ_ENTER:
5897                 open_view(view, REQ_VIEW_DIFF, flags);
5898                 break;
5899         case REQ_REFRESH:
5900                 load_refs();
5901                 open_view(view, REQ_VIEW_MAIN, OPEN_REFRESH);
5902                 break;
5903         default:
5904                 return request;
5905         }
5906
5907         return REQ_NONE;
5908 }
5909
5910 static bool
5911 grep_refs(struct ref **refs, regex_t *regex)
5912 {
5913         regmatch_t pmatch;
5914         size_t i = 0;
5915
5916         if (!refs)
5917                 return FALSE;
5918         do {
5919                 if (regexec(regex, refs[i]->name, 1, &pmatch, 0) != REG_NOMATCH)
5920                         return TRUE;
5921         } while (refs[i++]->next);
5922
5923         return FALSE;
5924 }
5925
5926 static bool
5927 main_grep(struct view *view, struct line *line)
5928 {
5929         struct commit *commit = line->data;
5930         enum { S_TITLE, S_AUTHOR, S_DATE, S_REFS, S_END } state;
5931         char buf[DATE_COLS + 1];
5932         regmatch_t pmatch;
5933
5934         for (state = S_TITLE; state < S_END; state++) {
5935                 char *text;
5936
5937                 switch (state) {
5938                 case S_TITLE:   text = commit->title;   break;
5939                 case S_AUTHOR:
5940                         if (!opt_author)
5941                                 continue;
5942                         text = commit->author;
5943                         break;
5944                 case S_DATE:
5945                         if (!opt_date)
5946                                 continue;
5947                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
5948                                 continue;
5949                         text = buf;
5950                         break;
5951                 case S_REFS:
5952                         if (!opt_show_refs)
5953                                 continue;
5954                         if (grep_refs(commit->refs, view->regex) == TRUE)
5955                                 return TRUE;
5956                         continue;
5957                 default:
5958                         return FALSE;
5959                 }
5960
5961                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5962                         return TRUE;
5963         }
5964
5965         return FALSE;
5966 }
5967
5968 static void
5969 main_select(struct view *view, struct line *line)
5970 {
5971         struct commit *commit = line->data;
5972
5973         string_copy_rev(view->ref, commit->id);
5974         string_copy_rev(ref_commit, view->ref);
5975 }
5976
5977 static struct view_ops main_ops = {
5978         "commit",
5979         main_argv,
5980         NULL,
5981         main_read,
5982         main_draw,
5983         main_request,
5984         main_grep,
5985         main_select,
5986 };
5987
5988
5989 /*
5990  * Unicode / UTF-8 handling
5991  *
5992  * NOTE: Much of the following code for dealing with Unicode is derived from
5993  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
5994  * src/intl/charset.c from the UTF-8 branch commit elinks-0.11.0-g31f2c28.
5995  */
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();         /* Disable conversion and detect newlines from input. */
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 }