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