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