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