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