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