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