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