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