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