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