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