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