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