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