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