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