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