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