Add support for preparing the initial commit in the status view
[tig] / tig.c
1 /* Copyright (c) 2006-2008 Jonas Fonseca <fonseca@diku.dk>
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU General Public License as
5  * published by the Free Software Foundation; either version 2 of
6  * the License, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17
18 #ifndef TIG_VERSION
19 #define TIG_VERSION "unknown-version"
20 #endif
21
22 #ifndef DEBUG
23 #define NDEBUG
24 #endif
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <signal.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <time.h>
38
39 #include <regex.h>
40
41 #include <locale.h>
42 #include <langinfo.h>
43 #include <iconv.h>
44
45 /* ncurses(3): Must be defined to have extended wide-character functions. */
46 #define _XOPEN_SOURCE_EXTENDED
47
48 #include <curses.h>
49
50 #if __GNUC__ >= 3
51 #define __NORETURN __attribute__((__noreturn__))
52 #else
53 #define __NORETURN
54 #endif
55
56 static void __NORETURN die(const char *err, ...);
57 static void warn(const char *msg, ...);
58 static void report(const char *msg, ...);
59 static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, size_t, char *, size_t));
60 static void set_nonblocking_input(bool loading);
61 static size_t utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve);
62
63 #define ABS(x)          ((x) >= 0  ? (x) : -(x))
64 #define MIN(x, y)       ((x) < (y) ? (x) :  (y))
65
66 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))
67 #define STRING_SIZE(x)  (sizeof(x) - 1)
68
69 #define SIZEOF_STR      1024    /* Default string size. */
70 #define SIZEOF_REF      256     /* Size of symbolic or SHA1 ID. */
71 #define SIZEOF_REV      41      /* Holds a SHA-1 and an ending NUL */
72
73 /* Revision graph */
74
75 #define REVGRAPH_INIT   'I'
76 #define REVGRAPH_MERGE  'M'
77 #define REVGRAPH_BRANCH '+'
78 #define REVGRAPH_COMMIT '*'
79 #define REVGRAPH_BOUND  '^'
80 #define REVGRAPH_LINE   '|'
81
82 #define SIZEOF_REVGRAPH 19      /* Size of revision ancestry graphics. */
83
84 /* This color name can be used to refer to the default term colors. */
85 #define COLOR_DEFAULT   (-1)
86
87 #define ICONV_NONE      ((iconv_t) -1)
88 #ifndef ICONV_CONST
89 #define ICONV_CONST     /* nothing */
90 #endif
91
92 /* The format and size of the date column in the main view. */
93 #define DATE_FORMAT     "%Y-%m-%d %H:%M"
94 #define DATE_COLS       STRING_SIZE("2006-04-29 14:21 ")
95
96 #define AUTHOR_COLS     20
97 #define ID_COLS         8
98
99 /* The default interval between line numbers. */
100 #define NUMBER_INTERVAL 5
101
102 #define TABSIZE         8
103
104 #define SCALE_SPLIT_VIEW(height)        ((height) * 2 / 3)
105
106 #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(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 void
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;
4212                 }
4213
4214         } else if (!status_update_file(view, line->data, line->type)) {
4215                 report("Failed to update file status");
4216         }
4217 }
4218
4219 static enum request
4220 status_request(struct view *view, enum request request, struct line *line)
4221 {
4222         struct status *status = line->data;
4223
4224         switch (request) {
4225         case REQ_STATUS_UPDATE:
4226                 status_update(view);
4227                 break;
4228
4229         case REQ_STATUS_MERGE:
4230                 if (!status || status->status != 'U') {
4231                         report("Merging only possible for files with unmerged status ('U').");
4232                         return REQ_NONE;
4233                 }
4234                 open_mergetool(status->new.name);
4235                 break;
4236
4237         case REQ_EDIT:
4238                 if (!status)
4239                         return request;
4240
4241                 open_editor(status->status != '?', status->new.name);
4242                 break;
4243
4244         case REQ_VIEW_BLAME:
4245                 if (status) {
4246                         string_copy(opt_file, status->new.name);
4247                         opt_ref[0] = 0;
4248                 }
4249                 return request;
4250
4251         case REQ_ENTER:
4252                 /* After returning the status view has been split to
4253                  * show the stage view. No further reloading is
4254                  * necessary. */
4255                 status_enter(view, line);
4256                 return REQ_NONE;
4257
4258         case REQ_REFRESH:
4259                 /* Simply reload the view. */
4260                 break;
4261
4262         default:
4263                 return request;
4264         }
4265
4266         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
4267
4268         return REQ_NONE;
4269 }
4270
4271 static void
4272 status_select(struct view *view, struct line *line)
4273 {
4274         struct status *status = line->data;
4275         char file[SIZEOF_STR] = "all files";
4276         char *text;
4277         char *key;
4278
4279         if (status && !string_format(file, "'%s'", status->new.name))
4280                 return;
4281
4282         if (!status && line[1].type == LINE_STAT_NONE)
4283                 line++;
4284
4285         switch (line->type) {
4286         case LINE_STAT_STAGED:
4287                 text = "Press %s to unstage %s for commit";
4288                 break;
4289
4290         case LINE_STAT_UNSTAGED:
4291                 text = "Press %s to stage %s for commit";
4292                 break;
4293
4294         case LINE_STAT_UNTRACKED:
4295                 text = "Press %s to stage %s for addition";
4296                 break;
4297
4298         case LINE_STAT_HEAD:
4299         case LINE_STAT_NONE:
4300                 text = "Nothing to update";
4301                 break;
4302
4303         default:
4304                 die("line type %d not handled in switch", line->type);
4305         }
4306
4307         if (status && status->status == 'U') {
4308                 text = "Press %s to resolve conflict in %s";
4309                 key = get_key(REQ_STATUS_MERGE);
4310
4311         } else {
4312                 key = get_key(REQ_STATUS_UPDATE);
4313         }
4314
4315         string_format(view->ref, text, key, file);
4316 }
4317
4318 static bool
4319 status_grep(struct view *view, struct line *line)
4320 {
4321         struct status *status = line->data;
4322         enum { S_STATUS, S_NAME, S_END } state;
4323         char buf[2] = "?";
4324         regmatch_t pmatch;
4325
4326         if (!status)
4327                 return FALSE;
4328
4329         for (state = S_STATUS; state < S_END; state++) {
4330                 char *text;
4331
4332                 switch (state) {
4333                 case S_NAME:    text = status->new.name;        break;
4334                 case S_STATUS:
4335                         buf[0] = status->status;
4336                         text = buf;
4337                         break;
4338
4339                 default:
4340                         return FALSE;
4341                 }
4342
4343                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4344                         return TRUE;
4345         }
4346
4347         return FALSE;
4348 }
4349
4350 static struct view_ops status_ops = {
4351         "file",
4352         status_open,
4353         NULL,
4354         status_draw,
4355         status_request,
4356         status_grep,
4357         status_select,
4358 };
4359
4360
4361 static bool
4362 stage_diff_line(FILE *pipe, struct line *line)
4363 {
4364         char *buf = line->data;
4365         size_t bufsize = strlen(buf);
4366         size_t written = 0;
4367
4368         while (!ferror(pipe) && written < bufsize) {
4369                 written += fwrite(buf + written, 1, bufsize - written, pipe);
4370         }
4371
4372         fputc('\n', pipe);
4373
4374         return written == bufsize;
4375 }
4376
4377 static struct line *
4378 stage_diff_hdr(struct view *view, struct line *line)
4379 {
4380         int diff_hdr_dir = line->type == LINE_DIFF_CHUNK ? -1 : 1;
4381         struct line *diff_hdr;
4382
4383         if (line->type == LINE_DIFF_CHUNK)
4384                 diff_hdr = line - 1;
4385         else
4386                 diff_hdr = view->line + 1;
4387
4388         while (diff_hdr > view->line && diff_hdr < view->line + view->lines) {
4389                 if (diff_hdr->type == LINE_DIFF_HEADER)
4390                         return diff_hdr;
4391
4392                 diff_hdr += diff_hdr_dir;
4393         }
4394
4395         return NULL;
4396 }
4397
4398 static bool
4399 stage_update_chunk(struct view *view, struct line *line)
4400 {
4401         char cmd[SIZEOF_STR];
4402         size_t cmdsize = 0;
4403         struct line *diff_hdr, *diff_chunk, *diff_end;
4404         FILE *pipe;
4405
4406         diff_hdr = stage_diff_hdr(view, line);
4407         if (!diff_hdr)
4408                 return FALSE;
4409
4410         if (opt_cdup[0] &&
4411             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
4412                 return FALSE;
4413
4414         if (!string_format_from(cmd, &cmdsize,
4415                                 "git apply --cached %s - && "
4416                                 "git update-index -q --unmerged --refresh 2>/dev/null",
4417                                 stage_line_type == LINE_STAT_STAGED ? "-R" : ""))
4418                 return FALSE;
4419
4420         pipe = popen(cmd, "w");
4421         if (!pipe)
4422                 return FALSE;
4423
4424         diff_end = view->line + view->lines;
4425         if (line->type != LINE_DIFF_CHUNK) {
4426                 diff_chunk = diff_hdr;
4427
4428         } else {
4429                 for (diff_chunk = line + 1; diff_chunk < diff_end; diff_chunk++)
4430                         if (diff_chunk->type == LINE_DIFF_CHUNK ||
4431                             diff_chunk->type == LINE_DIFF_HEADER)
4432                                 diff_end = diff_chunk;
4433
4434                 diff_chunk = line;
4435
4436                 while (diff_hdr->type != LINE_DIFF_CHUNK) {
4437                         switch (diff_hdr->type) {
4438                         case LINE_DIFF_HEADER:
4439                         case LINE_DIFF_INDEX:
4440                         case LINE_DIFF_ADD:
4441                         case LINE_DIFF_DEL:
4442                                 break;
4443
4444                         default:
4445                                 diff_hdr++;
4446                                 continue;
4447                         }
4448
4449                         if (!stage_diff_line(pipe, diff_hdr++)) {
4450                                 pclose(pipe);
4451                                 return FALSE;
4452                         }
4453                 }
4454         }
4455
4456         while (diff_chunk < diff_end && stage_diff_line(pipe, diff_chunk))
4457                 diff_chunk++;
4458
4459         pclose(pipe);
4460
4461         if (diff_chunk != diff_end)
4462                 return FALSE;
4463
4464         return TRUE;
4465 }
4466
4467 static void
4468 stage_update(struct view *view, struct line *line)
4469 {
4470         if (!opt_no_head && stage_line_type != LINE_STAT_UNTRACKED &&
4471             (line->type == LINE_DIFF_CHUNK || !stage_status.status)) {
4472                 if (!stage_update_chunk(view, line)) {
4473                         report("Failed to apply chunk");
4474                         return;
4475                 }
4476
4477         } else if (!status_update_file(view, &stage_status, stage_line_type)) {
4478                 report("Failed to update file");
4479                 return;
4480         }
4481
4482         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
4483
4484         view = VIEW(REQ_VIEW_STATUS);
4485         if (view_is_displayed(view))
4486                 status_enter(view, &view->line[view->lineno]);
4487 }
4488
4489 static enum request
4490 stage_request(struct view *view, enum request request, struct line *line)
4491 {
4492         switch (request) {
4493         case REQ_STATUS_UPDATE:
4494                 stage_update(view, line);
4495                 break;
4496
4497         case REQ_EDIT:
4498                 if (!stage_status.new.name[0])
4499                         return request;
4500
4501                 open_editor(stage_status.status != '?', stage_status.new.name);
4502                 break;
4503
4504         case REQ_VIEW_BLAME:
4505                 if (stage_status.new.name[0]) {
4506                         string_copy(opt_file, stage_status.new.name);
4507                         opt_ref[0] = 0;
4508                 }
4509                 return request;
4510
4511         case REQ_ENTER:
4512                 pager_request(view, request, line);
4513                 break;
4514
4515         default:
4516                 return request;
4517         }
4518
4519         return REQ_NONE;
4520 }
4521
4522 static struct view_ops stage_ops = {
4523         "line",
4524         NULL,
4525         pager_read,
4526         pager_draw,
4527         stage_request,
4528         pager_grep,
4529         pager_select,
4530 };
4531
4532
4533 /*
4534  * Revision graph
4535  */
4536
4537 struct commit {
4538         char id[SIZEOF_REV];            /* SHA1 ID. */
4539         char title[128];                /* First line of the commit message. */
4540         char author[75];                /* Author of the commit. */
4541         struct tm time;                 /* Date from the author ident. */
4542         struct ref **refs;              /* Repository references. */
4543         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
4544         size_t graph_size;              /* The width of the graph array. */
4545         bool has_parents;               /* Rewritten --parents seen. */
4546 };
4547
4548 /* Size of rev graph with no  "padding" columns */
4549 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
4550
4551 struct rev_graph {
4552         struct rev_graph *prev, *next, *parents;
4553         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
4554         size_t size;
4555         struct commit *commit;
4556         size_t pos;
4557         unsigned int boundary:1;
4558 };
4559
4560 /* Parents of the commit being visualized. */
4561 static struct rev_graph graph_parents[4];
4562
4563 /* The current stack of revisions on the graph. */
4564 static struct rev_graph graph_stacks[4] = {
4565         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
4566         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
4567         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
4568         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
4569 };
4570
4571 static inline bool
4572 graph_parent_is_merge(struct rev_graph *graph)
4573 {
4574         return graph->parents->size > 1;
4575 }
4576
4577 static inline void
4578 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
4579 {
4580         struct commit *commit = graph->commit;
4581
4582         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
4583                 commit->graph[commit->graph_size++] = symbol;
4584 }
4585
4586 static void
4587 done_rev_graph(struct rev_graph *graph)
4588 {
4589         if (graph_parent_is_merge(graph) &&
4590             graph->pos < graph->size - 1 &&
4591             graph->next->size == graph->size + graph->parents->size - 1) {
4592                 size_t i = graph->pos + graph->parents->size - 1;
4593
4594                 graph->commit->graph_size = i * 2;
4595                 while (i < graph->next->size - 1) {
4596                         append_to_rev_graph(graph, ' ');
4597                         append_to_rev_graph(graph, '\\');
4598                         i++;
4599                 }
4600         }
4601
4602         graph->size = graph->pos = 0;
4603         graph->commit = NULL;
4604         memset(graph->parents, 0, sizeof(*graph->parents));
4605 }
4606
4607 static void
4608 push_rev_graph(struct rev_graph *graph, char *parent)
4609 {
4610         int i;
4611
4612         /* "Collapse" duplicate parents lines.
4613          *
4614          * FIXME: This needs to also update update the drawn graph but
4615          * for now it just serves as a method for pruning graph lines. */
4616         for (i = 0; i < graph->size; i++)
4617                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
4618                         return;
4619
4620         if (graph->size < SIZEOF_REVITEMS) {
4621                 string_copy_rev(graph->rev[graph->size++], parent);
4622         }
4623 }
4624
4625 static chtype
4626 get_rev_graph_symbol(struct rev_graph *graph)
4627 {
4628         chtype symbol;
4629
4630         if (graph->boundary)
4631                 symbol = REVGRAPH_BOUND;
4632         else if (graph->parents->size == 0)
4633                 symbol = REVGRAPH_INIT;
4634         else if (graph_parent_is_merge(graph))
4635                 symbol = REVGRAPH_MERGE;
4636         else if (graph->pos >= graph->size)
4637                 symbol = REVGRAPH_BRANCH;
4638         else
4639                 symbol = REVGRAPH_COMMIT;
4640
4641         return symbol;
4642 }
4643
4644 static void
4645 draw_rev_graph(struct rev_graph *graph)
4646 {
4647         struct rev_filler {
4648                 chtype separator, line;
4649         };
4650         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
4651         static struct rev_filler fillers[] = {
4652                 { ' ',  REVGRAPH_LINE },
4653                 { '`',  '.' },
4654                 { '\'', ' ' },
4655                 { '/',  ' ' },
4656         };
4657         chtype symbol = get_rev_graph_symbol(graph);
4658         struct rev_filler *filler;
4659         size_t i;
4660
4661         filler = &fillers[DEFAULT];
4662
4663         for (i = 0; i < graph->pos; i++) {
4664                 append_to_rev_graph(graph, filler->line);
4665                 if (graph_parent_is_merge(graph->prev) &&
4666                     graph->prev->pos == i)
4667                         filler = &fillers[RSHARP];
4668
4669                 append_to_rev_graph(graph, filler->separator);
4670         }
4671
4672         /* Place the symbol for this revision. */
4673         append_to_rev_graph(graph, symbol);
4674
4675         if (graph->prev->size > graph->size)
4676                 filler = &fillers[RDIAG];
4677         else
4678                 filler = &fillers[DEFAULT];
4679
4680         i++;
4681
4682         for (; i < graph->size; i++) {
4683                 append_to_rev_graph(graph, filler->separator);
4684                 append_to_rev_graph(graph, filler->line);
4685                 if (graph_parent_is_merge(graph->prev) &&
4686                     i < graph->prev->pos + graph->parents->size)
4687                         filler = &fillers[RSHARP];
4688                 if (graph->prev->size > graph->size)
4689                         filler = &fillers[LDIAG];
4690         }
4691
4692         if (graph->prev->size > graph->size) {
4693                 append_to_rev_graph(graph, filler->separator);
4694                 if (filler->line != ' ')
4695                         append_to_rev_graph(graph, filler->line);
4696         }
4697 }
4698
4699 /* Prepare the next rev graph */
4700 static void
4701 prepare_rev_graph(struct rev_graph *graph)
4702 {
4703         size_t i;
4704
4705         /* First, traverse all lines of revisions up to the active one. */
4706         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
4707                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
4708                         break;
4709
4710                 push_rev_graph(graph->next, graph->rev[graph->pos]);
4711         }
4712
4713         /* Interleave the new revision parent(s). */
4714         for (i = 0; !graph->boundary && i < graph->parents->size; i++)
4715                 push_rev_graph(graph->next, graph->parents->rev[i]);
4716
4717         /* Lastly, put any remaining revisions. */
4718         for (i = graph->pos + 1; i < graph->size; i++)
4719                 push_rev_graph(graph->next, graph->rev[i]);
4720 }
4721
4722 static void
4723 update_rev_graph(struct rev_graph *graph)
4724 {
4725         /* If this is the finalizing update ... */
4726         if (graph->commit)
4727                 prepare_rev_graph(graph);
4728
4729         /* Graph visualization needs a one rev look-ahead,
4730          * so the first update doesn't visualize anything. */
4731         if (!graph->prev->commit)
4732                 return;
4733
4734         draw_rev_graph(graph->prev);
4735         done_rev_graph(graph->prev->prev);
4736 }
4737
4738
4739 /*
4740  * Main view backend
4741  */
4742
4743 static bool
4744 main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
4745 {
4746         char buf[DATE_COLS + 1];
4747         struct commit *commit = line->data;
4748         enum line_type type;
4749         int col = 0;
4750         size_t timelen;
4751         int space;
4752
4753         if (!*commit->author)
4754                 return FALSE;
4755
4756         space = view->width;
4757         wmove(view->win, lineno, col);
4758
4759         if (selected) {
4760                 type = LINE_CURSOR;
4761                 wattrset(view->win, get_line_attr(type));
4762                 wchgat(view->win, -1, 0, type, NULL);
4763         } else {
4764                 type = LINE_MAIN_COMMIT;
4765                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
4766         }
4767
4768         if (opt_date) {
4769                 int n;
4770
4771                 timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
4772                 n = draw_text(view, buf, view->width - col, FALSE, selected);
4773                 draw_text(view, " ", view->width - col - n, FALSE, selected);
4774
4775                 col += DATE_COLS;
4776                 wmove(view->win, lineno, col);
4777                 if (col >= view->width)
4778                         return TRUE;
4779         }
4780         if (type != LINE_CURSOR)
4781                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
4782
4783         if (opt_author) {
4784                 int max_len;
4785
4786                 max_len = view->width - col;
4787                 if (max_len > AUTHOR_COLS - 1)
4788                         max_len = AUTHOR_COLS - 1;
4789                 draw_text(view, commit->author, max_len, TRUE, selected);
4790                 col += AUTHOR_COLS;
4791                 if (col >= view->width)
4792                         return TRUE;
4793         }
4794
4795         if (opt_rev_graph && commit->graph_size) {
4796                 size_t graph_size = view->width - col;
4797                 size_t i;
4798
4799                 if (type != LINE_CURSOR)
4800                         wattrset(view->win, get_line_attr(LINE_MAIN_REVGRAPH));
4801                 wmove(view->win, lineno, col);
4802                 if (graph_size > commit->graph_size)
4803                         graph_size = commit->graph_size;
4804                 /* Using waddch() instead of waddnstr() ensures that
4805                  * they'll be rendered correctly for the cursor line. */
4806                 for (i = 0; i < graph_size; i++)
4807                         waddch(view->win, commit->graph[i]);
4808
4809                 col += commit->graph_size + 1;
4810                 if (col >= view->width)
4811                         return TRUE;
4812                 waddch(view->win, ' ');
4813         }
4814         if (type != LINE_CURSOR)
4815                 wattrset(view->win, A_NORMAL);
4816
4817         wmove(view->win, lineno, col);
4818
4819         if (opt_show_refs && commit->refs) {
4820                 size_t i = 0;
4821
4822                 do {
4823                         if (type == LINE_CURSOR)
4824                                 ;
4825                         else if (commit->refs[i]->head)
4826                                 wattrset(view->win, get_line_attr(LINE_MAIN_HEAD));
4827                         else if (commit->refs[i]->ltag)
4828                                 wattrset(view->win, get_line_attr(LINE_MAIN_LOCAL_TAG));
4829                         else if (commit->refs[i]->tag)
4830                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
4831                         else if (commit->refs[i]->remote)
4832                                 wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
4833                         else
4834                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
4835
4836                         col += draw_text(view, "[", view->width - col, TRUE, selected);
4837                         col += draw_text(view, commit->refs[i]->name, view->width - col,
4838                                          TRUE, selected);
4839                         col += draw_text(view, "]", view->width - col, TRUE, selected);
4840                         if (type != LINE_CURSOR)
4841                                 wattrset(view->win, A_NORMAL);
4842                         col += draw_text(view, " ", view->width - col, TRUE, selected);
4843                         if (col >= view->width)
4844                                 return TRUE;
4845                 } while (commit->refs[i++]->next);
4846         }
4847
4848         if (type != LINE_CURSOR)
4849                 wattrset(view->win, get_line_attr(type));
4850
4851         draw_text(view, commit->title, view->width - col, TRUE, selected);
4852         return TRUE;
4853 }
4854
4855 /* Reads git log --pretty=raw output and parses it into the commit struct. */
4856 static bool
4857 main_read(struct view *view, char *line)
4858 {
4859         static struct rev_graph *graph = graph_stacks;
4860         enum line_type type;
4861         struct commit *commit;
4862
4863         if (!line) {
4864                 update_rev_graph(graph);
4865                 return TRUE;
4866         }
4867
4868         type = get_line_type(line);
4869         if (type == LINE_COMMIT) {
4870                 commit = calloc(1, sizeof(struct commit));
4871                 if (!commit)
4872                         return FALSE;
4873
4874                 line += STRING_SIZE("commit ");
4875                 if (*line == '-') {
4876                         graph->boundary = 1;
4877                         line++;
4878                 }
4879
4880                 string_copy_rev(commit->id, line);
4881                 commit->refs = get_refs(commit->id);
4882                 graph->commit = commit;
4883                 add_line_data(view, commit, LINE_MAIN_COMMIT);
4884
4885                 while ((line = strchr(line, ' '))) {
4886                         line++;
4887                         push_rev_graph(graph->parents, line);
4888                         commit->has_parents = TRUE;
4889                 }
4890                 return TRUE;
4891         }
4892
4893         if (!view->lines)
4894                 return TRUE;
4895         commit = view->line[view->lines - 1].data;
4896
4897         switch (type) {
4898         case LINE_PARENT:
4899                 if (commit->has_parents)
4900                         break;
4901                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
4902                 break;
4903
4904         case LINE_AUTHOR:
4905         {
4906                 /* Parse author lines where the name may be empty:
4907                  *      author  <email@address.tld> 1138474660 +0100
4908                  */
4909                 char *ident = line + STRING_SIZE("author ");
4910                 char *nameend = strchr(ident, '<');
4911                 char *emailend = strchr(ident, '>');
4912
4913                 if (!nameend || !emailend)
4914                         break;
4915
4916                 update_rev_graph(graph);
4917                 graph = graph->next;
4918
4919                 *nameend = *emailend = 0;
4920                 ident = chomp_string(ident);
4921                 if (!*ident) {
4922                         ident = chomp_string(nameend + 1);
4923                         if (!*ident)
4924                                 ident = "Unknown";
4925                 }
4926
4927                 string_ncopy(commit->author, ident, strlen(ident));
4928
4929                 /* Parse epoch and timezone */
4930                 if (emailend[1] == ' ') {
4931                         char *secs = emailend + 2;
4932                         char *zone = strchr(secs, ' ');
4933                         time_t time = (time_t) atol(secs);
4934
4935                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
4936                                 long tz;
4937
4938                                 zone++;
4939                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
4940                                 tz += ('0' - zone[2]) * 60 * 60;
4941                                 tz += ('0' - zone[3]) * 60;
4942                                 tz += ('0' - zone[4]) * 60;
4943
4944                                 if (zone[0] == '-')
4945                                         tz = -tz;
4946
4947                                 time -= tz;
4948                         }
4949
4950                         gmtime_r(&time, &commit->time);
4951                 }
4952                 break;
4953         }
4954         default:
4955                 /* Fill in the commit title if it has not already been set. */
4956                 if (commit->title[0])
4957                         break;
4958
4959                 /* Require titles to start with a non-space character at the
4960                  * offset used by git log. */
4961                 if (strncmp(line, "    ", 4))
4962                         break;
4963                 line += 4;
4964                 /* Well, if the title starts with a whitespace character,
4965                  * try to be forgiving.  Otherwise we end up with no title. */
4966                 while (isspace(*line))
4967                         line++;
4968                 if (*line == '\0')
4969                         break;
4970                 /* FIXME: More graceful handling of titles; append "..." to
4971                  * shortened titles, etc. */
4972
4973                 string_ncopy(commit->title, line, strlen(line));
4974         }
4975
4976         return TRUE;
4977 }
4978
4979 static enum request
4980 main_request(struct view *view, enum request request, struct line *line)
4981 {
4982         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4983
4984         if (request == REQ_ENTER)
4985                 open_view(view, REQ_VIEW_DIFF, flags);
4986         else
4987                 return request;
4988
4989         return REQ_NONE;
4990 }
4991
4992 static bool
4993 main_grep(struct view *view, struct line *line)
4994 {
4995         struct commit *commit = line->data;
4996         enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
4997         char buf[DATE_COLS + 1];
4998         regmatch_t pmatch;
4999
5000         for (state = S_TITLE; state < S_END; state++) {
5001                 char *text;
5002
5003                 switch (state) {
5004                 case S_TITLE:   text = commit->title;   break;
5005                 case S_AUTHOR:  text = commit->author;  break;
5006                 case S_DATE:
5007                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
5008                                 continue;
5009                         text = buf;
5010                         break;
5011
5012                 default:
5013                         return FALSE;
5014                 }
5015
5016                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
5017                         return TRUE;
5018         }
5019
5020         return FALSE;
5021 }
5022
5023 static void
5024 main_select(struct view *view, struct line *line)
5025 {
5026         struct commit *commit = line->data;
5027
5028         string_copy_rev(view->ref, commit->id);
5029         string_copy_rev(ref_commit, view->ref);
5030 }
5031
5032 static struct view_ops main_ops = {
5033         "commit",
5034         NULL,
5035         main_read,
5036         main_draw,
5037         main_request,
5038         main_grep,
5039         main_select,
5040 };
5041
5042
5043 /*
5044  * Unicode / UTF-8 handling
5045  *
5046  * NOTE: Much of the following code for dealing with unicode is derived from
5047  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
5048  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
5049  */
5050
5051 /* I've (over)annotated a lot of code snippets because I am not entirely
5052  * confident that the approach taken by this small UTF-8 interface is correct.
5053  * --jonas */
5054
5055 static inline int
5056 unicode_width(unsigned long c)
5057 {
5058         if (c >= 0x1100 &&
5059            (c <= 0x115f                         /* Hangul Jamo */
5060             || c == 0x2329
5061             || c == 0x232a
5062             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
5063                                                 /* CJK ... Yi */
5064             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
5065             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
5066             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
5067             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
5068             || (c >= 0xffe0  && c <= 0xffe6)
5069             || (c >= 0x20000 && c <= 0x2fffd)
5070             || (c >= 0x30000 && c <= 0x3fffd)))
5071                 return 2;
5072
5073         if (c == '\t')
5074                 return opt_tab_size;
5075
5076         return 1;
5077 }
5078
5079 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
5080  * Illegal bytes are set one. */
5081 static const unsigned char utf8_bytes[256] = {
5082         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,
5083         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,
5084         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,
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         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,
5089         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,
5090 };
5091
5092 /* Decode UTF-8 multi-byte representation into a unicode character. */
5093 static inline unsigned long
5094 utf8_to_unicode(const char *string, size_t length)
5095 {
5096         unsigned long unicode;
5097
5098         switch (length) {
5099         case 1:
5100                 unicode  =   string[0];
5101                 break;
5102         case 2:
5103                 unicode  =  (string[0] & 0x1f) << 6;
5104                 unicode +=  (string[1] & 0x3f);
5105                 break;
5106         case 3:
5107                 unicode  =  (string[0] & 0x0f) << 12;
5108                 unicode += ((string[1] & 0x3f) << 6);
5109                 unicode +=  (string[2] & 0x3f);
5110                 break;
5111         case 4:
5112                 unicode  =  (string[0] & 0x0f) << 18;
5113                 unicode += ((string[1] & 0x3f) << 12);
5114                 unicode += ((string[2] & 0x3f) << 6);
5115                 unicode +=  (string[3] & 0x3f);
5116                 break;
5117         case 5:
5118                 unicode  =  (string[0] & 0x0f) << 24;
5119                 unicode += ((string[1] & 0x3f) << 18);
5120                 unicode += ((string[2] & 0x3f) << 12);
5121                 unicode += ((string[3] & 0x3f) << 6);
5122                 unicode +=  (string[4] & 0x3f);
5123                 break;
5124         case 6:
5125                 unicode  =  (string[0] & 0x01) << 30;
5126                 unicode += ((string[1] & 0x3f) << 24);
5127                 unicode += ((string[2] & 0x3f) << 18);
5128                 unicode += ((string[3] & 0x3f) << 12);
5129                 unicode += ((string[4] & 0x3f) << 6);
5130                 unicode +=  (string[5] & 0x3f);
5131                 break;
5132         default:
5133                 die("Invalid unicode length");
5134         }
5135
5136         /* Invalid characters could return the special 0xfffd value but NUL
5137          * should be just as good. */
5138         return unicode > 0xffff ? 0 : unicode;
5139 }
5140
5141 /* Calculates how much of string can be shown within the given maximum width
5142  * and sets trimmed parameter to non-zero value if all of string could not be
5143  * shown. If the reserve flag is TRUE, it will reserve at least one
5144  * trailing character, which can be useful when drawing a delimiter.
5145  *
5146  * Returns the number of bytes to output from string to satisfy max_width. */
5147 static size_t
5148 utf8_length(const char *string, size_t max_width, int *trimmed, bool reserve)
5149 {
5150         const char *start = string;
5151         const char *end = strchr(string, '\0');
5152         unsigned char last_bytes = 0;
5153         size_t width = 0;
5154
5155         *trimmed = 0;
5156
5157         while (string < end) {
5158                 int c = *(unsigned char *) string;
5159                 unsigned char bytes = utf8_bytes[c];
5160                 size_t ucwidth;
5161                 unsigned long unicode;
5162
5163                 if (string + bytes > end)
5164                         break;
5165
5166                 /* Change representation to figure out whether
5167                  * it is a single- or double-width character. */
5168
5169                 unicode = utf8_to_unicode(string, bytes);
5170                 /* FIXME: Graceful handling of invalid unicode character. */
5171                 if (!unicode)
5172                         break;
5173
5174                 ucwidth = unicode_width(unicode);
5175                 width  += ucwidth;
5176                 if (width > max_width) {
5177                         *trimmed = 1;
5178                         if (reserve && width - ucwidth == max_width) {
5179                                 string -= last_bytes;
5180                         }
5181                         break;
5182                 }
5183
5184                 string  += bytes;
5185                 last_bytes = bytes;
5186         }
5187
5188         return string - start;
5189 }
5190
5191
5192 /*
5193  * Status management
5194  */
5195
5196 /* Whether or not the curses interface has been initialized. */
5197 static bool cursed = FALSE;
5198
5199 /* The status window is used for polling keystrokes. */
5200 static WINDOW *status_win;
5201
5202 static bool status_empty = TRUE;
5203
5204 /* Update status and title window. */
5205 static void
5206 report(const char *msg, ...)
5207 {
5208         struct view *view = display[current_view];
5209
5210         if (input_mode)
5211                 return;
5212
5213         if (!view) {
5214                 char buf[SIZEOF_STR];
5215                 va_list args;
5216
5217                 va_start(args, msg);
5218                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
5219                         buf[sizeof(buf) - 1] = 0;
5220                         buf[sizeof(buf) - 2] = '.';
5221                         buf[sizeof(buf) - 3] = '.';
5222                         buf[sizeof(buf) - 4] = '.';
5223                 }
5224                 va_end(args);
5225                 die("%s", buf);
5226         }
5227
5228         if (!status_empty || *msg) {
5229                 va_list args;
5230
5231                 va_start(args, msg);
5232
5233                 wmove(status_win, 0, 0);
5234                 if (*msg) {
5235                         vwprintw(status_win, msg, args);
5236                         status_empty = FALSE;
5237                 } else {
5238                         status_empty = TRUE;
5239                 }
5240                 wclrtoeol(status_win);
5241                 wrefresh(status_win);
5242
5243                 va_end(args);
5244         }
5245
5246         update_view_title(view);
5247         update_display_cursor(view);
5248 }
5249
5250 /* Controls when nodelay should be in effect when polling user input. */
5251 static void
5252 set_nonblocking_input(bool loading)
5253 {
5254         static unsigned int loading_views;
5255
5256         if ((loading == FALSE && loading_views-- == 1) ||
5257             (loading == TRUE  && loading_views++ == 0))
5258                 nodelay(status_win, loading);
5259 }
5260
5261 static void
5262 init_display(void)
5263 {
5264         int x, y;
5265
5266         /* Initialize the curses library */
5267         if (isatty(STDIN_FILENO)) {
5268                 cursed = !!initscr();
5269         } else {
5270                 /* Leave stdin and stdout alone when acting as a pager. */
5271                 FILE *io = fopen("/dev/tty", "r+");
5272
5273                 if (!io)
5274                         die("Failed to open /dev/tty");
5275                 cursed = !!newterm(NULL, io, io);
5276         }
5277
5278         if (!cursed)
5279                 die("Failed to initialize curses");
5280
5281         nonl();         /* Tell curses not to do NL->CR/NL on output */
5282         cbreak();       /* Take input chars one at a time, no wait for \n */
5283         noecho();       /* Don't echo input */
5284         leaveok(stdscr, TRUE);
5285
5286         if (has_colors())
5287                 init_colors();
5288
5289         getmaxyx(stdscr, y, x);
5290         status_win = newwin(1, 0, y - 1, 0);
5291         if (!status_win)
5292                 die("Failed to create status window");
5293
5294         /* Enable keyboard mapping */
5295         keypad(status_win, TRUE);
5296         wbkgdset(status_win, get_line_attr(LINE_STATUS));
5297 }
5298
5299 static char *
5300 read_prompt(const char *prompt)
5301 {
5302         enum { READING, STOP, CANCEL } status = READING;
5303         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
5304         int pos = 0;
5305
5306         while (status == READING) {
5307                 struct view *view;
5308                 int i, key;
5309
5310                 input_mode = TRUE;
5311
5312                 foreach_view (view, i)
5313                         update_view(view);
5314
5315                 input_mode = FALSE;
5316
5317                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
5318                 wclrtoeol(status_win);
5319
5320                 /* Refresh, accept single keystroke of input */
5321                 key = wgetch(status_win);
5322                 switch (key) {
5323                 case KEY_RETURN:
5324                 case KEY_ENTER:
5325                 case '\n':
5326                         status = pos ? STOP : CANCEL;
5327                         break;
5328
5329                 case KEY_BACKSPACE:
5330                         if (pos > 0)
5331                                 pos--;
5332                         else
5333                                 status = CANCEL;
5334                         break;
5335
5336                 case KEY_ESC:
5337                         status = CANCEL;
5338                         break;
5339
5340                 case ERR:
5341                         break;
5342
5343                 default:
5344                         if (pos >= sizeof(buf)) {
5345                                 report("Input string too long");
5346                                 return NULL;
5347                         }
5348
5349                         if (isprint(key))
5350                                 buf[pos++] = (char) key;
5351                 }
5352         }
5353
5354         /* Clear the status window */
5355         status_empty = FALSE;
5356         report("");
5357
5358         if (status == CANCEL)
5359                 return NULL;
5360
5361         buf[pos++] = 0;
5362
5363         return buf;
5364 }
5365
5366 /*
5367  * Repository references
5368  */
5369
5370 static struct ref *refs = NULL;
5371 static size_t refs_alloc = 0;
5372 static size_t refs_size = 0;
5373
5374 /* Id <-> ref store */
5375 static struct ref ***id_refs = NULL;
5376 static size_t id_refs_alloc = 0;
5377 static size_t id_refs_size = 0;
5378
5379 static struct ref **
5380 get_refs(char *id)
5381 {
5382         struct ref ***tmp_id_refs;
5383         struct ref **ref_list = NULL;
5384         size_t ref_list_alloc = 0;
5385         size_t ref_list_size = 0;
5386         size_t i;
5387
5388         for (i = 0; i < id_refs_size; i++)
5389                 if (!strcmp(id, id_refs[i][0]->id))
5390                         return id_refs[i];
5391
5392         tmp_id_refs = realloc_items(id_refs, &id_refs_alloc, id_refs_size + 1,
5393                                     sizeof(*id_refs));
5394         if (!tmp_id_refs)
5395                 return NULL;
5396
5397         id_refs = tmp_id_refs;
5398
5399         for (i = 0; i < refs_size; i++) {
5400                 struct ref **tmp;
5401
5402                 if (strcmp(id, refs[i].id))
5403                         continue;
5404
5405                 tmp = realloc_items(ref_list, &ref_list_alloc,
5406                                     ref_list_size + 1, sizeof(*ref_list));
5407                 if (!tmp) {
5408                         if (ref_list)
5409                                 free(ref_list);
5410                         return NULL;
5411                 }
5412
5413                 ref_list = tmp;
5414                 if (ref_list_size > 0)
5415                         ref_list[ref_list_size - 1]->next = 1;
5416                 ref_list[ref_list_size] = &refs[i];
5417
5418                 /* XXX: The properties of the commit chains ensures that we can
5419                  * safely modify the shared ref. The repo references will
5420                  * always be similar for the same id. */
5421                 ref_list[ref_list_size]->next = 0;
5422                 ref_list_size++;
5423         }
5424
5425         if (ref_list)
5426                 id_refs[id_refs_size++] = ref_list;
5427
5428         return ref_list;
5429 }
5430
5431 static int
5432 read_ref(char *id, size_t idlen, char *name, size_t namelen)
5433 {
5434         struct ref *ref;
5435         bool tag = FALSE;
5436         bool ltag = FALSE;
5437         bool remote = FALSE;
5438         bool check_replace = FALSE;
5439         bool head = FALSE;
5440
5441         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
5442                 if (!strcmp(name + namelen - 3, "^{}")) {
5443                         namelen -= 3;
5444                         name[namelen] = 0;
5445                         if (refs_size > 0 && refs[refs_size - 1].ltag == TRUE)
5446                                 check_replace = TRUE;
5447                 } else {
5448                         ltag = TRUE;
5449                 }
5450
5451                 tag = TRUE;
5452                 namelen -= STRING_SIZE("refs/tags/");
5453                 name    += STRING_SIZE("refs/tags/");
5454
5455         } else if (!strncmp(name, "refs/remotes/", STRING_SIZE("refs/remotes/"))) {
5456                 remote = TRUE;
5457                 namelen -= STRING_SIZE("refs/remotes/");
5458                 name    += STRING_SIZE("refs/remotes/");
5459
5460         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
5461                 namelen -= STRING_SIZE("refs/heads/");
5462                 name    += STRING_SIZE("refs/heads/");
5463                 head     = !strncmp(opt_head, name, namelen);
5464
5465         } else if (!strcmp(name, "HEAD")) {
5466                 opt_no_head = FALSE;
5467                 return OK;
5468         }
5469
5470         if (check_replace && !strcmp(name, refs[refs_size - 1].name)) {
5471                 /* it's an annotated tag, replace the previous sha1 with the
5472                  * resolved commit id; relies on the fact git-ls-remote lists
5473                  * the commit id of an annotated tag right beofre the commit id
5474                  * it points to. */
5475                 refs[refs_size - 1].ltag = ltag;
5476                 string_copy_rev(refs[refs_size - 1].id, id);
5477
5478                 return OK;
5479         }
5480         refs = realloc_items(refs, &refs_alloc, refs_size + 1, sizeof(*refs));
5481         if (!refs)
5482                 return ERR;
5483
5484         ref = &refs[refs_size++];
5485         ref->name = malloc(namelen + 1);
5486         if (!ref->name)
5487                 return ERR;
5488
5489         strncpy(ref->name, name, namelen);
5490         ref->name[namelen] = 0;
5491         ref->tag = tag;
5492         ref->ltag = ltag;
5493         ref->remote = remote;
5494         ref->head = head;
5495         string_copy_rev(ref->id, id);
5496
5497         return OK;
5498 }
5499
5500 static int
5501 load_refs(void)
5502 {
5503         const char *cmd_env = getenv("TIG_LS_REMOTE");
5504         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
5505
5506         return read_properties(popen(cmd, "r"), "\t", read_ref);
5507 }
5508
5509 static int
5510 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
5511 {
5512         if (!strcmp(name, "i18n.commitencoding"))
5513                 string_ncopy(opt_encoding, value, valuelen);
5514
5515         if (!strcmp(name, "core.editor"))
5516                 string_ncopy(opt_editor, value, valuelen);
5517
5518         return OK;
5519 }
5520
5521 static int
5522 load_repo_config(void)
5523 {
5524         return read_properties(popen(GIT_CONFIG " --list", "r"),
5525                                "=", read_repo_config_option);
5526 }
5527
5528 static int
5529 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
5530 {
5531         if (!opt_git_dir[0]) {
5532                 string_ncopy(opt_git_dir, name, namelen);
5533
5534         } else if (opt_is_inside_work_tree == -1) {
5535                 /* This can be 3 different values depending on the
5536                  * version of git being used. If git-rev-parse does not
5537                  * understand --is-inside-work-tree it will simply echo
5538                  * the option else either "true" or "false" is printed.
5539                  * Default to true for the unknown case. */
5540                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
5541
5542         } else if (opt_cdup[0] == ' ') {
5543                 string_ncopy(opt_cdup, name, namelen);
5544         } else {
5545                 if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
5546                         namelen -= STRING_SIZE("refs/heads/");
5547                         name    += STRING_SIZE("refs/heads/");
5548                         string_ncopy(opt_head, name, namelen);
5549                 }
5550         }
5551
5552         return OK;
5553 }
5554
5555 static int
5556 load_repo_info(void)
5557 {
5558         int result;
5559         FILE *pipe = popen("git rev-parse --git-dir --is-inside-work-tree "
5560                            " --show-cdup --symbolic-full-name HEAD 2>/dev/null", "r");
5561
5562         /* XXX: The line outputted by "--show-cdup" can be empty so
5563          * initialize it to something invalid to make it possible to
5564          * detect whether it has been set or not. */
5565         opt_cdup[0] = ' ';
5566
5567         result = read_properties(pipe, "=", read_repo_info);
5568         if (opt_cdup[0] == ' ')
5569                 opt_cdup[0] = 0;
5570
5571         return result;
5572 }
5573
5574 static int
5575 read_properties(FILE *pipe, const char *separators,
5576                 int (*read_property)(char *, size_t, char *, size_t))
5577 {
5578         char buffer[BUFSIZ];
5579         char *name;
5580         int state = OK;
5581
5582         if (!pipe)
5583                 return ERR;
5584
5585         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
5586                 char *value;
5587                 size_t namelen;
5588                 size_t valuelen;
5589
5590                 name = chomp_string(name);
5591                 namelen = strcspn(name, separators);
5592
5593                 if (name[namelen]) {
5594                         name[namelen] = 0;
5595                         value = chomp_string(name + namelen + 1);
5596                         valuelen = strlen(value);
5597
5598                 } else {
5599                         value = "";
5600                         valuelen = 0;
5601                 }
5602
5603                 state = read_property(name, namelen, value, valuelen);
5604         }
5605
5606         if (state != ERR && ferror(pipe))
5607                 state = ERR;
5608
5609         pclose(pipe);
5610
5611         return state;
5612 }
5613
5614
5615 /*
5616  * Main
5617  */
5618
5619 static void __NORETURN
5620 quit(int sig)
5621 {
5622         /* XXX: Restore tty modes and let the OS cleanup the rest! */
5623         if (cursed)
5624                 endwin();
5625         exit(0);
5626 }
5627
5628 static void __NORETURN
5629 die(const char *err, ...)
5630 {
5631         va_list args;
5632
5633         endwin();
5634
5635         va_start(args, err);
5636         fputs("tig: ", stderr);
5637         vfprintf(stderr, err, args);
5638         fputs("\n", stderr);
5639         va_end(args);
5640
5641         exit(1);
5642 }
5643
5644 static void
5645 warn(const char *msg, ...)
5646 {
5647         va_list args;
5648
5649         va_start(args, msg);
5650         fputs("tig warning: ", stderr);
5651         vfprintf(stderr, msg, args);
5652         fputs("\n", stderr);
5653         va_end(args);
5654 }
5655
5656 int
5657 main(int argc, char *argv[])
5658 {
5659         struct view *view;
5660         enum request request;
5661         size_t i;
5662
5663         signal(SIGINT, quit);
5664
5665         if (setlocale(LC_ALL, "")) {
5666                 char *codeset = nl_langinfo(CODESET);
5667
5668                 string_ncopy(opt_codeset, codeset, strlen(codeset));
5669         }
5670
5671         if (load_repo_info() == ERR)
5672                 die("Failed to load repo info.");
5673
5674         if (load_options() == ERR)
5675                 die("Failed to load user config.");
5676
5677         /* Load the repo config file so options can be overwritten from
5678          * the command line. */
5679         if (load_repo_config() == ERR)
5680                 die("Failed to load repo config.");
5681
5682         if (!parse_options(argc, argv))
5683                 return 0;
5684
5685         /* Require a git repository unless when running in pager mode. */
5686         if (!opt_git_dir[0])
5687                 die("Not a git repository");
5688
5689         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
5690                 opt_utf8 = FALSE;
5691
5692         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
5693                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
5694                 if (opt_iconv == ICONV_NONE)
5695                         die("Failed to initialize character set conversion");
5696         }
5697
5698         if (load_refs() == ERR)
5699                 die("Failed to load refs.");
5700
5701         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
5702                 view->cmd_env = getenv(view->cmd_env);
5703
5704         request = opt_request;
5705
5706         init_display();
5707
5708         while (view_driver(display[current_view], request)) {
5709                 int key;
5710                 int i;
5711
5712                 foreach_view (view, i)
5713                         update_view(view);
5714
5715                 /* Refresh, accept single keystroke of input */
5716                 key = wgetch(status_win);
5717
5718                 /* wgetch() with nodelay() enabled returns ERR when there's no
5719                  * input. */
5720                 if (key == ERR) {
5721                         request = REQ_NONE;
5722                         continue;
5723                 }
5724
5725                 request = get_keybinding(display[current_view]->keymap, key);
5726
5727                 /* Some low-level request handling. This keeps access to
5728                  * status_win restricted. */
5729                 switch (request) {
5730                 case REQ_PROMPT:
5731                 {
5732                         char *cmd = read_prompt(":");
5733
5734                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
5735                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
5736                                         opt_request = REQ_VIEW_DIFF;
5737                                 } else {
5738                                         opt_request = REQ_VIEW_PAGER;
5739                                 }
5740                                 break;
5741                         }
5742
5743                         request = REQ_NONE;
5744                         break;
5745                 }
5746                 case REQ_SEARCH:
5747                 case REQ_SEARCH_BACK:
5748                 {
5749                         const char *prompt = request == REQ_SEARCH
5750                                            ? "/" : "?";
5751                         char *search = read_prompt(prompt);
5752
5753                         if (search)
5754                                 string_ncopy(opt_search, search, strlen(search));
5755                         else
5756                                 request = REQ_NONE;
5757                         break;
5758                 }
5759                 case REQ_SCREEN_RESIZE:
5760                 {
5761                         int height, width;
5762
5763                         getmaxyx(stdscr, height, width);
5764
5765                         /* Resize the status view and let the view driver take
5766                          * care of resizing the displayed views. */
5767                         wresize(status_win, 1, width);
5768                         mvwin(status_win, height - 1, 0);
5769                         wrefresh(status_win);
5770                         break;
5771                 }
5772                 default:
5773                         break;
5774                 }
5775         }
5776
5777         quit(0);
5778
5779         return 0;
5780 }