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