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