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