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