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