Cleanup exclude rule setup by using ls-files --exclude-standard flag
[tig] / tig.c
1 /* Copyright (c) 2006-2008 Jonas Fonseca <fonseca@diku.dk>
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU General Public License as
5  * published by the Free Software Foundation; either version 2 of
6  * the License, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17
18 #ifndef TIG_VERSION
19 #define TIG_VERSION "unknown-version"
20 #endif
21
22 #ifndef DEBUG
23 #define NDEBUG
24 #endif
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <signal.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <time.h>
38
39 #include <regex.h>
40
41 #include <locale.h>
42 #include <langinfo.h>
43 #include <iconv.h>
44
45 /* ncurses(3): Must be defined to have extended wide-character functions. */
46 #define _XOPEN_SOURCE_EXTENDED
47
48 #ifdef HAVE_NCURSESW_NCURSES_H
49 #include <ncursesw/ncurses.h>
50 #else
51 #ifdef HAVE_NCURSES_NCURSES_H
52 #include <ncurses/ncurses.h>
53 #else
54 #include <ncurses.h>
55 #endif
56 #endif
57
58 #if __GNUC__ >= 3
59 #define __NORETURN __attribute__((__noreturn__))
60 #else
61 #define __NORETURN
62 #endif
63
64 static void __NORETURN die(const char *err, ...);
65 static void warn(const char *msg, ...);
66 static void report(const char *msg, ...);
67 static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, size_t, char *, size_t));
68 static void set_nonblocking_input(bool loading);
69 static size_t utf8_length(const char *string, int *width, size_t max_width, int *trimmed, bool reserve);
70 static bool prompt_yesno(const char *prompt);
71
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-standard"
3971 #define STATUS_LIST_NO_HEAD_CMD \
3972         "git ls-files -z --cached --exclude-standard"
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         unsigned long prev_lineno = view->lineno;
3990         size_t i;
3991
3992         for (i = 0; i < view->lines; i++)
3993                 free(view->line[i].data);
3994         free(view->line);
3995         view->lines = view->line_alloc = view->line_size = view->lineno = 0;
3996         view->line = NULL;
3997
3998         if (!realloc_lines(view, view->line_size + 7))
3999                 return FALSE;
4000
4001         add_line_data(view, NULL, LINE_STAT_HEAD);
4002         if (opt_no_head)
4003                 string_copy(status_onbranch, "Initial commit");
4004         else if (!*opt_head)
4005                 string_copy(status_onbranch, "Not currently on any branch");
4006         else if (!string_format(status_onbranch, "On branch %s", opt_head))
4007                 return FALSE;
4008
4009         system("git update-index -q --refresh >/dev/null 2>/dev/null");
4010
4011         if (opt_no_head &&
4012             !status_run(view, STATUS_LIST_NO_HEAD_CMD, 'A', LINE_STAT_STAGED))
4013                 return FALSE;
4014         else if (!status_run(view, STATUS_DIFF_INDEX_CMD, 0, LINE_STAT_STAGED))
4015                 return FALSE;
4016
4017         if (!status_run(view, STATUS_DIFF_FILES_CMD, 0, LINE_STAT_UNSTAGED) ||
4018             !status_run(view, STATUS_LIST_OTHER_CMD, '?', LINE_STAT_UNTRACKED))
4019                 return FALSE;
4020
4021         /* If all went well restore the previous line number to stay in
4022          * the context or select a line with something that can be
4023          * updated. */
4024         if (prev_lineno >= view->lines)
4025                 prev_lineno = view->lines - 1;
4026         while (prev_lineno < view->lines && !view->line[prev_lineno].data)
4027                 prev_lineno++;
4028         while (prev_lineno > 0 && !view->line[prev_lineno].data)
4029                 prev_lineno--;
4030
4031         /* If the above fails, always skip the "On branch" line. */
4032         if (prev_lineno < view->lines)
4033                 view->lineno = prev_lineno;
4034         else
4035                 view->lineno = 1;
4036
4037         if (view->lineno < view->offset)
4038                 view->offset = view->lineno;
4039         else if (view->offset + view->height <= view->lineno)
4040                 view->offset = view->lineno - view->height + 1;
4041
4042         return TRUE;
4043 }
4044
4045 static bool
4046 status_draw(struct view *view, struct line *line, unsigned int lineno)
4047 {
4048         struct status *status = line->data;
4049         enum line_type type;
4050         char *text;
4051
4052         if (!status) {
4053                 switch (line->type) {
4054                 case LINE_STAT_STAGED:
4055                         type = LINE_STAT_SECTION;
4056                         text = "Changes to be committed:";
4057                         break;
4058
4059                 case LINE_STAT_UNSTAGED:
4060                         type = LINE_STAT_SECTION;
4061                         text = "Changed but not updated:";
4062                         break;
4063
4064                 case LINE_STAT_UNTRACKED:
4065                         type = LINE_STAT_SECTION;
4066                         text = "Untracked files:";
4067                         break;
4068
4069                 case LINE_STAT_NONE:
4070                         type = LINE_DEFAULT;
4071                         text = "    (no files)";
4072                         break;
4073
4074                 case LINE_STAT_HEAD:
4075                         type = LINE_STAT_HEAD;
4076                         text = status_onbranch;
4077                         break;
4078
4079                 default:
4080                         return FALSE;
4081                 }
4082         } else {
4083                 static char buf[] = { '?', ' ', ' ', ' ', 0 };
4084
4085                 buf[0] = status->status;
4086                 if (draw_text(view, line->type, buf, TRUE))
4087                         return TRUE;
4088                 type = LINE_DEFAULT;
4089                 text = status->new.name;
4090         }
4091
4092         draw_text(view, type, text, TRUE);
4093         return TRUE;
4094 }
4095
4096 static enum request
4097 status_enter(struct view *view, struct line *line)
4098 {
4099         struct status *status = line->data;
4100         char oldpath[SIZEOF_STR] = "";
4101         char newpath[SIZEOF_STR] = "";
4102         char *info;
4103         size_t cmdsize = 0;
4104         enum open_flags split;
4105
4106         if (line->type == LINE_STAT_NONE ||
4107             (!status && line[1].type == LINE_STAT_NONE)) {
4108                 report("No file to diff");
4109                 return REQ_NONE;
4110         }
4111
4112         if (status) {
4113                 if (sq_quote(oldpath, 0, status->old.name) >= sizeof(oldpath))
4114                         return REQ_QUIT;
4115                 /* Diffs for unmerged entries are empty when pasing the
4116                  * new path, so leave it empty. */
4117                 if (status->status != 'U' &&
4118                     sq_quote(newpath, 0, status->new.name) >= sizeof(newpath))
4119                         return REQ_QUIT;
4120         }
4121
4122         if (opt_cdup[0] &&
4123             line->type != LINE_STAT_UNTRACKED &&
4124             !string_format_from(opt_cmd, &cmdsize, "cd %s;", opt_cdup))
4125                 return REQ_QUIT;
4126
4127         switch (line->type) {
4128         case LINE_STAT_STAGED:
4129                 if (opt_no_head) {
4130                         if (!string_format_from(opt_cmd, &cmdsize,
4131                                                 STATUS_DIFF_NO_HEAD_SHOW_CMD,
4132                                                 newpath))
4133                                 return REQ_QUIT;
4134                 } else {
4135                         if (!string_format_from(opt_cmd, &cmdsize,
4136                                                 STATUS_DIFF_INDEX_SHOW_CMD,
4137                                                 oldpath, newpath))
4138                                 return REQ_QUIT;
4139                 }
4140
4141                 if (status)
4142                         info = "Staged changes to %s";
4143                 else
4144                         info = "Staged changes";
4145                 break;
4146
4147         case LINE_STAT_UNSTAGED:
4148                 if (!string_format_from(opt_cmd, &cmdsize,
4149                                         STATUS_DIFF_FILES_SHOW_CMD, oldpath, newpath))
4150                         return REQ_QUIT;
4151                 if (status)
4152                         info = "Unstaged changes to %s";
4153                 else
4154                         info = "Unstaged changes";
4155                 break;
4156
4157         case LINE_STAT_UNTRACKED:
4158                 if (opt_pipe)
4159                         return REQ_QUIT;
4160
4161                 if (!status) {
4162                         report("No file to show");
4163                         return REQ_NONE;
4164                 }
4165
4166                 opt_pipe = fopen(status->new.name, "r");
4167                 info = "Untracked file %s";
4168                 break;
4169
4170         case LINE_STAT_HEAD:
4171                 return REQ_NONE;
4172
4173         default:
4174                 die("line type %d not handled in switch", line->type);
4175         }
4176
4177         split = view_is_displayed(view) ? OPEN_SPLIT : 0;
4178         open_view(view, REQ_VIEW_STAGE, OPEN_RELOAD | split);
4179         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
4180                 if (status) {
4181                         stage_status = *status;
4182                 } else {
4183                         memset(&stage_status, 0, sizeof(stage_status));
4184                 }
4185
4186                 stage_line_type = line->type;
4187                 stage_chunks = 0;
4188                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.new.name);
4189         }
4190
4191         return REQ_NONE;
4192 }
4193
4194 static bool
4195 status_exists(struct status *status, enum line_type type)
4196 {
4197         struct view *view = VIEW(REQ_VIEW_STATUS);
4198         struct line *line;
4199
4200         for (line = view->line; line < view->line + view->lines; line++) {
4201                 struct status *pos = line->data;
4202
4203                 if (line->type == type && pos &&
4204                     !strcmp(status->new.name, pos->new.name))
4205                         return TRUE;
4206         }
4207
4208         return FALSE;
4209 }
4210
4211
4212 static FILE *
4213 status_update_prepare(enum line_type type)
4214 {
4215         char cmd[SIZEOF_STR];
4216         size_t cmdsize = 0;
4217
4218         if (opt_cdup[0] &&
4219             type != LINE_STAT_UNTRACKED &&
4220             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
4221                 return NULL;
4222
4223         switch (type) {
4224         case LINE_STAT_STAGED:
4225                 string_add(cmd, cmdsize, "git update-index -z --index-info");
4226                 break;
4227
4228         case LINE_STAT_UNSTAGED:
4229         case LINE_STAT_UNTRACKED:
4230                 string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin");
4231                 break;
4232
4233         default:
4234                 die("line type %d not handled in switch", type);
4235         }
4236
4237         return popen(cmd, "w");
4238 }
4239
4240 static bool
4241 status_update_write(FILE *pipe, struct status *status, enum line_type type)
4242 {
4243         char buf[SIZEOF_STR];
4244         size_t bufsize = 0;
4245         size_t written = 0;
4246
4247         switch (type) {
4248         case LINE_STAT_STAGED:
4249                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
4250                                         status->old.mode,
4251                                         status->old.rev,
4252                                         status->old.name, 0))
4253                         return FALSE;
4254                 break;
4255
4256         case LINE_STAT_UNSTAGED:
4257         case LINE_STAT_UNTRACKED:
4258                 if (!string_format_from(buf, &bufsize, "%s%c", status->new.name, 0))
4259                         return FALSE;
4260                 break;
4261
4262         default:
4263                 die("line type %d not handled in switch", type);
4264         }
4265
4266         while (!ferror(pipe) && written < bufsize) {
4267                 written += fwrite(buf + written, 1, bufsize - written, pipe);
4268         }
4269
4270         return written == bufsize;
4271 }
4272
4273 static bool
4274 status_update_file(struct status *status, enum line_type type)
4275 {
4276         FILE *pipe = status_update_prepare(type);
4277         bool result;
4278
4279         if (!pipe)
4280                 return FALSE;
4281
4282         result = status_update_write(pipe, status, type);
4283         pclose(pipe);
4284         return result;
4285 }
4286
4287 static bool
4288 status_update_files(struct view *view, struct line *line)
4289 {
4290         FILE *pipe = status_update_prepare(line->type);
4291         bool result = TRUE;
4292         struct line *pos = view->line + view->lines;
4293         int files = 0;
4294         int file, done;
4295
4296         if (!pipe)
4297                 return FALSE;
4298
4299         for (pos = line; pos < view->line + view->lines && pos->data; pos++)
4300                 files++;
4301
4302         for (file = 0, done = 0; result && file < files; line++, file++) {
4303                 int almost_done = file * 100 / files;
4304
4305                 if (almost_done > done) {
4306                         done = almost_done;
4307                         string_format(view->ref, "updating file %u of %u (%d%% done)",
4308                                       file, files, done);
4309                         update_view_title(view);
4310                 }
4311                 result = status_update_write(pipe, line->data, line->type);
4312         }
4313
4314         pclose(pipe);
4315         return result;
4316 }
4317
4318 static bool
4319 status_update(struct view *view)
4320 {
4321         struct line *line = &view->line[view->lineno];
4322
4323         assert(view->lines);
4324
4325         if (!line->data) {
4326                 /* This should work even for the "On branch" line. */
4327                 if (line < view->line + view->lines && !line[1].data) {
4328                         report("Nothing to update");
4329                         return FALSE;
4330                 }
4331
4332                 if (!status_update_files(view, line + 1)) {
4333                         report("Failed to update file status");
4334                         return FALSE;
4335                 }
4336
4337         } else if (!status_update_file(line->data, line->type)) {
4338                 report("Failed to update file status");
4339                 return FALSE;
4340         }
4341
4342         return TRUE;
4343 }
4344
4345 static bool
4346 status_checkout(struct view *view)
4347 {
4348         struct line *line = &view->line[view->lineno];
4349
4350         assert(view->lines);
4351
4352         if (!line->data || line->type != LINE_STAT_UNSTAGED) {
4353                 /* This should work even for the "On branch" line. */
4354                 if (line < view->line + view->lines && !line[1].data) {
4355                         report("Nothing to checkout");
4356                 } else if (line->type == LINE_STAT_UNTRACKED) {
4357                         report("Cannot checkout untracked files");
4358                 } else if (line->type == LINE_STAT_STAGED) {
4359                         report("Cannot checkout staged files");
4360                 } else {
4361                         report("Cannot checkout multiple files");
4362                 }
4363                 return FALSE;
4364
4365         } else {
4366                 struct status *status = line->data;
4367                 char cmd[SIZEOF_STR];
4368                 char file_sq[SIZEOF_STR];
4369
4370                 if (sq_quote(file_sq, 0, status->old.name) < sizeof(file_sq) &&
4371                     string_format(cmd, "git checkout %s%s", opt_cdup, file_sq)) {
4372                         run_confirm(cmd, "Are you sure you want to overwrite any changes?");
4373                 }
4374
4375                 return TRUE;
4376         }
4377 }
4378
4379 static enum request
4380 status_request(struct view *view, enum request request, struct line *line)
4381 {
4382         struct status *status = line->data;
4383
4384         switch (request) {
4385         case REQ_STATUS_UPDATE:
4386                 if (!status_update(view))
4387                         return REQ_NONE;
4388                 break;
4389
4390         case REQ_STATUS_CHECKOUT:
4391                 if (!status_checkout(view))
4392                         return REQ_NONE;
4393                 break;
4394
4395         case REQ_STATUS_MERGE:
4396                 if (!status || status->status != 'U') {
4397                         report("Merging only possible for files with unmerged status ('U').");
4398                         return REQ_NONE;
4399                 }
4400                 open_mergetool(status->new.name);
4401                 break;
4402
4403         case REQ_EDIT:
4404                 if (!status)
4405                         return request;
4406
4407                 open_editor(status->status != '?', status->new.name);
4408                 break;
4409
4410         case REQ_VIEW_BLAME:
4411                 if (status) {
4412                         string_copy(opt_file, status->new.name);
4413                         opt_ref[0] = 0;
4414                 }
4415                 return request;
4416
4417         case REQ_ENTER:
4418                 /* After returning the status view has been split to
4419                  * show the stage view. No further reloading is
4420                  * necessary. */
4421                 status_enter(view, line);
4422                 return REQ_NONE;
4423
4424         case REQ_REFRESH:
4425                 /* Simply reload the view. */
4426                 break;
4427
4428         default:
4429                 return request;
4430         }
4431
4432         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
4433
4434         return REQ_NONE;
4435 }
4436
4437 static void
4438 status_select(struct view *view, struct line *line)
4439 {
4440         struct status *status = line->data;
4441         char file[SIZEOF_STR] = "all files";
4442         char *text;
4443         char *key;
4444
4445         if (status && !string_format(file, "'%s'", status->new.name))
4446                 return;
4447
4448         if (!status && line[1].type == LINE_STAT_NONE)
4449                 line++;
4450
4451         switch (line->type) {
4452         case LINE_STAT_STAGED:
4453                 text = "Press %s to unstage %s for commit";
4454                 break;
4455
4456         case LINE_STAT_UNSTAGED:
4457                 text = "Press %s to stage %s for commit";
4458                 break;
4459
4460         case LINE_STAT_UNTRACKED:
4461                 text = "Press %s to stage %s for addition";
4462                 break;
4463
4464         case LINE_STAT_HEAD:
4465         case LINE_STAT_NONE:
4466                 text = "Nothing to update";
4467                 break;
4468
4469         default:
4470                 die("line type %d not handled in switch", line->type);
4471         }
4472
4473         if (status && status->status == 'U') {
4474                 text = "Press %s to resolve conflict in %s";
4475                 key = get_key(REQ_STATUS_MERGE);
4476
4477         } else {
4478                 key = get_key(REQ_STATUS_UPDATE);
4479         }
4480
4481         string_format(view->ref, text, key, file);
4482 }
4483
4484 static bool
4485 status_grep(struct view *view, struct line *line)
4486 {
4487         struct status *status = line->data;
4488         enum { S_STATUS, S_NAME, S_END } state;
4489         char buf[2] = "?";
4490         regmatch_t pmatch;
4491
4492         if (!status)
4493                 return FALSE;
4494
4495         for (state = S_STATUS; state < S_END; state++) {
4496                 char *text;
4497
4498                 switch (state) {
4499                 case S_NAME:    text = status->new.name;        break;
4500                 case S_STATUS:
4501                         buf[0] = status->status;
4502                         text = buf;
4503                         break;
4504
4505                 default:
4506                         return FALSE;
4507                 }
4508
4509                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4510                         return TRUE;
4511         }
4512
4513         return FALSE;
4514 }
4515
4516 static struct view_ops status_ops = {
4517         "file",
4518         status_open,
4519         NULL,
4520         status_draw,
4521         status_request,
4522         status_grep,
4523         status_select,
4524 };
4525
4526
4527 static bool
4528 stage_diff_line(FILE *pipe, struct line *line)
4529 {
4530         char *buf = line->data;
4531         size_t bufsize = strlen(buf);
4532         size_t written = 0;
4533
4534         while (!ferror(pipe) && written < bufsize) {
4535                 written += fwrite(buf + written, 1, bufsize - written, pipe);
4536         }
4537
4538         fputc('\n', pipe);
4539
4540         return written == bufsize;
4541 }
4542
4543 static bool
4544 stage_diff_write(FILE *pipe, struct line *line, struct line *end)
4545 {
4546         while (line < end) {
4547                 if (!stage_diff_line(pipe, line++))
4548                         return FALSE;
4549                 if (line->type == LINE_DIFF_CHUNK ||
4550                     line->type == LINE_DIFF_HEADER)
4551                         break;
4552         }
4553
4554         return TRUE;
4555 }
4556
4557 static struct line *
4558 stage_diff_find(struct view *view, struct line *line, enum line_type type)
4559 {
4560         for (; view->line < line; line--)
4561                 if (line->type == type)
4562                         return line;
4563
4564         return NULL;
4565 }
4566
4567 static bool
4568 stage_update_chunk(struct view *view, struct line *chunk)
4569 {
4570         char cmd[SIZEOF_STR];
4571         size_t cmdsize = 0;
4572         struct line *diff_hdr;
4573         FILE *pipe;
4574
4575         diff_hdr = stage_diff_find(view, chunk, LINE_DIFF_HEADER);
4576         if (!diff_hdr)
4577                 return FALSE;
4578
4579         if (opt_cdup[0] &&
4580             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
4581                 return FALSE;
4582
4583         if (!string_format_from(cmd, &cmdsize,
4584                                 "git apply --whitespace=nowarn --cached %s - && "
4585                                 "git update-index -q --unmerged --refresh 2>/dev/null",
4586                                 stage_line_type == LINE_STAT_STAGED ? "-R" : ""))
4587                 return FALSE;
4588
4589         pipe = popen(cmd, "w");
4590         if (!pipe)
4591                 return FALSE;
4592
4593         if (!stage_diff_write(pipe, diff_hdr, chunk) ||
4594             !stage_diff_write(pipe, chunk, view->line + view->lines))
4595                 chunk = NULL;
4596
4597         pclose(pipe);
4598
4599         return chunk ? TRUE : FALSE;
4600 }
4601
4602 static bool
4603 stage_update(struct view *view, struct line *line)
4604 {
4605         struct line *chunk = NULL;
4606
4607         if (!opt_no_head && stage_line_type != LINE_STAT_UNTRACKED)
4608                 chunk = stage_diff_find(view, line, LINE_DIFF_CHUNK);
4609
4610         if (chunk) {
4611                 if (!stage_update_chunk(view, chunk)) {
4612                         report("Failed to apply chunk");
4613                         return FALSE;
4614                 }
4615
4616         } else if (!stage_status.status) {
4617                 view = VIEW(REQ_VIEW_STATUS);
4618
4619                 for (line = view->line; line < view->line + view->lines; line++)
4620                         if (line->type == stage_line_type)
4621                                 break;
4622
4623                 if (!status_update_files(view, line + 1)) {
4624                         report("Failed to update files");
4625                         return FALSE;
4626                 }
4627
4628         } else if (!status_update_file(&stage_status, stage_line_type)) {
4629                 report("Failed to update file");
4630                 return FALSE;
4631         }
4632
4633         return TRUE;
4634 }
4635
4636 static void
4637 stage_next(struct view *view, struct line *line)
4638 {
4639         int i;
4640
4641         if (!stage_chunks) {
4642                 static size_t alloc = 0;
4643                 int *tmp;
4644
4645                 for (line = view->line; line < view->line + view->lines; line++) {
4646                         if (line->type != LINE_DIFF_CHUNK)
4647                                 continue;
4648
4649                         tmp = realloc_items(stage_chunk, &alloc,
4650                                             stage_chunks, sizeof(*tmp));
4651                         if (!tmp) {
4652                                 report("Allocation failure");
4653                                 return;
4654                         }
4655
4656                         stage_chunk = tmp;
4657                         stage_chunk[stage_chunks++] = line - view->line;
4658                 }
4659         }
4660
4661         for (i = 0; i < stage_chunks; i++) {
4662                 if (stage_chunk[i] > view->lineno) {
4663                         do_scroll_view(view, stage_chunk[i] - view->lineno);
4664                         report("Chunk %d of %d", i + 1, stage_chunks);
4665                         return;
4666                 }
4667         }
4668
4669         report("No next chunk found");
4670 }
4671
4672 static enum request
4673 stage_request(struct view *view, enum request request, struct line *line)
4674 {
4675         switch (request) {
4676         case REQ_STATUS_UPDATE:
4677                 if (!stage_update(view, line))
4678                         return REQ_NONE;
4679                 break;
4680
4681         case REQ_STAGE_NEXT:
4682                 if (stage_line_type == LINE_STAT_UNTRACKED) {
4683                         report("File is untracked; press %s to add",
4684                                get_key(REQ_STATUS_UPDATE));
4685                         return REQ_NONE;
4686                 }
4687                 stage_next(view, line);
4688                 return REQ_NONE;
4689
4690         case REQ_EDIT:
4691                 if (!stage_status.new.name[0])
4692                         return request;
4693
4694                 open_editor(stage_status.status != '?', stage_status.new.name);
4695                 break;
4696
4697         case REQ_REFRESH:
4698                 /* Reload everything ... */
4699                 break;
4700
4701         case REQ_VIEW_BLAME:
4702                 if (stage_status.new.name[0]) {
4703                         string_copy(opt_file, stage_status.new.name);
4704                         opt_ref[0] = 0;
4705                 }
4706                 return request;
4707
4708         case REQ_ENTER:
4709                 return pager_request(view, request, line);
4710
4711         default:
4712                 return request;
4713         }
4714
4715         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD | OPEN_NOMAXIMIZE);
4716
4717         /* Check whether the staged entry still exists, and close the
4718          * stage view if it doesn't. */
4719         if (!status_exists(&stage_status, stage_line_type))
4720                 return REQ_VIEW_CLOSE;
4721
4722         if (stage_line_type == LINE_STAT_UNTRACKED)
4723                 opt_pipe = fopen(stage_status.new.name, "r");
4724         else
4725                 string_copy(opt_cmd, view->cmd);
4726         open_view(view, REQ_VIEW_STAGE, OPEN_RELOAD | OPEN_NOMAXIMIZE);
4727
4728         return REQ_NONE;
4729 }
4730
4731 static struct view_ops stage_ops = {
4732         "line",
4733         NULL,
4734         pager_read,
4735         pager_draw,
4736         stage_request,
4737         pager_grep,
4738         pager_select,
4739 };
4740
4741
4742 /*
4743  * Revision graph
4744  */
4745
4746 struct commit {
4747         char id[SIZEOF_REV];            /* SHA1 ID. */
4748         char title[128];                /* First line of the commit message. */
4749         char author[75];                /* Author of the commit. */
4750         struct tm time;                 /* Date from the author ident. */
4751         struct ref **refs;              /* Repository references. */
4752         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
4753         size_t graph_size;              /* The width of the graph array. */
4754         bool has_parents;               /* Rewritten --parents seen. */
4755 };
4756
4757 /* Size of rev graph with no  "padding" columns */
4758 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
4759
4760 struct rev_graph {
4761         struct rev_graph *prev, *next, *parents;
4762         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
4763         size_t size;
4764         struct commit *commit;
4765         size_t pos;
4766         unsigned int boundary:1;
4767 };
4768
4769 /* Parents of the commit being visualized. */
4770 static struct rev_graph graph_parents[4];
4771
4772 /* The current stack of revisions on the graph. */
4773 static struct rev_graph graph_stacks[4] = {
4774         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
4775         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
4776         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
4777         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
4778 };
4779
4780 static inline bool
4781 graph_parent_is_merge(struct rev_graph *graph)
4782 {
4783         return graph->parents->size > 1;
4784 }
4785
4786 static inline void
4787 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
4788 {
4789         struct commit *commit = graph->commit;
4790
4791         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
4792                 commit->graph[commit->graph_size++] = symbol;
4793 }
4794
4795 static void
4796 clear_rev_graph(struct rev_graph *graph)
4797 {
4798         graph->boundary = 0;
4799         graph->size = graph->pos = 0;
4800         graph->commit = NULL;
4801         memset(graph->parents, 0, sizeof(*graph->parents));
4802 }
4803
4804 static void
4805 done_rev_graph(struct rev_graph *graph)
4806 {
4807         if (graph_parent_is_merge(graph) &&
4808             graph->pos < graph->size - 1 &&
4809             graph->next->size == graph->size + graph->parents->size - 1) {
4810                 size_t i = graph->pos + graph->parents->size - 1;
4811
4812                 graph->commit->graph_size = i * 2;
4813                 while (i < graph->next->size - 1) {
4814                         append_to_rev_graph(graph, ' ');
4815                         append_to_rev_graph(graph, '\\');
4816                         i++;
4817                 }
4818         }
4819
4820         clear_rev_graph(graph);
4821 }
4822
4823 static void
4824 push_rev_graph(struct rev_graph *graph, char *parent)
4825 {
4826         int i;
4827
4828         /* "Collapse" duplicate parents lines.
4829          *
4830          * FIXME: This needs to also update update the drawn graph but
4831          * for now it just serves as a method for pruning graph lines. */
4832         for (i = 0; i < graph->size; i++)
4833                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
4834                         return;
4835
4836         if (graph->size < SIZEOF_REVITEMS) {
4837                 string_copy_rev(graph->rev[graph->size++], parent);
4838         }
4839 }
4840
4841 static chtype
4842 get_rev_graph_symbol(struct rev_graph *graph)
4843 {
4844         chtype symbol;
4845
4846         if (graph->boundary)
4847                 symbol = REVGRAPH_BOUND;
4848         else if (graph->parents->size == 0)
4849                 symbol = REVGRAPH_INIT;
4850         else if (graph_parent_is_merge(graph))
4851                 symbol = REVGRAPH_MERGE;
4852         else if (graph->pos >= graph->size)
4853                 symbol = REVGRAPH_BRANCH;
4854         else
4855                 symbol = REVGRAPH_COMMIT;
4856
4857         return symbol;
4858 }
4859
4860 static void
4861 draw_rev_graph(struct rev_graph *graph)
4862 {
4863         struct rev_filler {
4864                 chtype separator, line;
4865         };
4866         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
4867         static struct rev_filler fillers[] = {
4868                 { ' ',  '|' },
4869                 { '`',  '.' },
4870                 { '\'', ' ' },
4871                 { '/',  ' ' },
4872         };
4873         chtype symbol = get_rev_graph_symbol(graph);
4874         struct rev_filler *filler;
4875         size_t i;
4876
4877         if (opt_line_graphics)
4878                 fillers[DEFAULT].line = line_graphics[LINE_GRAPHIC_VLINE];
4879
4880         filler = &fillers[DEFAULT];
4881
4882         for (i = 0; i < graph->pos; i++) {
4883                 append_to_rev_graph(graph, filler->line);
4884                 if (graph_parent_is_merge(graph->prev) &&
4885                     graph->prev->pos == i)
4886                         filler = &fillers[RSHARP];
4887
4888                 append_to_rev_graph(graph, filler->separator);
4889         }
4890
4891         /* Place the symbol for this revision. */
4892         append_to_rev_graph(graph, symbol);
4893
4894         if (graph->prev->size > graph->size)
4895                 filler = &fillers[RDIAG];
4896         else
4897                 filler = &fillers[DEFAULT];
4898
4899         i++;
4900
4901         for (; i < graph->size; i++) {
4902                 append_to_rev_graph(graph, filler->separator);
4903                 append_to_rev_graph(graph, filler->line);
4904                 if (graph_parent_is_merge(graph->prev) &&
4905                     i < graph->prev->pos + graph->parents->size)
4906                         filler = &fillers[RSHARP];
4907                 if (graph->prev->size > graph->size)
4908                         filler = &fillers[LDIAG];
4909         }
4910
4911         if (graph->prev->size > graph->size) {
4912                 append_to_rev_graph(graph, filler->separator);
4913                 if (filler->line != ' ')
4914                         append_to_rev_graph(graph, filler->line);
4915         }
4916 }
4917
4918 /* Prepare the next rev graph */
4919 static void
4920 prepare_rev_graph(struct rev_graph *graph)
4921 {
4922         size_t i;
4923
4924         /* First, traverse all lines of revisions up to the active one. */
4925         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
4926                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
4927                         break;
4928
4929                 push_rev_graph(graph->next, graph->rev[graph->pos]);
4930         }
4931
4932         /* Interleave the new revision parent(s). */
4933         for (i = 0; !graph->boundary && i < graph->parents->size; i++)
4934                 push_rev_graph(graph->next, graph->parents->rev[i]);
4935
4936         /* Lastly, put any remaining revisions. */
4937         for (i = graph->pos + 1; i < graph->size; i++)
4938                 push_rev_graph(graph->next, graph->rev[i]);
4939 }
4940
4941 static void
4942 update_rev_graph(struct rev_graph *graph)
4943 {
4944         /* If this is the finalizing update ... */
4945         if (graph->commit)
4946                 prepare_rev_graph(graph);
4947
4948         /* Graph visualization needs a one rev look-ahead,
4949          * so the first update doesn't visualize anything. */
4950         if (!graph->prev->commit)
4951                 return;
4952
4953         draw_rev_graph(graph->prev);
4954         done_rev_graph(graph->prev->prev);
4955 }
4956
4957
4958 /*
4959  * Main view backend
4960  */
4961
4962 static bool
4963 main_draw(struct view *view, struct line *line, unsigned int lineno)
4964 {
4965         struct commit *commit = line->data;
4966
4967         if (!*commit->author)
4968                 return FALSE;
4969
4970         if (opt_date && draw_date(view, &commit->time))
4971                 return TRUE;
4972
4973         if (opt_author &&
4974             draw_field(view, LINE_MAIN_AUTHOR, commit->author, opt_author_cols, TRUE))
4975                 return TRUE;
4976
4977         if (opt_rev_graph && commit->graph_size &&
4978             draw_graphic(view, LINE_MAIN_REVGRAPH, commit->graph, commit->graph_size))
4979                 return TRUE;
4980
4981         if (opt_show_refs && commit->refs) {
4982                 size_t i = 0;
4983
4984                 do {
4985                         enum line_type type;
4986
4987                         if (commit->refs[i]->head)
4988                                 type = LINE_MAIN_HEAD;
4989                         else if (commit->refs[i]->ltag)
4990                                 type = LINE_MAIN_LOCAL_TAG;
4991                         else if (commit->refs[i]->tag)
4992                                 type = LINE_MAIN_TAG;
4993                         else if (commit->refs[i]->tracked)
4994                                 type = LINE_MAIN_TRACKED;
4995                         else if (commit->refs[i]->remote)
4996                                 type = LINE_MAIN_REMOTE;
4997                         else
4998                                 type = LINE_MAIN_REF;
4999
5000                         if (draw_text(view, type, "[", TRUE) ||
5001                             draw_text(view, type, commit->refs[i]->name, TRUE) ||
5002                             draw_text(view, type, "]", TRUE))
5003                                 return TRUE;
5004
5005                         if (draw_text(view, LINE_DEFAULT, " ", TRUE))
5006                                 return TRUE;
5007                 } while (commit->refs[i++]->next);
5008         }
5009
5010         draw_text(view, LINE_DEFAULT, commit->title, TRUE);
5011         return TRUE;
5012 }
5013
5014 /* Reads git log --pretty=raw output and parses it into the commit struct. */
5015 static bool
5016 main_read(struct view *view, char *line)
5017 {
5018         static struct rev_graph *graph = graph_stacks;
5019         enum line_type type;
5020         struct commit *commit;
5021
5022         if (!line) {
5023                 int i;
5024
5025                 if (!view->lines && !view->parent)
5026                         die("No revisions match the given arguments.");
5027                 if (view->lines > 0) {
5028                         commit = view->line[view->lines - 1].data;
5029                         if (!*commit->author) {
5030                                 view->lines--;
5031                                 free(commit);
5032                                 graph->commit = NULL;
5033                         }
5034                 }
5035                 update_rev_graph(graph);
5036
5037                 for (i = 0; i < ARRAY_SIZE(graph_stacks); i++)
5038                         clear_rev_graph(&graph_stacks[i]);
5039                 return TRUE;
5040         }
5041
5042         type = get_line_type(line);
5043         if (type == LINE_COMMIT) {
5044                 commit = calloc(1, sizeof(struct commit));
5045                 if (!commit)
5046                         return FALSE;
5047
5048                 line += STRING_SIZE("commit ");
5049                 if (*line == '-') {
5050                         graph->boundary = 1;
5051                         line++;
5052                 }
5053
5054                 string_copy_rev(commit->id, line);
5055                 commit->refs = get_refs(commit->id);
5056                 graph->commit = commit;
5057                 add_line_data(view, commit, LINE_MAIN_COMMIT);
5058
5059                 while ((line = strchr(line, ' '))) {
5060                         line++;
5061                         push_rev_graph(graph->parents, line);
5062                         commit->has_parents = TRUE;
5063                 }
5064                 return TRUE;
5065         }
5066
5067         if (!view->lines)
5068                 return TRUE;
5069         commit = view->line[view->lines - 1].data;
5070
5071         switch (type) {
5072         case LINE_PARENT:
5073                 if (commit->has_parents)
5074                         break;
5075                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
5076                 break;
5077
5078         case LINE_AUTHOR:
5079         {
5080                 /* Parse author lines where the name may be empty:
5081                  *      author  <email@address.tld> 1138474660 +0100
5082                  */
5083                 char *ident = line + STRING_SIZE("author ");
5084                 char *nameend = strchr(ident, '<');
5085                 char *emailend = strchr(ident, '>');
5086
5087                 if (!nameend || !emailend)
5088                         break;
5089
5090                 update_rev_graph(graph);
5091                 graph = graph->next;
5092
5093                 *nameend = *emailend = 0;
5094                 ident = chomp_string(ident);
5095                 if (!*ident) {
5096                         ident = chomp_string(nameend + 1);
5097                         if (!*ident)
5098                                 ident = "Unknown";
5099                 }
5100
5101                 string_ncopy(commit->author, ident, strlen(ident));
5102
5103                 /* Parse epoch and timezone */
5104                 if (emailend[1] == ' ') {
5105                         char *secs = emailend + 2;
5106                         char *zone = strchr(secs, ' ');
5107                         time_t time = (time_t) atol(secs);
5108
5109                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
5110                                 long tz;
5111
5112                                 zone++;
5113                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
5114                                 tz += ('0' - zone[2]) * 60 * 60;
5115                                 tz += ('0' - zone[3]) * 60;
5116                                 tz += ('0' - zone[4]) * 60;
5117
5118                                 if (zone[0] == '-')
5119                                         tz = -tz;
5120
5121                                 time -= tz;
5122                         }
5123
5124                         gmtime_r(&time, &commit->time);
5125                 }
5126                 break;
5127         }
5128         default:
5129                 /* Fill in the commit title if it has not already been set. */
5130                 if (commit->title[0])
5131                         break;
5132
5133                 /* Require titles to start with a non-space character at the
5134                  * offset used by git log. */
5135                 if (strncmp(line, "    ", 4))
5136                         break;
5137                 line += 4;
5138                 /* Well, if the title starts with a whitespace character,
5139                  * try to be forgiving.  Otherwise we end up with no title. */
5140                 while (isspace(*line))
5141                         line++;
5142                 if (*line == '\0')
5143                         break;
5144                 /* FIXME: More graceful handling of titles; append "..." to
5145                  * shortened titles, etc. */
5146
5147                 string_ncopy(commit->title, line, strlen(line));
5148         }
5149
5150         return TRUE;
5151 }
5152
5153 static enum request
5154 main_request(struct view *view, enum request request, struct line *line)
5155 {
5156         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
5157
5158         switch (request) {
5159         case REQ_ENTER:
5160                 open_view(view, REQ_VIEW_DIFF, flags);
5161                 break;
5162         case REQ_REFRESH:
5163                 string_copy(opt_cmd, view->cmd);
5164                 open_view(view, REQ_VIEW_MAIN, OPEN_RELOAD);
5165                 break;
5166         default:
5167                 return request;
5168         }
5169
5170         return REQ_NONE;
5171 }
5172
5173 static bool
5174 grep_refs(struct ref **refs, regex_t *regex)
5175 {
5176         regmatch_t pmatch;
5177         size_t i = 0;
5178
5179         if (!refs)
5180                 return FALSE;
5181         do {
5182                 if (regexec(regex, refs[i]->name, 1, &pmatch, 0) != REG_NOMATCH)
5183                         return TRUE;
5184         } while (refs[i++]->next);
5185
5186         return FALSE;
5187 }
5188
5189 static bool
5190 main_grep(struct view *view, struct line *line)
5191 {
5192         struct commit *commit = line->data;
5193         enum { S_TITLE, S_AUTHOR, S_DATE, S_REFS, S_END } state;
5194         char buf[DATE_COLS + 1];
5195         regmatch_t pmatch;
5196
5197         for (state = S_TITLE; state < S_END; state++) {
5198                 char *text;
5199
5200                 switch (state) {
5201                 case S_TITLE:   text = commit->title;   break;
5202                 case S_AUTHOR:
5203                         if (!opt_author)
5204                                 continue;
5205                         text = commit->author;
5206                         break;
5207                 case S_DATE:
5208                         if (!opt_date)
5209                                 continue;
5210                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
5211                                 continue;
5212                         text = buf;
5213                         break;
5214                 case S_REFS:
5215                         if (!opt_show_refs)
5216                                 continue;
5217                         if (grep_refs(commit->refs, view->regex) == TRUE)
5218                                 return TRUE;
5219                         continue;
5220                 default:
5221                         return FALSE;
5222                 }
5223
5224                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5225                         return TRUE;
5226         }
5227
5228         return FALSE;
5229 }
5230
5231 static void
5232 main_select(struct view *view, struct line *line)
5233 {
5234         struct commit *commit = line->data;
5235
5236         string_copy_rev(view->ref, commit->id);
5237         string_copy_rev(ref_commit, view->ref);
5238 }
5239
5240 static struct view_ops main_ops = {
5241         "commit",
5242         NULL,
5243         main_read,
5244         main_draw,
5245         main_request,
5246         main_grep,
5247         main_select,
5248 };
5249
5250
5251 /*
5252  * Unicode / UTF-8 handling
5253  *
5254  * NOTE: Much of the following code for dealing with unicode is derived from
5255  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
5256  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
5257  */
5258
5259 /* I've (over)annotated a lot of code snippets because I am not entirely
5260  * confident that the approach taken by this small UTF-8 interface is correct.
5261  * --jonas */
5262
5263 static inline int
5264 unicode_width(unsigned long c)
5265 {
5266         if (c >= 0x1100 &&
5267            (c <= 0x115f                         /* Hangul Jamo */
5268             || c == 0x2329
5269             || c == 0x232a
5270             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
5271                                                 /* CJK ... Yi */
5272             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
5273             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
5274             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
5275             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
5276             || (c >= 0xffe0  && c <= 0xffe6)
5277             || (c >= 0x20000 && c <= 0x2fffd)
5278             || (c >= 0x30000 && c <= 0x3fffd)))
5279                 return 2;
5280
5281         if (c == '\t')
5282                 return opt_tab_size;
5283
5284         return 1;
5285 }
5286
5287 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
5288  * Illegal bytes are set one. */
5289 static const unsigned char utf8_bytes[256] = {
5290         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,
5291         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,
5292         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,
5293         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,
5294         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,
5295         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,
5296         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,
5297         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,
5298 };
5299
5300 /* Decode UTF-8 multi-byte representation into a unicode character. */
5301 static inline unsigned long
5302 utf8_to_unicode(const char *string, size_t length)
5303 {
5304         unsigned long unicode;
5305
5306         switch (length) {
5307         case 1:
5308                 unicode  =   string[0];
5309                 break;
5310         case 2:
5311                 unicode  =  (string[0] & 0x1f) << 6;
5312                 unicode +=  (string[1] & 0x3f);
5313                 break;
5314         case 3:
5315                 unicode  =  (string[0] & 0x0f) << 12;
5316                 unicode += ((string[1] & 0x3f) << 6);
5317                 unicode +=  (string[2] & 0x3f);
5318                 break;
5319         case 4:
5320                 unicode  =  (string[0] & 0x0f) << 18;
5321                 unicode += ((string[1] & 0x3f) << 12);
5322                 unicode += ((string[2] & 0x3f) << 6);
5323                 unicode +=  (string[3] & 0x3f);
5324                 break;
5325         case 5:
5326                 unicode  =  (string[0] & 0x0f) << 24;
5327                 unicode += ((string[1] & 0x3f) << 18);
5328                 unicode += ((string[2] & 0x3f) << 12);
5329                 unicode += ((string[3] & 0x3f) << 6);
5330                 unicode +=  (string[4] & 0x3f);
5331                 break;
5332         case 6:
5333                 unicode  =  (string[0] & 0x01) << 30;
5334                 unicode += ((string[1] & 0x3f) << 24);
5335                 unicode += ((string[2] & 0x3f) << 18);
5336                 unicode += ((string[3] & 0x3f) << 12);
5337                 unicode += ((string[4] & 0x3f) << 6);
5338                 unicode +=  (string[5] & 0x3f);
5339                 break;
5340         default:
5341                 die("Invalid unicode length");
5342         }
5343
5344         /* Invalid characters could return the special 0xfffd value but NUL
5345          * should be just as good. */
5346         return unicode > 0xffff ? 0 : unicode;
5347 }
5348
5349 /* Calculates how much of string can be shown within the given maximum width
5350  * and sets trimmed parameter to non-zero value if all of string could not be
5351  * shown. If the reserve flag is TRUE, it will reserve at least one
5352  * trailing character, which can be useful when drawing a delimiter.
5353  *
5354  * Returns the number of bytes to output from string to satisfy max_width. */
5355 static size_t
5356 utf8_length(const char *string, int *width, size_t max_width, int *trimmed, bool reserve)
5357 {
5358         const char *start = string;
5359         const char *end = strchr(string, '\0');
5360         unsigned char last_bytes = 0;
5361         size_t last_ucwidth = 0;
5362
5363         *width = 0;
5364         *trimmed = 0;
5365
5366         while (string < end) {
5367                 int c = *(unsigned char *) string;
5368                 unsigned char bytes = utf8_bytes[c];
5369                 size_t ucwidth;
5370                 unsigned long unicode;
5371
5372                 if (string + bytes > end)
5373                         break;
5374
5375                 /* Change representation to figure out whether
5376                  * it is a single- or double-width character. */
5377
5378                 unicode = utf8_to_unicode(string, bytes);
5379                 /* FIXME: Graceful handling of invalid unicode character. */
5380                 if (!unicode)
5381                         break;
5382
5383                 ucwidth = unicode_width(unicode);
5384                 *width  += ucwidth;
5385                 if (*width > max_width) {
5386                         *trimmed = 1;
5387                         *width -= ucwidth;
5388                         if (reserve && *width == max_width) {
5389                                 string -= last_bytes;
5390                                 *width -= last_ucwidth;
5391                         }
5392                         break;
5393                 }
5394
5395                 string  += bytes;
5396                 last_bytes = bytes;
5397                 last_ucwidth = ucwidth;
5398         }
5399
5400         return string - start;
5401 }
5402
5403
5404 /*
5405  * Status management
5406  */
5407
5408 /* Whether or not the curses interface has been initialized. */
5409 static bool cursed = FALSE;
5410
5411 /* The status window is used for polling keystrokes. */
5412 static WINDOW *status_win;
5413
5414 static bool status_empty = TRUE;
5415
5416 /* Update status and title window. */
5417 static void
5418 report(const char *msg, ...)
5419 {
5420         struct view *view = display[current_view];
5421
5422         if (input_mode)
5423                 return;
5424
5425         if (!view) {
5426                 char buf[SIZEOF_STR];
5427                 va_list args;
5428
5429                 va_start(args, msg);
5430                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
5431                         buf[sizeof(buf) - 1] = 0;
5432                         buf[sizeof(buf) - 2] = '.';
5433                         buf[sizeof(buf) - 3] = '.';
5434                         buf[sizeof(buf) - 4] = '.';
5435                 }
5436                 va_end(args);
5437                 die("%s", buf);
5438         }
5439
5440         if (!status_empty || *msg) {
5441                 va_list args;
5442
5443                 va_start(args, msg);
5444
5445                 wmove(status_win, 0, 0);
5446                 if (*msg) {
5447                         vwprintw(status_win, msg, args);
5448                         status_empty = FALSE;
5449                 } else {
5450                         status_empty = TRUE;
5451                 }
5452                 wclrtoeol(status_win);
5453                 wrefresh(status_win);
5454
5455                 va_end(args);
5456         }
5457
5458         update_view_title(view);
5459         update_display_cursor(view);
5460 }
5461
5462 /* Controls when nodelay should be in effect when polling user input. */
5463 static void
5464 set_nonblocking_input(bool loading)
5465 {
5466         static unsigned int loading_views;
5467
5468         if ((loading == FALSE && loading_views-- == 1) ||
5469             (loading == TRUE  && loading_views++ == 0))
5470                 nodelay(status_win, loading);
5471 }
5472
5473 static void
5474 init_display(void)
5475 {
5476         int x, y;
5477
5478         /* Initialize the curses library */
5479         if (isatty(STDIN_FILENO)) {
5480                 cursed = !!initscr();
5481         } else {
5482                 /* Leave stdin and stdout alone when acting as a pager. */
5483                 FILE *io = fopen("/dev/tty", "r+");
5484
5485                 if (!io)
5486                         die("Failed to open /dev/tty");
5487                 cursed = !!newterm(NULL, io, io);
5488         }
5489
5490         if (!cursed)
5491                 die("Failed to initialize curses");
5492
5493         nonl();         /* Tell curses not to do NL->CR/NL on output */
5494         cbreak();       /* Take input chars one at a time, no wait for \n */
5495         noecho();       /* Don't echo input */
5496         leaveok(stdscr, TRUE);
5497
5498         if (has_colors())
5499                 init_colors();
5500
5501         getmaxyx(stdscr, y, x);
5502         status_win = newwin(1, 0, y - 1, 0);
5503         if (!status_win)
5504                 die("Failed to create status window");
5505
5506         /* Enable keyboard mapping */
5507         keypad(status_win, TRUE);
5508         wbkgdset(status_win, get_line_attr(LINE_STATUS));
5509
5510         TABSIZE = opt_tab_size;
5511         if (opt_line_graphics) {
5512                 line_graphics[LINE_GRAPHIC_VLINE] = ACS_VLINE;
5513         }
5514 }
5515
5516 static bool
5517 prompt_yesno(const char *prompt)
5518 {
5519         enum { WAIT, STOP, CANCEL  } status = WAIT;
5520         bool answer = FALSE;
5521
5522         while (status == WAIT) {
5523                 struct view *view;
5524                 int i, key;
5525
5526                 input_mode = TRUE;
5527
5528                 foreach_view (view, i)
5529                         update_view(view);
5530
5531                 input_mode = FALSE;
5532
5533                 mvwprintw(status_win, 0, 0, "%s [Yy]/[Nn]", prompt);
5534                 wclrtoeol(status_win);
5535
5536                 /* Refresh, accept single keystroke of input */
5537                 key = wgetch(status_win);
5538                 switch (key) {
5539                 case ERR:
5540                         break;
5541
5542                 case 'y':
5543                 case 'Y':
5544                         answer = TRUE;
5545                         status = STOP;
5546                         break;
5547
5548                 case KEY_ESC:
5549                 case KEY_RETURN:
5550                 case KEY_ENTER:
5551                 case KEY_BACKSPACE:
5552                 case 'n':
5553                 case 'N':
5554                 case '\n':
5555                 default:
5556                         answer = FALSE;
5557                         status = CANCEL;
5558                 }
5559         }
5560
5561         /* Clear the status window */
5562         status_empty = FALSE;
5563         report("");
5564
5565         return answer;
5566 }
5567
5568 static char *
5569 read_prompt(const char *prompt)
5570 {
5571         enum { READING, STOP, CANCEL } status = READING;
5572         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
5573         int pos = 0;
5574
5575         while (status == READING) {
5576                 struct view *view;
5577                 int i, key;
5578
5579                 input_mode = TRUE;
5580
5581                 foreach_view (view, i)
5582                         update_view(view);
5583
5584                 input_mode = FALSE;
5585
5586                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
5587                 wclrtoeol(status_win);
5588
5589                 /* Refresh, accept single keystroke of input */
5590                 key = wgetch(status_win);
5591                 switch (key) {
5592                 case KEY_RETURN:
5593                 case KEY_ENTER:
5594                 case '\n':
5595                         status = pos ? STOP : CANCEL;
5596                         break;
5597
5598                 case KEY_BACKSPACE:
5599                         if (pos > 0)
5600                                 pos--;
5601                         else
5602                                 status = CANCEL;
5603                         break;
5604
5605                 case KEY_ESC:
5606                         status = CANCEL;
5607                         break;
5608
5609                 case ERR:
5610                         break;
5611
5612                 default:
5613                         if (pos >= sizeof(buf)) {
5614                                 report("Input string too long");
5615                                 return NULL;
5616                         }
5617
5618                         if (isprint(key))
5619                                 buf[pos++] = (char) key;
5620                 }
5621         }
5622
5623         /* Clear the status window */
5624         status_empty = FALSE;
5625         report("");
5626
5627         if (status == CANCEL)
5628                 return NULL;
5629
5630         buf[pos++] = 0;
5631
5632         return buf;
5633 }
5634
5635 /*
5636  * Repository references
5637  */
5638
5639 static struct ref *refs = NULL;
5640 static size_t refs_alloc = 0;
5641 static size_t refs_size = 0;
5642
5643 /* Id <-> ref store */
5644 static struct ref ***id_refs = NULL;
5645 static size_t id_refs_alloc = 0;
5646 static size_t id_refs_size = 0;
5647
5648 static struct ref **
5649 get_refs(char *id)
5650 {
5651         struct ref ***tmp_id_refs;
5652         struct ref **ref_list = NULL;
5653         size_t ref_list_alloc = 0;
5654         size_t ref_list_size = 0;
5655         size_t i;
5656
5657         for (i = 0; i < id_refs_size; i++)
5658                 if (!strcmp(id, id_refs[i][0]->id))
5659                         return id_refs[i];
5660
5661         tmp_id_refs = realloc_items(id_refs, &id_refs_alloc, id_refs_size + 1,
5662                                     sizeof(*id_refs));
5663         if (!tmp_id_refs)
5664                 return NULL;
5665
5666         id_refs = tmp_id_refs;
5667
5668         for (i = 0; i < refs_size; i++) {
5669                 struct ref **tmp;
5670
5671                 if (strcmp(id, refs[i].id))
5672                         continue;
5673
5674                 tmp = realloc_items(ref_list, &ref_list_alloc,
5675                                     ref_list_size + 1, sizeof(*ref_list));
5676                 if (!tmp) {
5677                         if (ref_list)
5678                                 free(ref_list);
5679                         return NULL;
5680                 }
5681
5682                 ref_list = tmp;
5683                 if (ref_list_size > 0)
5684                         ref_list[ref_list_size - 1]->next = 1;
5685                 ref_list[ref_list_size] = &refs[i];
5686
5687                 /* XXX: The properties of the commit chains ensures that we can
5688                  * safely modify the shared ref. The repo references will
5689                  * always be similar for the same id. */
5690                 ref_list[ref_list_size]->next = 0;
5691                 ref_list_size++;
5692         }
5693
5694         if (ref_list)
5695                 id_refs[id_refs_size++] = ref_list;
5696
5697         return ref_list;
5698 }
5699
5700 static int
5701 read_ref(char *id, size_t idlen, char *name, size_t namelen)
5702 {
5703         struct ref *ref;
5704         bool tag = FALSE;
5705         bool ltag = FALSE;
5706         bool remote = FALSE;
5707         bool tracked = FALSE;
5708         bool check_replace = FALSE;
5709         bool head = FALSE;
5710
5711         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
5712                 if (!strcmp(name + namelen - 3, "^{}")) {
5713                         namelen -= 3;
5714                         name[namelen] = 0;
5715                         if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
5716                                 check_replace = TRUE;
5717                 } else {
5718                         ltag = TRUE;
5719                 }
5720
5721                 tag = TRUE;
5722                 namelen -= STRING_SIZE("refs/tags/");
5723                 name    += STRING_SIZE("refs/tags/");
5724
5725         } else if (!strncmp(name, "refs/remotes/", STRING_SIZE("refs/remotes/"))) {
5726                 remote = TRUE;
5727                 namelen -= STRING_SIZE("refs/remotes/");
5728                 name    += STRING_SIZE("refs/remotes/");
5729                 tracked  = !strcmp(opt_remote, name);
5730
5731         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
5732                 namelen -= STRING_SIZE("refs/heads/");
5733                 name    += STRING_SIZE("refs/heads/");
5734                 head     = !strncmp(opt_head, name, namelen);
5735
5736         } else if (!strcmp(name, "HEAD")) {
5737                 opt_no_head = FALSE;
5738                 return OK;
5739         }
5740
5741         if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
5742                 /* it's an annotated tag, replace the previous sha1 with the
5743                  * resolved commit id; relies on the fact git-ls-remote lists
5744                  * the commit id of an annotated tag right beofre the commit id
5745                  * it points to. */
5746                 refs[refs_size - 1].ltag = ltag;
5747                 string_copy_rev(refs[refs_size - 1].id, id);
5748
5749                 return OK;
5750         }
5751         refs = realloc_items(refs, &refs_alloc, refs_size + 1, sizeof(*refs));
5752         if (!refs)
5753                 return ERR;
5754
5755         ref = &refs[refs_size++];
5756         ref->name = malloc(namelen + 1);
5757         if (!ref->name)
5758                 return ERR;
5759
5760         strncpy(ref->name, name, namelen);
5761         ref->name[namelen] = 0;
5762         ref->head = head;
5763         ref->tag = tag;
5764         ref->ltag = ltag;
5765         ref->remote = remote;
5766         ref->tracked = tracked;
5767         string_copy_rev(ref->id, id);
5768
5769         return OK;
5770 }
5771
5772 static int
5773 load_refs(void)
5774 {
5775         const char *cmd_env = getenv("TIG_LS_REMOTE");
5776         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
5777
5778         return read_properties(popen(cmd, "r"), "\t", read_ref);
5779 }
5780
5781 static int
5782 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
5783 {
5784         if (!strcmp(name, "i18n.commitencoding"))
5785                 string_ncopy(opt_encoding, value, valuelen);
5786
5787         if (!strcmp(name, "core.editor"))
5788                 string_ncopy(opt_editor, value, valuelen);
5789
5790         /* branch.<head>.remote */
5791         if (*opt_head &&
5792             !strncmp(name, "branch.", 7) &&
5793             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
5794             !strcmp(name + 7 + strlen(opt_head), ".remote"))
5795                 string_ncopy(opt_remote, value, valuelen);
5796
5797         if (*opt_head && *opt_remote &&
5798             !strncmp(name, "branch.", 7) &&
5799             !strncmp(name + 7, opt_head, strlen(opt_head)) &&
5800             !strcmp(name + 7 + strlen(opt_head), ".merge")) {
5801                 size_t from = strlen(opt_remote);
5802
5803                 if (!strncmp(value, "refs/heads/", STRING_SIZE("refs/heads/"))) {
5804                         value += STRING_SIZE("refs/heads/");
5805                         valuelen -= STRING_SIZE("refs/heads/");
5806                 }
5807
5808                 if (!string_format_from(opt_remote, &from, "/%s", value))
5809                         opt_remote[0] = 0;
5810         }
5811
5812         return OK;
5813 }
5814
5815 static int
5816 load_git_config(void)
5817 {
5818         return read_properties(popen(GIT_CONFIG " --list", "r"),
5819                                "=", read_repo_config_option);
5820 }
5821
5822 static int
5823 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
5824 {
5825         if (!opt_git_dir[0]) {
5826                 string_ncopy(opt_git_dir, name, namelen);
5827
5828         } else if (opt_is_inside_work_tree == -1) {
5829                 /* This can be 3 different values depending on the
5830                  * version of git being used. If git-rev-parse does not
5831                  * understand --is-inside-work-tree it will simply echo
5832                  * the option else either "true" or "false" is printed.
5833                  * Default to true for the unknown case. */
5834                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
5835
5836         } else if (opt_cdup[0] == ' ') {
5837                 string_ncopy(opt_cdup, name, namelen);
5838         } else {
5839                 if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
5840                         namelen -= STRING_SIZE("refs/heads/");
5841                         name    += STRING_SIZE("refs/heads/");
5842                         string_ncopy(opt_head, name, namelen);
5843                 }
5844         }
5845
5846         return OK;
5847 }
5848
5849 static int
5850 load_repo_info(void)
5851 {
5852         int result;
5853         FILE *pipe = popen("(git rev-parse --git-dir --is-inside-work-tree "
5854                            " --show-cdup; git symbolic-ref HEAD) 2>/dev/null", "r");
5855
5856         /* XXX: The line outputted by "--show-cdup" can be empty so
5857          * initialize it to something invalid to make it possible to
5858          * detect whether it has been set or not. */
5859         opt_cdup[0] = ' ';
5860
5861         result = read_properties(pipe, "=", read_repo_info);
5862         if (opt_cdup[0] == ' ')
5863                 opt_cdup[0] = 0;
5864
5865         return result;
5866 }
5867
5868 static int
5869 read_properties(FILE *pipe, const char *separators,
5870                 int (*read_property)(char *, size_t, char *, size_t))
5871 {
5872         char buffer[BUFSIZ];
5873         char *name;
5874         int state = OK;
5875
5876         if (!pipe)
5877                 return ERR;
5878
5879         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
5880                 char *value;
5881                 size_t namelen;
5882                 size_t valuelen;
5883
5884                 name = chomp_string(name);
5885                 namelen = strcspn(name, separators);
5886
5887                 if (name[namelen]) {
5888                         name[namelen] = 0;
5889                         value = chomp_string(name + namelen + 1);
5890                         valuelen = strlen(value);
5891
5892                 } else {
5893                         value = "";
5894                         valuelen = 0;
5895                 }
5896
5897                 state = read_property(name, namelen, value, valuelen);
5898         }
5899
5900         if (state != ERR && ferror(pipe))
5901                 state = ERR;
5902
5903         pclose(pipe);
5904
5905         return state;
5906 }
5907
5908
5909 /*
5910  * Main
5911  */
5912
5913 static void __NORETURN
5914 quit(int sig)
5915 {
5916         /* XXX: Restore tty modes and let the OS cleanup the rest! */
5917         if (cursed)
5918                 endwin();
5919         exit(0);
5920 }
5921
5922 static void __NORETURN
5923 die(const char *err, ...)
5924 {
5925         va_list args;
5926
5927         endwin();
5928
5929         va_start(args, err);
5930         fputs("tig: ", stderr);
5931         vfprintf(stderr, err, args);
5932         fputs("\n", stderr);
5933         va_end(args);
5934
5935         exit(1);
5936 }
5937
5938 static void
5939 warn(const char *msg, ...)
5940 {
5941         va_list args;
5942
5943         va_start(args, msg);
5944         fputs("tig warning: ", stderr);
5945         vfprintf(stderr, msg, args);
5946         fputs("\n", stderr);
5947         va_end(args);
5948 }
5949
5950 int
5951 main(int argc, char *argv[])
5952 {
5953         struct view *view;
5954         enum request request;
5955         size_t i;
5956
5957         signal(SIGINT, quit);
5958
5959         if (setlocale(LC_ALL, "")) {
5960                 char *codeset = nl_langinfo(CODESET);
5961
5962                 string_ncopy(opt_codeset, codeset, strlen(codeset));
5963         }
5964
5965         if (load_repo_info() == ERR)
5966                 die("Failed to load repo info.");
5967
5968         if (load_options() == ERR)
5969                 die("Failed to load user config.");
5970
5971         if (load_git_config() == ERR)
5972                 die("Failed to load repo config.");
5973
5974         request = parse_options(argc, argv);
5975         if (request == REQ_NONE)
5976                 return 0;
5977
5978         /* Require a git repository unless when running in pager mode. */
5979         if (!opt_git_dir[0] && request != REQ_VIEW_PAGER)
5980                 die("Not a git repository");
5981
5982         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
5983                 opt_utf8 = FALSE;
5984
5985         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
5986                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
5987                 if (opt_iconv == ICONV_NONE)
5988                         die("Failed to initialize character set conversion");
5989         }
5990
5991         if (*opt_git_dir && load_refs() == ERR)
5992                 die("Failed to load refs.");
5993
5994         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
5995                 view->cmd_env = getenv(view->cmd_env);
5996
5997         init_display();
5998
5999         while (view_driver(display[current_view], request)) {
6000                 int key;
6001                 int i;
6002
6003                 foreach_view (view, i)
6004                         update_view(view);
6005
6006                 /* Refresh, accept single keystroke of input */
6007                 key = wgetch(status_win);
6008
6009                 /* wgetch() with nodelay() enabled returns ERR when there's no
6010                  * input. */
6011                 if (key == ERR) {
6012                         request = REQ_NONE;
6013                         continue;
6014                 }
6015
6016                 request = get_keybinding(display[current_view]->keymap, key);
6017
6018                 /* Some low-level request handling. This keeps access to
6019                  * status_win restricted. */
6020                 switch (request) {
6021                 case REQ_PROMPT:
6022                 {
6023                         char *cmd = read_prompt(":");
6024
6025                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
6026                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
6027                                         request = REQ_VIEW_DIFF;
6028                                 } else {
6029                                         request = REQ_VIEW_PAGER;
6030                                 }
6031
6032                                 /* Always reload^Wrerun commands from the prompt. */
6033                                 open_view(view, request, OPEN_RELOAD);
6034                         }
6035
6036                         request = REQ_NONE;
6037                         break;
6038                 }
6039                 case REQ_SEARCH:
6040                 case REQ_SEARCH_BACK:
6041                 {
6042                         const char *prompt = request == REQ_SEARCH
6043                                            ? "/" : "?";
6044                         char *search = read_prompt(prompt);
6045
6046                         if (search)
6047                                 string_ncopy(opt_search, search, strlen(search));
6048                         else
6049                                 request = REQ_NONE;
6050                         break;
6051                 }
6052                 case REQ_SCREEN_RESIZE:
6053                 {
6054                         int height, width;
6055
6056                         getmaxyx(stdscr, height, width);
6057
6058                         /* Resize the status view and let the view driver take
6059                          * care of resizing the displayed views. */
6060                         wresize(status_win, 1, width);
6061                         mvwin(status_win, height - 1, 0);
6062                         wrefresh(status_win);
6063                         break;
6064                 }
6065                 default:
6066                         break;
6067                 }
6068         }
6069
6070         quit(0);
6071
6072         return 0;
6073 }