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