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