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