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