read_properties: use size_t instead of int for callback
[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 *, size_t, char *, size_t));
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_ncopy(opt_encoding, arg, strlen(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, size_t optlen, char *value, size_t 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                 size_t 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         if (view->pipe)
1806                 end_update(view);
1807
1808         if (opt_cmd[0]) {
1809                 string_copy(view->cmd, opt_cmd);
1810                 opt_cmd[0] = 0;
1811                 /* When running random commands, initially show the
1812                  * command in the title. However, it maybe later be
1813                  * overwritten if a commit line is selected. */
1814                 string_copy(view->ref, view->cmd);
1815
1816         } else if (view == VIEW(REQ_VIEW_TREE)) {
1817                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1818                 char path[SIZEOF_STR];
1819
1820                 if (strcmp(view->vid, view->id))
1821                         opt_path[0] = path[0] = 0;
1822                 else if (sq_quote(path, 0, opt_path) >= sizeof(path))
1823                         return FALSE;
1824
1825                 if (!string_format(view->cmd, format, view->id, path))
1826                         return FALSE;
1827
1828         } else {
1829                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1830                 const char *id = view->id;
1831
1832                 if (!string_format(view->cmd, format, id, id, id, id, id))
1833                         return FALSE;
1834
1835                 /* Put the current ref_* value to the view title ref
1836                  * member. This is needed by the blob view. Most other
1837                  * views sets it automatically after loading because the
1838                  * first line is a commit line. */
1839                 string_copy_rev(view->ref, view->id);
1840         }
1841
1842         /* Special case for the pager view. */
1843         if (opt_pipe) {
1844                 view->pipe = opt_pipe;
1845                 opt_pipe = NULL;
1846         } else {
1847                 view->pipe = popen(view->cmd, "r");
1848         }
1849
1850         if (!view->pipe)
1851                 return FALSE;
1852
1853         set_nonblocking_input(TRUE);
1854
1855         view->offset = 0;
1856         view->lines  = 0;
1857         view->lineno = 0;
1858         string_copy_rev(view->vid, view->id);
1859
1860         if (view->line) {
1861                 int i;
1862
1863                 for (i = 0; i < view->lines; i++)
1864                         if (view->line[i].data)
1865                                 free(view->line[i].data);
1866
1867                 free(view->line);
1868                 view->line = NULL;
1869         }
1870
1871         view->start_time = time(NULL);
1872
1873         return TRUE;
1874 }
1875
1876 static struct line *
1877 realloc_lines(struct view *view, size_t line_size)
1878 {
1879         struct line *tmp = realloc(view->line, sizeof(*view->line) * line_size);
1880
1881         if (!tmp)
1882                 return NULL;
1883
1884         view->line = tmp;
1885         view->line_size = line_size;
1886         return view->line;
1887 }
1888
1889 static bool
1890 update_view(struct view *view)
1891 {
1892         char in_buffer[BUFSIZ];
1893         char out_buffer[BUFSIZ * 2];
1894         char *line;
1895         /* The number of lines to read. If too low it will cause too much
1896          * redrawing (and possible flickering), if too high responsiveness
1897          * will suffer. */
1898         unsigned long lines = view->height;
1899         int redraw_from = -1;
1900
1901         if (!view->pipe)
1902                 return TRUE;
1903
1904         /* Only redraw if lines are visible. */
1905         if (view->offset + view->height >= view->lines)
1906                 redraw_from = view->lines - view->offset;
1907
1908         /* FIXME: This is probably not perfect for backgrounded views. */
1909         if (!realloc_lines(view, view->lines + lines))
1910                 goto alloc_error;
1911
1912         while ((line = fgets(in_buffer, sizeof(in_buffer), view->pipe))) {
1913                 size_t linelen = strlen(line);
1914
1915                 if (linelen)
1916                         line[linelen - 1] = 0;
1917
1918                 if (opt_iconv != ICONV_NONE) {
1919                         char *inbuf = line;
1920                         size_t inlen = linelen;
1921
1922                         char *outbuf = out_buffer;
1923                         size_t outlen = sizeof(out_buffer);
1924
1925                         size_t ret;
1926
1927                         ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
1928                         if (ret != (size_t) -1) {
1929                                 line = out_buffer;
1930                                 linelen = strlen(out_buffer);
1931                         }
1932                 }
1933
1934                 if (!view->ops->read(view, line))
1935                         goto alloc_error;
1936
1937                 if (lines-- == 1)
1938                         break;
1939         }
1940
1941         {
1942                 int digits;
1943
1944                 lines = view->lines;
1945                 for (digits = 0; lines; digits++)
1946                         lines /= 10;
1947
1948                 /* Keep the displayed view in sync with line number scaling. */
1949                 if (digits != view->digits) {
1950                         view->digits = digits;
1951                         redraw_from = 0;
1952                 }
1953         }
1954
1955         if (!view_is_displayed(view))
1956                 goto check_pipe;
1957
1958         if (view == VIEW(REQ_VIEW_TREE)) {
1959                 /* Clear the view and redraw everything since the tree sorting
1960                  * might have rearranged things. */
1961                 redraw_view(view);
1962
1963         } else if (redraw_from >= 0) {
1964                 /* If this is an incremental update, redraw the previous line
1965                  * since for commits some members could have changed when
1966                  * loading the main view. */
1967                 if (redraw_from > 0)
1968                         redraw_from--;
1969
1970                 /* Since revision graph visualization requires knowledge
1971                  * about the parent commit, it causes a further one-off
1972                  * needed to be redrawn for incremental updates. */
1973                 if (redraw_from > 0 && opt_rev_graph)
1974                         redraw_from--;
1975
1976                 /* Incrementally draw avoids flickering. */
1977                 redraw_view_from(view, redraw_from);
1978         }
1979
1980         /* Update the title _after_ the redraw so that if the redraw picks up a
1981          * commit reference in view->ref it'll be available here. */
1982         update_view_title(view);
1983
1984 check_pipe:
1985         if (ferror(view->pipe)) {
1986                 report("Failed to read: %s", strerror(errno));
1987                 goto end;
1988
1989         } else if (feof(view->pipe)) {
1990                 report("");
1991                 goto end;
1992         }
1993
1994         return TRUE;
1995
1996 alloc_error:
1997         report("Allocation failure");
1998
1999 end:
2000         view->ops->read(view, NULL);
2001         end_update(view);
2002         return FALSE;
2003 }
2004
2005 static struct line *
2006 add_line_data(struct view *view, void *data, enum line_type type)
2007 {
2008         struct line *line = &view->line[view->lines++];
2009
2010         memset(line, 0, sizeof(*line));
2011         line->type = type;
2012         line->data = data;
2013
2014         return line;
2015 }
2016
2017 static struct line *
2018 add_line_text(struct view *view, char *data, enum line_type type)
2019 {
2020         if (data)
2021                 data = strdup(data);
2022
2023         return data ? add_line_data(view, data, type) : NULL;
2024 }
2025
2026
2027 /*
2028  * View opening
2029  */
2030
2031 enum open_flags {
2032         OPEN_DEFAULT = 0,       /* Use default view switching. */
2033         OPEN_SPLIT = 1,         /* Split current view. */
2034         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
2035         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2036 };
2037
2038 static void
2039 open_view(struct view *prev, enum request request, enum open_flags flags)
2040 {
2041         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
2042         bool split = !!(flags & OPEN_SPLIT);
2043         bool reload = !!(flags & OPEN_RELOAD);
2044         struct view *view = VIEW(request);
2045         int nviews = displayed_views();
2046         struct view *base_view = display[0];
2047
2048         if (view == prev && nviews == 1 && !reload) {
2049                 report("Already in %s view", view->name);
2050                 return;
2051         }
2052
2053         if (view->ops->open) {
2054                 if (!view->ops->open(view)) {
2055                         report("Failed to load %s view", view->name);
2056                         return;
2057                 }
2058
2059         } else if ((reload || strcmp(view->vid, view->id)) &&
2060                    !begin_update(view)) {
2061                 report("Failed to load %s view", view->name);
2062                 return;
2063         }
2064
2065         if (split) {
2066                 display[1] = view;
2067                 if (!backgrounded)
2068                         current_view = 1;
2069         } else {
2070                 /* Maximize the current view. */
2071                 memset(display, 0, sizeof(display));
2072                 current_view = 0;
2073                 display[current_view] = view;
2074         }
2075
2076         /* Resize the view when switching between split- and full-screen,
2077          * or when switching between two different full-screen views. */
2078         if (nviews != displayed_views() ||
2079             (nviews == 1 && base_view != display[0]))
2080                 resize_display();
2081
2082         if (split && prev->lineno - prev->offset >= prev->height) {
2083                 /* Take the title line into account. */
2084                 int lines = prev->lineno - prev->offset - prev->height + 1;
2085
2086                 /* Scroll the view that was split if the current line is
2087                  * outside the new limited view. */
2088                 do_scroll_view(prev, lines);
2089         }
2090
2091         if (prev && view != prev) {
2092                 if (split && !backgrounded) {
2093                         /* "Blur" the previous view. */
2094                         update_view_title(prev);
2095                 }
2096
2097                 view->parent = prev;
2098         }
2099
2100         if (view->pipe && view->lines == 0) {
2101                 /* Clear the old view and let the incremental updating refill
2102                  * the screen. */
2103                 wclear(view->win);
2104                 report("");
2105         } else {
2106                 redraw_view(view);
2107                 report("");
2108         }
2109
2110         /* If the view is backgrounded the above calls to report()
2111          * won't redraw the view title. */
2112         if (backgrounded)
2113                 update_view_title(view);
2114 }
2115
2116
2117 /*
2118  * User request switch noodle
2119  */
2120
2121 static int
2122 view_driver(struct view *view, enum request request)
2123 {
2124         int i;
2125
2126         switch (request) {
2127         case REQ_MOVE_UP:
2128         case REQ_MOVE_DOWN:
2129         case REQ_MOVE_PAGE_UP:
2130         case REQ_MOVE_PAGE_DOWN:
2131         case REQ_MOVE_FIRST_LINE:
2132         case REQ_MOVE_LAST_LINE:
2133                 move_view(view, request);
2134                 break;
2135
2136         case REQ_SCROLL_LINE_DOWN:
2137         case REQ_SCROLL_LINE_UP:
2138         case REQ_SCROLL_PAGE_DOWN:
2139         case REQ_SCROLL_PAGE_UP:
2140                 scroll_view(view, request);
2141                 break;
2142
2143         case REQ_VIEW_BLOB:
2144                 if (!ref_blob[0]) {
2145                         report("No file chosen, press %s to open tree view",
2146                                get_key(REQ_VIEW_TREE));
2147                         break;
2148                 }
2149                 open_view(view, request, OPEN_DEFAULT);
2150                 break;
2151
2152         case REQ_VIEW_PAGER:
2153                 if (!VIEW(REQ_VIEW_PAGER)->lines) {
2154                         report("No pager content, press %s to run command from prompt",
2155                                get_key(REQ_PROMPT));
2156                         break;
2157                 }
2158                 open_view(view, request, OPEN_DEFAULT);
2159                 break;
2160
2161         case REQ_VIEW_MAIN:
2162         case REQ_VIEW_DIFF:
2163         case REQ_VIEW_LOG:
2164         case REQ_VIEW_TREE:
2165         case REQ_VIEW_HELP:
2166         case REQ_VIEW_STATUS:
2167                 open_view(view, request, OPEN_DEFAULT);
2168                 break;
2169
2170         case REQ_NEXT:
2171         case REQ_PREVIOUS:
2172                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
2173
2174                 if ((view == VIEW(REQ_VIEW_DIFF) &&
2175                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
2176                    (view == VIEW(REQ_VIEW_BLOB) &&
2177                      view->parent == VIEW(REQ_VIEW_TREE))) {
2178                         view = view->parent;
2179                         move_view(view, request);
2180                         if (view_is_displayed(view))
2181                                 update_view_title(view);
2182                 } else {
2183                         move_view(view, request);
2184                         break;
2185                 }
2186                 /* Fall-through */
2187
2188         case REQ_ENTER:
2189                 if (!view->lines) {
2190                         report("Nothing to enter");
2191                         break;
2192                 }
2193                 return view->ops->enter(view, &view->line[view->lineno]);
2194
2195         case REQ_VIEW_NEXT:
2196         {
2197                 int nviews = displayed_views();
2198                 int next_view = (current_view + 1) % nviews;
2199
2200                 if (next_view == current_view) {
2201                         report("Only one view is displayed");
2202                         break;
2203                 }
2204
2205                 current_view = next_view;
2206                 /* Blur out the title of the previous view. */
2207                 update_view_title(view);
2208                 report("");
2209                 break;
2210         }
2211         case REQ_TOGGLE_LINENO:
2212                 opt_line_number = !opt_line_number;
2213                 redraw_display();
2214                 break;
2215
2216         case REQ_TOGGLE_REV_GRAPH:
2217                 opt_rev_graph = !opt_rev_graph;
2218                 redraw_display();
2219                 break;
2220
2221         case REQ_PROMPT:
2222                 /* Always reload^Wrerun commands from the prompt. */
2223                 open_view(view, opt_request, OPEN_RELOAD);
2224                 break;
2225
2226         case REQ_SEARCH:
2227         case REQ_SEARCH_BACK:
2228                 search_view(view, request);
2229                 break;
2230
2231         case REQ_FIND_NEXT:
2232         case REQ_FIND_PREV:
2233                 find_next(view, request);
2234                 break;
2235
2236         case REQ_STOP_LOADING:
2237                 for (i = 0; i < ARRAY_SIZE(views); i++) {
2238                         view = &views[i];
2239                         if (view->pipe)
2240                                 report("Stopped loading the %s view", view->name),
2241                         end_update(view);
2242                 }
2243                 break;
2244
2245         case REQ_SHOW_VERSION:
2246                 report("tig-%s (built %s)", VERSION, __DATE__);
2247                 return TRUE;
2248
2249         case REQ_SCREEN_RESIZE:
2250                 resize_display();
2251                 /* Fall-through */
2252         case REQ_SCREEN_REDRAW:
2253                 redraw_display();
2254                 break;
2255
2256         case REQ_NONE:
2257                 doupdate();
2258                 return TRUE;
2259
2260         case REQ_VIEW_CLOSE:
2261                 /* XXX: Mark closed views by letting view->parent point to the
2262                  * view itself. Parents to closed view should never be
2263                  * followed. */
2264                 if (view->parent &&
2265                     view->parent->parent != view->parent) {
2266                         memset(display, 0, sizeof(display));
2267                         current_view = 0;
2268                         display[current_view] = view->parent;
2269                         view->parent = view;
2270                         resize_display();
2271                         redraw_display();
2272                         break;
2273                 }
2274                 /* Fall-through */
2275         case REQ_QUIT:
2276                 return FALSE;
2277
2278         default:
2279                 /* An unknown key will show most commonly used commands. */
2280                 report("Unknown key, press 'h' for help");
2281                 return TRUE;
2282         }
2283
2284         return TRUE;
2285 }
2286
2287
2288 /*
2289  * Pager backend
2290  */
2291
2292 static bool
2293 pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2294 {
2295         char *text = line->data;
2296         enum line_type type = line->type;
2297         int textlen = strlen(text);
2298         int attr;
2299
2300         wmove(view->win, lineno, 0);
2301
2302         if (selected) {
2303                 type = LINE_CURSOR;
2304                 wchgat(view->win, -1, 0, type, NULL);
2305         }
2306
2307         attr = get_line_attr(type);
2308         wattrset(view->win, attr);
2309
2310         if (opt_line_number || opt_tab_size < TABSIZE) {
2311                 static char spaces[] = "                    ";
2312                 int col_offset = 0, col = 0;
2313
2314                 if (opt_line_number) {
2315                         unsigned long real_lineno = view->offset + lineno + 1;
2316
2317                         if (real_lineno == 1 ||
2318                             (real_lineno % opt_num_interval) == 0) {
2319                                 wprintw(view->win, "%.*d", view->digits, real_lineno);
2320
2321                         } else {
2322                                 waddnstr(view->win, spaces,
2323                                          MIN(view->digits, STRING_SIZE(spaces)));
2324                         }
2325                         waddstr(view->win, ": ");
2326                         col_offset = view->digits + 2;
2327                 }
2328
2329                 while (text && col_offset + col < view->width) {
2330                         int cols_max = view->width - col_offset - col;
2331                         char *pos = text;
2332                         int cols;
2333
2334                         if (*text == '\t') {
2335                                 text++;
2336                                 assert(sizeof(spaces) > TABSIZE);
2337                                 pos = spaces;
2338                                 cols = opt_tab_size - (col % opt_tab_size);
2339
2340                         } else {
2341                                 text = strchr(text, '\t');
2342                                 cols = line ? text - pos : strlen(pos);
2343                         }
2344
2345                         waddnstr(view->win, pos, MIN(cols, cols_max));
2346                         col += cols;
2347                 }
2348
2349         } else {
2350                 int col = 0, pos = 0;
2351
2352                 for (; pos < textlen && col < view->width; pos++, col++)
2353                         if (text[pos] == '\t')
2354                                 col += TABSIZE - (col % TABSIZE) - 1;
2355
2356                 waddnstr(view->win, text, pos);
2357         }
2358
2359         return TRUE;
2360 }
2361
2362 static bool
2363 add_describe_ref(char *buf, size_t *bufpos, char *commit_id, const char *sep)
2364 {
2365         char refbuf[SIZEOF_STR];
2366         char *ref = NULL;
2367         FILE *pipe;
2368
2369         if (!string_format(refbuf, "git describe %s 2>/dev/null", commit_id))
2370                 return TRUE;
2371
2372         pipe = popen(refbuf, "r");
2373         if (!pipe)
2374                 return TRUE;
2375
2376         if ((ref = fgets(refbuf, sizeof(refbuf), pipe)))
2377                 ref = chomp_string(ref);
2378         pclose(pipe);
2379
2380         if (!ref || !*ref)
2381                 return TRUE;
2382
2383         /* This is the only fatal call, since it can "corrupt" the buffer. */
2384         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
2385                 return FALSE;
2386
2387         return TRUE;
2388 }
2389
2390 static void
2391 add_pager_refs(struct view *view, struct line *line)
2392 {
2393         char buf[SIZEOF_STR];
2394         char *commit_id = line->data + STRING_SIZE("commit ");
2395         struct ref **refs;
2396         size_t bufpos = 0, refpos = 0;
2397         const char *sep = "Refs: ";
2398         bool is_tag = FALSE;
2399
2400         assert(line->type == LINE_COMMIT);
2401
2402         refs = get_refs(commit_id);
2403         if (!refs) {
2404                 if (view == VIEW(REQ_VIEW_DIFF))
2405                         goto try_add_describe_ref;
2406                 return;
2407         }
2408
2409         do {
2410                 struct ref *ref = refs[refpos];
2411                 char *fmt = ref->tag    ? "%s[%s]" :
2412                             ref->remote ? "%s<%s>" : "%s%s";
2413
2414                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
2415                         return;
2416                 sep = ", ";
2417                 if (ref->tag)
2418                         is_tag = TRUE;
2419         } while (refs[refpos++]->next);
2420
2421         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
2422 try_add_describe_ref:
2423                 /* Add <tag>-g<commit_id> "fake" reference. */
2424                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
2425                         return;
2426         }
2427
2428         if (bufpos == 0)
2429                 return;
2430
2431         if (!realloc_lines(view, view->line_size + 1))
2432                 return;
2433
2434         add_line_text(view, buf, LINE_PP_REFS);
2435 }
2436
2437 static bool
2438 pager_read(struct view *view, char *data)
2439 {
2440         struct line *line;
2441
2442         if (!data)
2443                 return TRUE;
2444
2445         line = add_line_text(view, data, get_line_type(data));
2446         if (!line)
2447                 return FALSE;
2448
2449         if (line->type == LINE_COMMIT &&
2450             (view == VIEW(REQ_VIEW_DIFF) ||
2451              view == VIEW(REQ_VIEW_LOG)))
2452                 add_pager_refs(view, line);
2453
2454         return TRUE;
2455 }
2456
2457 static bool
2458 pager_enter(struct view *view, struct line *line)
2459 {
2460         int split = 0;
2461
2462         if (line->type == LINE_COMMIT &&
2463            (view == VIEW(REQ_VIEW_LOG) ||
2464             view == VIEW(REQ_VIEW_PAGER))) {
2465                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
2466                 split = 1;
2467         }
2468
2469         /* Always scroll the view even if it was split. That way
2470          * you can use Enter to scroll through the log view and
2471          * split open each commit diff. */
2472         scroll_view(view, REQ_SCROLL_LINE_DOWN);
2473
2474         /* FIXME: A minor workaround. Scrolling the view will call report("")
2475          * but if we are scrolling a non-current view this won't properly
2476          * update the view title. */
2477         if (split)
2478                 update_view_title(view);
2479
2480         return TRUE;
2481 }
2482
2483 static bool
2484 pager_grep(struct view *view, struct line *line)
2485 {
2486         regmatch_t pmatch;
2487         char *text = line->data;
2488
2489         if (!*text)
2490                 return FALSE;
2491
2492         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
2493                 return FALSE;
2494
2495         return TRUE;
2496 }
2497
2498 static void
2499 pager_select(struct view *view, struct line *line)
2500 {
2501         if (line->type == LINE_COMMIT) {
2502                 char *text = line->data + STRING_SIZE("commit ");
2503
2504                 if (view != VIEW(REQ_VIEW_PAGER))
2505                         string_copy_rev(view->ref, text);
2506                 string_copy_rev(ref_commit, text);
2507         }
2508 }
2509
2510 static struct view_ops pager_ops = {
2511         "line",
2512         NULL,
2513         pager_read,
2514         pager_draw,
2515         pager_enter,
2516         pager_grep,
2517         pager_select,
2518 };
2519
2520
2521 /*
2522  * Help backend
2523  */
2524
2525 static bool
2526 help_open(struct view *view)
2527 {
2528         char buf[BUFSIZ];
2529         int lines = ARRAY_SIZE(req_info) + 2;
2530         int i;
2531
2532         if (view->lines > 0)
2533                 return TRUE;
2534
2535         for (i = 0; i < ARRAY_SIZE(req_info); i++)
2536                 if (!req_info[i].request)
2537                         lines++;
2538
2539         view->line = calloc(lines, sizeof(*view->line));
2540         if (!view->line)
2541                 return FALSE;
2542
2543         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
2544
2545         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
2546                 char *key;
2547
2548                 if (!req_info[i].request) {
2549                         add_line_text(view, "", LINE_DEFAULT);
2550                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
2551                         continue;
2552                 }
2553
2554                 key = get_key(req_info[i].request);
2555                 if (!string_format(buf, "    %-25s %s", key, req_info[i].help))
2556                         continue;
2557
2558                 add_line_text(view, buf, LINE_DEFAULT);
2559         }
2560
2561         return TRUE;
2562 }
2563
2564 static struct view_ops help_ops = {
2565         "line",
2566         help_open,
2567         NULL,
2568         pager_draw,
2569         pager_enter,
2570         pager_grep,
2571         pager_select,
2572 };
2573
2574
2575 /*
2576  * Tree backend
2577  */
2578
2579 /* Parse output from git-ls-tree(1):
2580  *
2581  * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile
2582  * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README
2583  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
2584  * 100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38 web.conf
2585  */
2586
2587 #define SIZEOF_TREE_ATTR \
2588         STRING_SIZE("100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38\t")
2589
2590 #define TREE_UP_FORMAT "040000 tree %s\t.."
2591
2592 static int
2593 tree_compare_entry(enum line_type type1, char *name1,
2594                    enum line_type type2, char *name2)
2595 {
2596         if (type1 != type2) {
2597                 if (type1 == LINE_TREE_DIR)
2598                         return -1;
2599                 return 1;
2600         }
2601
2602         return strcmp(name1, name2);
2603 }
2604
2605 static bool
2606 tree_read(struct view *view, char *text)
2607 {
2608         size_t textlen = text ? strlen(text) : 0;
2609         char buf[SIZEOF_STR];
2610         unsigned long pos;
2611         enum line_type type;
2612         bool first_read = view->lines == 0;
2613
2614         if (textlen <= SIZEOF_TREE_ATTR)
2615                 return FALSE;
2616
2617         type = text[STRING_SIZE("100644 ")] == 't'
2618              ? LINE_TREE_DIR : LINE_TREE_FILE;
2619
2620         if (first_read) {
2621                 /* Add path info line */
2622                 if (!string_format(buf, "Directory path /%s", opt_path) ||
2623                     !realloc_lines(view, view->line_size + 1) ||
2624                     !add_line_text(view, buf, LINE_DEFAULT))
2625                         return FALSE;
2626
2627                 /* Insert "link" to parent directory. */
2628                 if (*opt_path) {
2629                         if (!string_format(buf, TREE_UP_FORMAT, view->ref) ||
2630                             !realloc_lines(view, view->line_size + 1) ||
2631                             !add_line_text(view, buf, LINE_TREE_DIR))
2632                                 return FALSE;
2633                 }
2634         }
2635
2636         /* Strip the path part ... */
2637         if (*opt_path) {
2638                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
2639                 size_t striplen = strlen(opt_path);
2640                 char *path = text + SIZEOF_TREE_ATTR;
2641
2642                 if (pathlen > striplen)
2643                         memmove(path, path + striplen,
2644                                 pathlen - striplen + 1);
2645         }
2646
2647         /* Skip "Directory ..." and ".." line. */
2648         for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
2649                 struct line *line = &view->line[pos];
2650                 char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR;
2651                 char *path2 = text + SIZEOF_TREE_ATTR;
2652                 int cmp = tree_compare_entry(line->type, path1, type, path2);
2653
2654                 if (cmp <= 0)
2655                         continue;
2656
2657                 text = strdup(text);
2658                 if (!text)
2659                         return FALSE;
2660
2661                 if (view->lines > pos)
2662                         memmove(&view->line[pos + 1], &view->line[pos],
2663                                 (view->lines - pos) * sizeof(*line));
2664
2665                 line = &view->line[pos];
2666                 line->data = text;
2667                 line->type = type;
2668                 view->lines++;
2669                 return TRUE;
2670         }
2671
2672         if (!add_line_text(view, text, type))
2673                 return FALSE;
2674
2675         /* Move the current line to the first tree entry. */
2676         if (first_read)
2677                 view->lineno++;
2678
2679         return TRUE;
2680 }
2681
2682 static bool
2683 tree_enter(struct view *view, struct line *line)
2684 {
2685         enum open_flags flags;
2686         enum request request;
2687
2688         switch (line->type) {
2689         case LINE_TREE_DIR:
2690                 /* Depending on whether it is a subdir or parent (updir?) link
2691                  * mangle the path buffer. */
2692                 if (line == &view->line[1] && *opt_path) {
2693                         size_t path_len = strlen(opt_path);
2694                         char *dirsep = opt_path + path_len - 1;
2695
2696                         while (dirsep > opt_path && dirsep[-1] != '/')
2697                                 dirsep--;
2698
2699                         dirsep[0] = 0;
2700
2701                 } else {
2702                         size_t pathlen = strlen(opt_path);
2703                         size_t origlen = pathlen;
2704                         char *data = line->data;
2705                         char *basename = data + SIZEOF_TREE_ATTR;
2706
2707                         if (!string_format_from(opt_path, &pathlen, "%s/", basename)) {
2708                                 opt_path[origlen] = 0;
2709                                 return TRUE;
2710                         }
2711                 }
2712
2713                 /* Trees and subtrees share the same ID, so they are not not
2714                  * unique like blobs. */
2715                 flags = OPEN_RELOAD;
2716                 request = REQ_VIEW_TREE;
2717                 break;
2718
2719         case LINE_TREE_FILE:
2720                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
2721                 request = REQ_VIEW_BLOB;
2722                 break;
2723
2724         default:
2725                 return TRUE;
2726         }
2727
2728         open_view(view, request, flags);
2729
2730         return TRUE;
2731 }
2732
2733 static void
2734 tree_select(struct view *view, struct line *line)
2735 {
2736         char *text = line->data + STRING_SIZE("100644 blob ");
2737
2738         if (line->type == LINE_TREE_FILE) {
2739                 string_copy_rev(ref_blob, text);
2740
2741         } else if (line->type != LINE_TREE_DIR) {
2742                 return;
2743         }
2744
2745         string_copy_rev(view->ref, text);
2746 }
2747
2748 static struct view_ops tree_ops = {
2749         "file",
2750         NULL,
2751         tree_read,
2752         pager_draw,
2753         tree_enter,
2754         pager_grep,
2755         tree_select,
2756 };
2757
2758 static bool
2759 blob_read(struct view *view, char *line)
2760 {
2761         return add_line_text(view, line, LINE_DEFAULT);
2762 }
2763
2764 static struct view_ops blob_ops = {
2765         "line",
2766         NULL,
2767         blob_read,
2768         pager_draw,
2769         pager_enter,
2770         pager_grep,
2771         pager_select,
2772 };
2773
2774
2775 /*
2776  * Status backend
2777  */
2778
2779 struct status {
2780         char status;
2781         struct {
2782                 mode_t mode;
2783                 char rev[SIZEOF_REV];
2784         } old;
2785         struct {
2786                 mode_t mode;
2787                 char rev[SIZEOF_REV];
2788         } new;
2789         char name[SIZEOF_STR];
2790 };
2791
2792 /* Get fields from the diff line:
2793  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
2794  */
2795 static inline bool
2796 status_get_diff(struct status *file, char *buf, size_t bufsize)
2797 {
2798         char *old_mode = buf +  1;
2799         char *new_mode = buf +  8;
2800         char *old_rev  = buf + 15;
2801         char *new_rev  = buf + 56;
2802         char *status   = buf + 97;
2803
2804         if (bufsize != 99 ||
2805             old_mode[-1] != ':' ||
2806             new_mode[-1] != ' ' ||
2807             old_rev[-1]  != ' ' ||
2808             new_rev[-1]  != ' ' ||
2809             status[-1]   != ' ')
2810                 return FALSE;
2811
2812         file->status = *status;
2813
2814         string_copy_rev(file->old.rev, old_rev);
2815         string_copy_rev(file->new.rev, new_rev);
2816
2817         file->old.mode = strtoul(old_mode, NULL, 8);
2818         file->new.mode = strtoul(new_mode, NULL, 8);
2819
2820         file->name[0] = 0;
2821
2822         return TRUE;
2823 }
2824
2825 static bool
2826 status_run(struct view *view, const char cmd[], bool diff, enum line_type type)
2827 {
2828         struct status *file = NULL;
2829         char buf[SIZEOF_STR * 4];
2830         size_t bufsize = 0;
2831         FILE *pipe;
2832
2833         pipe = popen(cmd, "r");
2834         if (!pipe)
2835                 return FALSE;
2836
2837         add_line_data(view, NULL, type);
2838
2839         while (!feof(pipe) && !ferror(pipe)) {
2840                 char *sep;
2841                 size_t readsize;
2842
2843                 readsize = fread(buf + bufsize, 1, sizeof(buf) - bufsize, pipe);
2844                 if (!readsize)
2845                         break;
2846                 bufsize += readsize;
2847
2848                 /* Process while we have NUL chars. */
2849                 while ((sep = memchr(buf, 0, bufsize))) {
2850                         size_t sepsize = sep - buf + 1;
2851
2852                         if (!file) {
2853                                 if (!realloc_lines(view, view->line_size + 1))
2854                                         goto error_out;
2855
2856                                 file = calloc(1, sizeof(*file));
2857                                 if (!file)
2858                                         goto error_out;
2859
2860                                 add_line_data(view, file, type);
2861                         }
2862
2863                         /* Parse diff info part. */
2864                         if (!diff) {
2865                                 file->status = '?';
2866
2867                         } else if (!file->status) {
2868                                 if (!status_get_diff(file, buf, sepsize))
2869                                         goto error_out;
2870
2871                                 bufsize -= sepsize;
2872                                 memmove(buf, sep + 1, bufsize);
2873
2874                                 sep = memchr(buf, 0, bufsize);
2875                                 if (!sep)
2876                                         break;
2877                                 sepsize = sep - buf + 1;
2878                         }
2879
2880                         /* git-ls-files just delivers a NUL separated
2881                          * list of file names similar to the second half
2882                          * of the git-diff-* output. */
2883                         string_ncopy(file->name, buf, sepsize);
2884                         bufsize -= sepsize;
2885                         memmove(buf, sep + 1, bufsize);
2886                         file = NULL;
2887                 }
2888         }
2889
2890         if (ferror(pipe)) {
2891 error_out:
2892                 pclose(pipe);
2893                 return FALSE;
2894         }
2895
2896         if (!view->line[view->lines - 1].data)
2897                 add_line_data(view, NULL, LINE_STAT_NONE);
2898
2899         pclose(pipe);
2900         return TRUE;
2901 }
2902
2903 #define STATUS_DIFF_INDEX_CMD "git diff-index -z --cached HEAD"
2904 #define STATUS_DIFF_FILES_CMD "git diff-files -z"
2905 #define STATUS_LIST_OTHER_CMD \
2906         "_git_exclude=$(git rev-parse --git-dir)/info/exclude;" \
2907         "test -f \"$_git_exclude\" && exclude=\"--exclude-from=$_git_exclude\";" \
2908         "git ls-files -z --others --exclude-per-directory=.gitignore \"$exclude\"" \
2909
2910 /* First parse staged info using git-diff-index(1), then parse unstaged
2911  * info using git-diff-files(1), and finally untracked files using
2912  * git-ls-files(1). */
2913 static bool
2914 status_open(struct view *view)
2915 {
2916         size_t i;
2917
2918         for (i = 0; i < view->lines; i++)
2919                 free(view->line[i].data);
2920         free(view->line);
2921         view->lines = view->line_size = 0;
2922         view->line = NULL;
2923
2924         if (!realloc_lines(view, view->line_size + 6))
2925                 return FALSE;
2926
2927         if (!status_run(view, STATUS_DIFF_INDEX_CMD, TRUE, LINE_STAT_STAGED) ||
2928             !status_run(view, STATUS_DIFF_FILES_CMD, TRUE, LINE_STAT_UNSTAGED) ||
2929             !status_run(view, STATUS_LIST_OTHER_CMD, FALSE, LINE_STAT_UNTRACKED))
2930                 return FALSE;
2931
2932         return TRUE;
2933 }
2934
2935 static bool
2936 status_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2937 {
2938         struct status *status = line->data;
2939
2940         wmove(view->win, lineno, 0);
2941
2942         if (selected) {
2943                 wattrset(view->win, get_line_attr(LINE_CURSOR));
2944                 wchgat(view->win, -1, 0, LINE_CURSOR, NULL);
2945
2946         } else if (!status && line->type != LINE_STAT_NONE) {
2947                 wattrset(view->win, get_line_attr(LINE_STAT_SECTION));
2948                 wchgat(view->win, -1, 0, LINE_STAT_SECTION, NULL);
2949
2950         } else {
2951                 wattrset(view->win, get_line_attr(line->type));
2952         }
2953
2954         if (!status) {
2955                 char *text;
2956
2957                 switch (line->type) {
2958                 case LINE_STAT_STAGED:
2959                         text = "Changes to be committed:";
2960                         break;
2961
2962                 case LINE_STAT_UNSTAGED:
2963                         text = "Changed but not updated:";
2964                         break;
2965
2966                 case LINE_STAT_UNTRACKED:
2967                         text = "Untracked files:";
2968                         break;
2969
2970                 case LINE_STAT_NONE:
2971                         text = "    (no files)";
2972                         break;
2973
2974                 default:
2975                         return FALSE;
2976                 }
2977
2978                 waddstr(view->win, text);
2979                 return TRUE;
2980         }
2981
2982         waddch(view->win, status->status);
2983         if (!selected)
2984                 wattrset(view->win, A_NORMAL);
2985         wmove(view->win, lineno, 4);
2986         waddstr(view->win, status->name);
2987
2988         return TRUE;
2989 }
2990
2991 static bool
2992 status_enter(struct view *view, struct line *line)
2993 {
2994         struct status *status = line->data;
2995         char cmd[SIZEOF_STR];
2996         char buf[SIZEOF_STR];
2997         size_t cmdsize = 0;
2998         size_t bufsize = 0;
2999         size_t written = 0;
3000         FILE *pipe;
3001
3002         if (!status)
3003                 return TRUE;
3004
3005         if (opt_cdup[0] &&
3006             line->type != LINE_STAT_UNTRACKED &&
3007             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
3008                 return FALSE;
3009
3010         switch (line->type) {
3011         case LINE_STAT_STAGED:
3012                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
3013                                         status->old.mode,
3014                                         status->old.rev,
3015                                         status->name, 0))
3016                         return FALSE;
3017
3018                 string_add(cmd, cmdsize, "git update-index -z --index-info");
3019                 break;
3020
3021         case LINE_STAT_UNSTAGED:
3022         case LINE_STAT_UNTRACKED:
3023                 if (!string_format_from(buf, &bufsize, "%s%c", status->name, 0))
3024                         return FALSE;
3025
3026                 string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin");
3027                 break;
3028
3029         default:
3030                 die("w00t");
3031         }
3032
3033         pipe = popen(cmd, "w");
3034         if (!pipe)
3035                 return FALSE;
3036
3037         while (!ferror(pipe) && written < bufsize) {
3038                 written += fwrite(buf + written, 1, bufsize - written, pipe);
3039         }
3040
3041         pclose(pipe);
3042
3043         if (written != bufsize)
3044                 return FALSE;
3045
3046         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
3047         return TRUE;
3048 }
3049
3050 static void
3051 status_select(struct view *view, struct line *line)
3052 {
3053         char *text;
3054
3055         switch (line->type) {
3056         case LINE_STAT_STAGED:
3057                 text = "Press Enter to unstage file for commit";
3058                 break;
3059
3060         case LINE_STAT_UNSTAGED:
3061                 text = "Press Enter to stage file for commit  ";
3062                 break;
3063
3064         case LINE_STAT_UNTRACKED:
3065                 text = "Press Enter to stage file for addition";
3066                 break;
3067
3068         case LINE_STAT_NONE:
3069                 return;
3070
3071         default:
3072                 die("w00t");
3073         }
3074
3075         string_ncopy(view->ref, text, strlen(text));
3076 }
3077
3078 static bool
3079 status_grep(struct view *view, struct line *line)
3080 {
3081         struct status *status = line->data;
3082         enum { S_STATUS, S_NAME, S_END } state;
3083         char buf[2] = "?";
3084         regmatch_t pmatch;
3085
3086         if (!status)
3087                 return FALSE;
3088
3089         for (state = S_STATUS; state < S_END; state++) {
3090                 char *text;
3091
3092                 switch (state) {
3093                 case S_NAME:    text = status->name;    break;
3094                 case S_STATUS:
3095                         buf[0] = status->status;
3096                         text = buf;
3097                         break;
3098
3099                 default:
3100                         return FALSE;
3101                 }
3102
3103                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
3104                         return TRUE;
3105         }
3106
3107         return FALSE;
3108 }
3109
3110 static struct view_ops status_ops = {
3111         "file",
3112         status_open,
3113         NULL,
3114         status_draw,
3115         status_enter,
3116         status_grep,
3117         status_select,
3118 };
3119
3120
3121 /*
3122  * Revision graph
3123  */
3124
3125 struct commit {
3126         char id[SIZEOF_REV];            /* SHA1 ID. */
3127         char title[128];                /* First line of the commit message. */
3128         char author[75];                /* Author of the commit. */
3129         struct tm time;                 /* Date from the author ident. */
3130         struct ref **refs;              /* Repository references. */
3131         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
3132         size_t graph_size;              /* The width of the graph array. */
3133 };
3134
3135 /* Size of rev graph with no  "padding" columns */
3136 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
3137
3138 struct rev_graph {
3139         struct rev_graph *prev, *next, *parents;
3140         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
3141         size_t size;
3142         struct commit *commit;
3143         size_t pos;
3144 };
3145
3146 /* Parents of the commit being visualized. */
3147 static struct rev_graph graph_parents[4];
3148
3149 /* The current stack of revisions on the graph. */
3150 static struct rev_graph graph_stacks[4] = {
3151         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
3152         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
3153         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
3154         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
3155 };
3156
3157 static inline bool
3158 graph_parent_is_merge(struct rev_graph *graph)
3159 {
3160         return graph->parents->size > 1;
3161 }
3162
3163 static inline void
3164 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
3165 {
3166         struct commit *commit = graph->commit;
3167
3168         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
3169                 commit->graph[commit->graph_size++] = symbol;
3170 }
3171
3172 static void
3173 done_rev_graph(struct rev_graph *graph)
3174 {
3175         if (graph_parent_is_merge(graph) &&
3176             graph->pos < graph->size - 1 &&
3177             graph->next->size == graph->size + graph->parents->size - 1) {
3178                 size_t i = graph->pos + graph->parents->size - 1;
3179
3180                 graph->commit->graph_size = i * 2;
3181                 while (i < graph->next->size - 1) {
3182                         append_to_rev_graph(graph, ' ');
3183                         append_to_rev_graph(graph, '\\');
3184                         i++;
3185                 }
3186         }
3187
3188         graph->size = graph->pos = 0;
3189         graph->commit = NULL;
3190         memset(graph->parents, 0, sizeof(*graph->parents));
3191 }
3192
3193 static void
3194 push_rev_graph(struct rev_graph *graph, char *parent)
3195 {
3196         int i;
3197
3198         /* "Collapse" duplicate parents lines.
3199          *
3200          * FIXME: This needs to also update update the drawn graph but
3201          * for now it just serves as a method for pruning graph lines. */
3202         for (i = 0; i < graph->size; i++)
3203                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
3204                         return;
3205
3206         if (graph->size < SIZEOF_REVITEMS) {
3207                 string_copy_rev(graph->rev[graph->size++], parent);
3208         }
3209 }
3210
3211 static chtype
3212 get_rev_graph_symbol(struct rev_graph *graph)
3213 {
3214         chtype symbol;
3215
3216         if (graph->parents->size == 0)
3217                 symbol = REVGRAPH_INIT;
3218         else if (graph_parent_is_merge(graph))
3219                 symbol = REVGRAPH_MERGE;
3220         else if (graph->pos >= graph->size)
3221                 symbol = REVGRAPH_BRANCH;
3222         else
3223                 symbol = REVGRAPH_COMMIT;
3224
3225         return symbol;
3226 }
3227
3228 static void
3229 draw_rev_graph(struct rev_graph *graph)
3230 {
3231         struct rev_filler {
3232                 chtype separator, line;
3233         };
3234         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
3235         static struct rev_filler fillers[] = {
3236                 { ' ',  REVGRAPH_LINE },
3237                 { '`',  '.' },
3238                 { '\'', ' ' },
3239                 { '/',  ' ' },
3240         };
3241         chtype symbol = get_rev_graph_symbol(graph);
3242         struct rev_filler *filler;
3243         size_t i;
3244
3245         filler = &fillers[DEFAULT];
3246
3247         for (i = 0; i < graph->pos; i++) {
3248                 append_to_rev_graph(graph, filler->line);
3249                 if (graph_parent_is_merge(graph->prev) &&
3250                     graph->prev->pos == i)
3251                         filler = &fillers[RSHARP];
3252
3253                 append_to_rev_graph(graph, filler->separator);
3254         }
3255
3256         /* Place the symbol for this revision. */
3257         append_to_rev_graph(graph, symbol);
3258
3259         if (graph->prev->size > graph->size)
3260                 filler = &fillers[RDIAG];
3261         else
3262                 filler = &fillers[DEFAULT];
3263
3264         i++;
3265
3266         for (; i < graph->size; i++) {
3267                 append_to_rev_graph(graph, filler->separator);
3268                 append_to_rev_graph(graph, filler->line);
3269                 if (graph_parent_is_merge(graph->prev) &&
3270                     i < graph->prev->pos + graph->parents->size)
3271                         filler = &fillers[RSHARP];
3272                 if (graph->prev->size > graph->size)
3273                         filler = &fillers[LDIAG];
3274         }
3275
3276         if (graph->prev->size > graph->size) {
3277                 append_to_rev_graph(graph, filler->separator);
3278                 if (filler->line != ' ')
3279                         append_to_rev_graph(graph, filler->line);
3280         }
3281 }
3282
3283 /* Prepare the next rev graph */
3284 static void
3285 prepare_rev_graph(struct rev_graph *graph)
3286 {
3287         size_t i;
3288
3289         /* First, traverse all lines of revisions up to the active one. */
3290         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
3291                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
3292                         break;
3293
3294                 push_rev_graph(graph->next, graph->rev[graph->pos]);
3295         }
3296
3297         /* Interleave the new revision parent(s). */
3298         for (i = 0; i < graph->parents->size; i++)
3299                 push_rev_graph(graph->next, graph->parents->rev[i]);
3300
3301         /* Lastly, put any remaining revisions. */
3302         for (i = graph->pos + 1; i < graph->size; i++)
3303                 push_rev_graph(graph->next, graph->rev[i]);
3304 }
3305
3306 static void
3307 update_rev_graph(struct rev_graph *graph)
3308 {
3309         /* If this is the finalizing update ... */
3310         if (graph->commit)
3311                 prepare_rev_graph(graph);
3312
3313         /* Graph visualization needs a one rev look-ahead,
3314          * so the first update doesn't visualize anything. */
3315         if (!graph->prev->commit)
3316                 return;
3317
3318         draw_rev_graph(graph->prev);
3319         done_rev_graph(graph->prev->prev);
3320 }
3321
3322
3323 /*
3324  * Main view backend
3325  */
3326
3327 static bool
3328 main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
3329 {
3330         char buf[DATE_COLS + 1];
3331         struct commit *commit = line->data;
3332         enum line_type type;
3333         int col = 0;
3334         size_t timelen;
3335         size_t authorlen;
3336         int trimmed = 1;
3337
3338         if (!*commit->author)
3339                 return FALSE;
3340
3341         wmove(view->win, lineno, col);
3342
3343         if (selected) {
3344                 type = LINE_CURSOR;
3345                 wattrset(view->win, get_line_attr(type));
3346                 wchgat(view->win, -1, 0, type, NULL);
3347
3348         } else {
3349                 type = LINE_MAIN_COMMIT;
3350                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
3351         }
3352
3353         timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
3354         waddnstr(view->win, buf, timelen);
3355         waddstr(view->win, " ");
3356
3357         col += DATE_COLS;
3358         wmove(view->win, lineno, col);
3359         if (type != LINE_CURSOR)
3360                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
3361
3362         if (opt_utf8) {
3363                 authorlen = utf8_length(commit->author, AUTHOR_COLS - 2, &col, &trimmed);
3364         } else {
3365                 authorlen = strlen(commit->author);
3366                 if (authorlen > AUTHOR_COLS - 2) {
3367                         authorlen = AUTHOR_COLS - 2;
3368                         trimmed = 1;
3369                 }
3370         }
3371
3372         if (trimmed) {
3373                 waddnstr(view->win, commit->author, authorlen);
3374                 if (type != LINE_CURSOR)
3375                         wattrset(view->win, get_line_attr(LINE_MAIN_DELIM));
3376                 waddch(view->win, '~');
3377         } else {
3378                 waddstr(view->win, commit->author);
3379         }
3380
3381         col += AUTHOR_COLS;
3382         if (type != LINE_CURSOR)
3383                 wattrset(view->win, A_NORMAL);
3384
3385         if (opt_rev_graph && commit->graph_size) {
3386                 size_t i;
3387
3388                 wmove(view->win, lineno, col);
3389                 /* Using waddch() instead of waddnstr() ensures that
3390                  * they'll be rendered correctly for the cursor line. */
3391                 for (i = 0; i < commit->graph_size; i++)
3392                         waddch(view->win, commit->graph[i]);
3393
3394                 waddch(view->win, ' ');
3395                 col += commit->graph_size + 1;
3396         }
3397
3398         wmove(view->win, lineno, col);
3399
3400         if (commit->refs) {
3401                 size_t i = 0;
3402
3403                 do {
3404                         if (type == LINE_CURSOR)
3405                                 ;
3406                         else if (commit->refs[i]->tag)
3407                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
3408                         else if (commit->refs[i]->remote)
3409                                 wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
3410                         else
3411                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
3412                         waddstr(view->win, "[");
3413                         waddstr(view->win, commit->refs[i]->name);
3414                         waddstr(view->win, "]");
3415                         if (type != LINE_CURSOR)
3416                                 wattrset(view->win, A_NORMAL);
3417                         waddstr(view->win, " ");
3418                         col += strlen(commit->refs[i]->name) + STRING_SIZE("[] ");
3419                 } while (commit->refs[i++]->next);
3420         }
3421
3422         if (type != LINE_CURSOR)
3423                 wattrset(view->win, get_line_attr(type));
3424
3425         {
3426                 int titlelen = strlen(commit->title);
3427
3428                 if (col + titlelen > view->width)
3429                         titlelen = view->width - col;
3430
3431                 waddnstr(view->win, commit->title, titlelen);
3432         }
3433
3434         return TRUE;
3435 }
3436
3437 /* Reads git log --pretty=raw output and parses it into the commit struct. */
3438 static bool
3439 main_read(struct view *view, char *line)
3440 {
3441         static struct rev_graph *graph = graph_stacks;
3442         enum line_type type;
3443         struct commit *commit;
3444
3445         if (!line) {
3446                 update_rev_graph(graph);
3447                 return TRUE;
3448         }
3449
3450         type = get_line_type(line);
3451         if (type == LINE_COMMIT) {
3452                 commit = calloc(1, sizeof(struct commit));
3453                 if (!commit)
3454                         return FALSE;
3455
3456                 string_copy_rev(commit->id, line + STRING_SIZE("commit "));
3457                 commit->refs = get_refs(commit->id);
3458                 graph->commit = commit;
3459                 add_line_data(view, commit, LINE_MAIN_COMMIT);
3460                 return TRUE;
3461         }
3462
3463         if (!view->lines)
3464                 return TRUE;
3465         commit = view->line[view->lines - 1].data;
3466
3467         switch (type) {
3468         case LINE_PARENT:
3469                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
3470                 break;
3471
3472         case LINE_AUTHOR:
3473         {
3474                 /* Parse author lines where the name may be empty:
3475                  *      author  <email@address.tld> 1138474660 +0100
3476                  */
3477                 char *ident = line + STRING_SIZE("author ");
3478                 char *nameend = strchr(ident, '<');
3479                 char *emailend = strchr(ident, '>');
3480
3481                 if (!nameend || !emailend)
3482                         break;
3483
3484                 update_rev_graph(graph);
3485                 graph = graph->next;
3486
3487                 *nameend = *emailend = 0;
3488                 ident = chomp_string(ident);
3489                 if (!*ident) {
3490                         ident = chomp_string(nameend + 1);
3491                         if (!*ident)
3492                                 ident = "Unknown";
3493                 }
3494
3495                 string_ncopy(commit->author, ident, strlen(ident));
3496
3497                 /* Parse epoch and timezone */
3498                 if (emailend[1] == ' ') {
3499                         char *secs = emailend + 2;
3500                         char *zone = strchr(secs, ' ');
3501                         time_t time = (time_t) atol(secs);
3502
3503                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
3504                                 long tz;
3505
3506                                 zone++;
3507                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
3508                                 tz += ('0' - zone[2]) * 60 * 60;
3509                                 tz += ('0' - zone[3]) * 60;
3510                                 tz += ('0' - zone[4]) * 60;
3511
3512                                 if (zone[0] == '-')
3513                                         tz = -tz;
3514
3515                                 time -= tz;
3516                         }
3517
3518                         gmtime_r(&time, &commit->time);
3519                 }
3520                 break;
3521         }
3522         default:
3523                 /* Fill in the commit title if it has not already been set. */
3524                 if (commit->title[0])
3525                         break;
3526
3527                 /* Require titles to start with a non-space character at the
3528                  * offset used by git log. */
3529                 if (strncmp(line, "    ", 4))
3530                         break;
3531                 line += 4;
3532                 /* Well, if the title starts with a whitespace character,
3533                  * try to be forgiving.  Otherwise we end up with no title. */
3534                 while (isspace(*line))
3535                         line++;
3536                 if (*line == '\0')
3537                         break;
3538                 /* FIXME: More graceful handling of titles; append "..." to
3539                  * shortened titles, etc. */
3540
3541                 string_ncopy(commit->title, line, strlen(line));
3542         }
3543
3544         return TRUE;
3545 }
3546
3547 static bool
3548 main_enter(struct view *view, struct line *line)
3549 {
3550         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3551
3552         open_view(view, REQ_VIEW_DIFF, flags);
3553         return TRUE;
3554 }
3555
3556 static bool
3557 main_grep(struct view *view, struct line *line)
3558 {
3559         struct commit *commit = line->data;
3560         enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
3561         char buf[DATE_COLS + 1];
3562         regmatch_t pmatch;
3563
3564         for (state = S_TITLE; state < S_END; state++) {
3565                 char *text;
3566
3567                 switch (state) {
3568                 case S_TITLE:   text = commit->title;   break;
3569                 case S_AUTHOR:  text = commit->author;  break;
3570                 case S_DATE:
3571                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
3572                                 continue;
3573                         text = buf;
3574                         break;
3575
3576                 default:
3577                         return FALSE;
3578                 }
3579
3580                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
3581                         return TRUE;
3582         }
3583
3584         return FALSE;
3585 }
3586
3587 static void
3588 main_select(struct view *view, struct line *line)
3589 {
3590         struct commit *commit = line->data;
3591
3592         string_copy_rev(view->ref, commit->id);
3593         string_copy_rev(ref_commit, view->ref);
3594 }
3595
3596 static struct view_ops main_ops = {
3597         "commit",
3598         NULL,
3599         main_read,
3600         main_draw,
3601         main_enter,
3602         main_grep,
3603         main_select,
3604 };
3605
3606
3607 /*
3608  * Unicode / UTF-8 handling
3609  *
3610  * NOTE: Much of the following code for dealing with unicode is derived from
3611  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
3612  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
3613  */
3614
3615 /* I've (over)annotated a lot of code snippets because I am not entirely
3616  * confident that the approach taken by this small UTF-8 interface is correct.
3617  * --jonas */
3618
3619 static inline int
3620 unicode_width(unsigned long c)
3621 {
3622         if (c >= 0x1100 &&
3623            (c <= 0x115f                         /* Hangul Jamo */
3624             || c == 0x2329
3625             || c == 0x232a
3626             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
3627                                                 /* CJK ... Yi */
3628             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
3629             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
3630             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
3631             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
3632             || (c >= 0xffe0  && c <= 0xffe6)
3633             || (c >= 0x20000 && c <= 0x2fffd)
3634             || (c >= 0x30000 && c <= 0x3fffd)))
3635                 return 2;
3636
3637         return 1;
3638 }
3639
3640 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
3641  * Illegal bytes are set one. */
3642 static const unsigned char utf8_bytes[256] = {
3643         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,
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         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,
3650         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,
3651 };
3652
3653 /* Decode UTF-8 multi-byte representation into a unicode character. */
3654 static inline unsigned long
3655 utf8_to_unicode(const char *string, size_t length)
3656 {
3657         unsigned long unicode;
3658
3659         switch (length) {
3660         case 1:
3661                 unicode  =   string[0];
3662                 break;
3663         case 2:
3664                 unicode  =  (string[0] & 0x1f) << 6;
3665                 unicode +=  (string[1] & 0x3f);
3666                 break;
3667         case 3:
3668                 unicode  =  (string[0] & 0x0f) << 12;
3669                 unicode += ((string[1] & 0x3f) << 6);
3670                 unicode +=  (string[2] & 0x3f);
3671                 break;
3672         case 4:
3673                 unicode  =  (string[0] & 0x0f) << 18;
3674                 unicode += ((string[1] & 0x3f) << 12);
3675                 unicode += ((string[2] & 0x3f) << 6);
3676                 unicode +=  (string[3] & 0x3f);
3677                 break;
3678         case 5:
3679                 unicode  =  (string[0] & 0x0f) << 24;
3680                 unicode += ((string[1] & 0x3f) << 18);
3681                 unicode += ((string[2] & 0x3f) << 12);
3682                 unicode += ((string[3] & 0x3f) << 6);
3683                 unicode +=  (string[4] & 0x3f);
3684                 break;
3685         case 6:
3686                 unicode  =  (string[0] & 0x01) << 30;
3687                 unicode += ((string[1] & 0x3f) << 24);
3688                 unicode += ((string[2] & 0x3f) << 18);
3689                 unicode += ((string[3] & 0x3f) << 12);
3690                 unicode += ((string[4] & 0x3f) << 6);
3691                 unicode +=  (string[5] & 0x3f);
3692                 break;
3693         default:
3694                 die("Invalid unicode length");
3695         }
3696
3697         /* Invalid characters could return the special 0xfffd value but NUL
3698          * should be just as good. */
3699         return unicode > 0xffff ? 0 : unicode;
3700 }
3701
3702 /* Calculates how much of string can be shown within the given maximum width
3703  * and sets trimmed parameter to non-zero value if all of string could not be
3704  * shown.
3705  *
3706  * Additionally, adds to coloffset how many many columns to move to align with
3707  * the expected position. Takes into account how multi-byte and double-width
3708  * characters will effect the cursor position.
3709  *
3710  * Returns the number of bytes to output from string to satisfy max_width. */
3711 static size_t
3712 utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed)
3713 {
3714         const char *start = string;
3715         const char *end = strchr(string, '\0');
3716         size_t mbwidth = 0;
3717         size_t width = 0;
3718
3719         *trimmed = 0;
3720
3721         while (string < end) {
3722                 int c = *(unsigned char *) string;
3723                 unsigned char bytes = utf8_bytes[c];
3724                 size_t ucwidth;
3725                 unsigned long unicode;
3726
3727                 if (string + bytes > end)
3728                         break;
3729
3730                 /* Change representation to figure out whether
3731                  * it is a single- or double-width character. */
3732
3733                 unicode = utf8_to_unicode(string, bytes);
3734                 /* FIXME: Graceful handling of invalid unicode character. */
3735                 if (!unicode)
3736                         break;
3737
3738                 ucwidth = unicode_width(unicode);
3739                 width  += ucwidth;
3740                 if (width > max_width) {
3741                         *trimmed = 1;
3742                         break;
3743                 }
3744
3745                 /* The column offset collects the differences between the
3746                  * number of bytes encoding a character and the number of
3747                  * columns will be used for rendering said character.
3748                  *
3749                  * So if some character A is encoded in 2 bytes, but will be
3750                  * represented on the screen using only 1 byte this will and up
3751                  * adding 1 to the multi-byte column offset.
3752                  *
3753                  * Assumes that no double-width character can be encoding in
3754                  * less than two bytes. */
3755                 if (bytes > ucwidth)
3756                         mbwidth += bytes - ucwidth;
3757
3758                 string  += bytes;
3759         }
3760
3761         *coloffset += mbwidth;
3762
3763         return string - start;
3764 }
3765
3766
3767 /*
3768  * Status management
3769  */
3770
3771 /* Whether or not the curses interface has been initialized. */
3772 static bool cursed = FALSE;
3773
3774 /* The status window is used for polling keystrokes. */
3775 static WINDOW *status_win;
3776
3777 static bool status_empty = TRUE;
3778
3779 /* Update status and title window. */
3780 static void
3781 report(const char *msg, ...)
3782 {
3783         struct view *view = display[current_view];
3784
3785         if (input_mode)
3786                 return;
3787
3788         if (!status_empty || *msg) {
3789                 va_list args;
3790
3791                 va_start(args, msg);
3792
3793                 wmove(status_win, 0, 0);
3794                 if (*msg) {
3795                         vwprintw(status_win, msg, args);
3796                         status_empty = FALSE;
3797                 } else {
3798                         status_empty = TRUE;
3799                 }
3800                 wclrtoeol(status_win);
3801                 wrefresh(status_win);
3802
3803                 va_end(args);
3804         }
3805
3806         update_view_title(view);
3807         update_display_cursor(view);
3808 }
3809
3810 /* Controls when nodelay should be in effect when polling user input. */
3811 static void
3812 set_nonblocking_input(bool loading)
3813 {
3814         static unsigned int loading_views;
3815
3816         if ((loading == FALSE && loading_views-- == 1) ||
3817             (loading == TRUE  && loading_views++ == 0))
3818                 nodelay(status_win, loading);
3819 }
3820
3821 static void
3822 init_display(void)
3823 {
3824         int x, y;
3825
3826         /* Initialize the curses library */
3827         if (isatty(STDIN_FILENO)) {
3828                 cursed = !!initscr();
3829         } else {
3830                 /* Leave stdin and stdout alone when acting as a pager. */
3831                 FILE *io = fopen("/dev/tty", "r+");
3832
3833                 if (!io)
3834                         die("Failed to open /dev/tty");
3835                 cursed = !!newterm(NULL, io, io);
3836         }
3837
3838         if (!cursed)
3839                 die("Failed to initialize curses");
3840
3841         nonl();         /* Tell curses not to do NL->CR/NL on output */
3842         cbreak();       /* Take input chars one at a time, no wait for \n */
3843         noecho();       /* Don't echo input */
3844         leaveok(stdscr, TRUE);
3845
3846         if (has_colors())
3847                 init_colors();
3848
3849         getmaxyx(stdscr, y, x);
3850         status_win = newwin(1, 0, y - 1, 0);
3851         if (!status_win)
3852                 die("Failed to create status window");
3853
3854         /* Enable keyboard mapping */
3855         keypad(status_win, TRUE);
3856         wbkgdset(status_win, get_line_attr(LINE_STATUS));
3857 }
3858
3859 static char *
3860 read_prompt(const char *prompt)
3861 {
3862         enum { READING, STOP, CANCEL } status = READING;
3863         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
3864         int pos = 0;
3865
3866         while (status == READING) {
3867                 struct view *view;
3868                 int i, key;
3869
3870                 input_mode = TRUE;
3871
3872                 foreach_view (view, i)
3873                         update_view(view);
3874
3875                 input_mode = FALSE;
3876
3877                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
3878                 wclrtoeol(status_win);
3879
3880                 /* Refresh, accept single keystroke of input */
3881                 key = wgetch(status_win);
3882                 switch (key) {
3883                 case KEY_RETURN:
3884                 case KEY_ENTER:
3885                 case '\n':
3886                         status = pos ? STOP : CANCEL;
3887                         break;
3888
3889                 case KEY_BACKSPACE:
3890                         if (pos > 0)
3891                                 pos--;
3892                         else
3893                                 status = CANCEL;
3894                         break;
3895
3896                 case KEY_ESC:
3897                         status = CANCEL;
3898                         break;
3899
3900                 case ERR:
3901                         break;
3902
3903                 default:
3904                         if (pos >= sizeof(buf)) {
3905                                 report("Input string too long");
3906                                 return NULL;
3907                         }
3908
3909                         if (isprint(key))
3910                                 buf[pos++] = (char) key;
3911                 }
3912         }
3913
3914         /* Clear the status window */
3915         status_empty = FALSE;
3916         report("");
3917
3918         if (status == CANCEL)
3919                 return NULL;
3920
3921         buf[pos++] = 0;
3922
3923         return buf;
3924 }
3925
3926 /*
3927  * Repository references
3928  */
3929
3930 static struct ref *refs;
3931 static size_t refs_size;
3932
3933 /* Id <-> ref store */
3934 static struct ref ***id_refs;
3935 static size_t id_refs_size;
3936
3937 static struct ref **
3938 get_refs(char *id)
3939 {
3940         struct ref ***tmp_id_refs;
3941         struct ref **ref_list = NULL;
3942         size_t ref_list_size = 0;
3943         size_t i;
3944
3945         for (i = 0; i < id_refs_size; i++)
3946                 if (!strcmp(id, id_refs[i][0]->id))
3947                         return id_refs[i];
3948
3949         tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
3950         if (!tmp_id_refs)
3951                 return NULL;
3952
3953         id_refs = tmp_id_refs;
3954
3955         for (i = 0; i < refs_size; i++) {
3956                 struct ref **tmp;
3957
3958                 if (strcmp(id, refs[i].id))
3959                         continue;
3960
3961                 tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
3962                 if (!tmp) {
3963                         if (ref_list)
3964                                 free(ref_list);
3965                         return NULL;
3966                 }
3967
3968                 ref_list = tmp;
3969                 if (ref_list_size > 0)
3970                         ref_list[ref_list_size - 1]->next = 1;
3971                 ref_list[ref_list_size] = &refs[i];
3972
3973                 /* XXX: The properties of the commit chains ensures that we can
3974                  * safely modify the shared ref. The repo references will
3975                  * always be similar for the same id. */
3976                 ref_list[ref_list_size]->next = 0;
3977                 ref_list_size++;
3978         }
3979
3980         if (ref_list)
3981                 id_refs[id_refs_size++] = ref_list;
3982
3983         return ref_list;
3984 }
3985
3986 static int
3987 read_ref(char *id, size_t idlen, char *name, size_t namelen)
3988 {
3989         struct ref *ref;
3990         bool tag = FALSE;
3991         bool remote = FALSE;
3992
3993         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
3994                 /* Commits referenced by tags has "^{}" appended. */
3995                 if (name[namelen - 1] != '}')
3996                         return OK;
3997
3998                 while (namelen > 0 && name[namelen] != '^')
3999                         namelen--;
4000
4001                 tag = TRUE;
4002                 namelen -= STRING_SIZE("refs/tags/");
4003                 name    += STRING_SIZE("refs/tags/");
4004
4005         } else if (!strncmp(name, "refs/remotes/", STRING_SIZE("refs/remotes/"))) {
4006                 remote = TRUE;
4007                 namelen -= STRING_SIZE("refs/remotes/");
4008                 name    += STRING_SIZE("refs/remotes/");
4009
4010         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
4011                 namelen -= STRING_SIZE("refs/heads/");
4012                 name    += STRING_SIZE("refs/heads/");
4013
4014         } else if (!strcmp(name, "HEAD")) {
4015                 return OK;
4016         }
4017
4018         refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
4019         if (!refs)
4020                 return ERR;
4021
4022         ref = &refs[refs_size++];
4023         ref->name = malloc(namelen + 1);
4024         if (!ref->name)
4025                 return ERR;
4026
4027         strncpy(ref->name, name, namelen);
4028         ref->name[namelen] = 0;
4029         ref->tag = tag;
4030         ref->remote = remote;
4031         string_copy_rev(ref->id, id);
4032
4033         return OK;
4034 }
4035
4036 static int
4037 load_refs(void)
4038 {
4039         const char *cmd_env = getenv("TIG_LS_REMOTE");
4040         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
4041
4042         return read_properties(popen(cmd, "r"), "\t", read_ref);
4043 }
4044
4045 static int
4046 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
4047 {
4048         if (!strcmp(name, "i18n.commitencoding"))
4049                 string_ncopy(opt_encoding, value, valuelen);
4050
4051         return OK;
4052 }
4053
4054 static int
4055 load_repo_config(void)
4056 {
4057         return read_properties(popen("git repo-config --list", "r"),
4058                                "=", read_repo_config_option);
4059 }
4060
4061 static int
4062 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
4063 {
4064         if (!opt_cdup[0])
4065                 string_ncopy(opt_cdup, name, namelen);
4066         return OK;
4067 }
4068
4069 static int
4070 load_repo_info(void)
4071 {
4072         return read_properties(popen("git rev-parse --show-cdup", "r"),
4073                                "=", read_repo_info);
4074 }
4075
4076 static int
4077 read_properties(FILE *pipe, const char *separators,
4078                 int (*read_property)(char *, size_t, char *, size_t))
4079 {
4080         char buffer[BUFSIZ];
4081         char *name;
4082         int state = OK;
4083
4084         if (!pipe)
4085                 return ERR;
4086
4087         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
4088                 char *value;
4089                 size_t namelen;
4090                 size_t valuelen;
4091
4092                 name = chomp_string(name);
4093                 namelen = strcspn(name, separators);
4094
4095                 if (name[namelen]) {
4096                         name[namelen] = 0;
4097                         value = chomp_string(name + namelen + 1);
4098                         valuelen = strlen(value);
4099
4100                 } else {
4101                         value = "";
4102                         valuelen = 0;
4103                 }
4104
4105                 state = read_property(name, namelen, value, valuelen);
4106         }
4107
4108         if (state != ERR && ferror(pipe))
4109                 state = ERR;
4110
4111         pclose(pipe);
4112
4113         return state;
4114 }
4115
4116
4117 /*
4118  * Main
4119  */
4120
4121 static void __NORETURN
4122 quit(int sig)
4123 {
4124         /* XXX: Restore tty modes and let the OS cleanup the rest! */
4125         if (cursed)
4126                 endwin();
4127         exit(0);
4128 }
4129
4130 static void __NORETURN
4131 die(const char *err, ...)
4132 {
4133         va_list args;
4134
4135         endwin();
4136
4137         va_start(args, err);
4138         fputs("tig: ", stderr);
4139         vfprintf(stderr, err, args);
4140         fputs("\n", stderr);
4141         va_end(args);
4142
4143         exit(1);
4144 }
4145
4146 int
4147 main(int argc, char *argv[])
4148 {
4149         struct view *view;
4150         enum request request;
4151         size_t i;
4152
4153         signal(SIGINT, quit);
4154
4155         if (setlocale(LC_ALL, "")) {
4156                 char *codeset = nl_langinfo(CODESET);
4157
4158                 string_ncopy(opt_codeset, codeset, strlen(codeset));
4159         }
4160
4161         if (load_options() == ERR)
4162                 die("Failed to load user config.");
4163
4164         /* Load the repo config file so options can be overwritten from
4165          * the command line. */
4166         if (load_repo_config() == ERR)
4167                 die("Failed to load repo config.");
4168
4169         if (load_repo_info() == ERR)
4170                 die("Failed to load repo info.");
4171
4172         if (!parse_options(argc, argv))
4173                 return 0;
4174
4175         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
4176                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
4177                 if (opt_iconv == ICONV_NONE)
4178                         die("Failed to initialize character set conversion");
4179         }
4180
4181         if (load_refs() == ERR)
4182                 die("Failed to load refs.");
4183
4184         /* Require a git repository unless when running in pager mode. */
4185         if (refs_size == 0 && opt_request != REQ_VIEW_PAGER)
4186                 die("Not a git repository");
4187
4188         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
4189                 view->cmd_env = getenv(view->cmd_env);
4190
4191         request = opt_request;
4192
4193         init_display();
4194
4195         while (view_driver(display[current_view], request)) {
4196                 int key;
4197                 int i;
4198
4199                 foreach_view (view, i)
4200                         update_view(view);
4201
4202                 /* Refresh, accept single keystroke of input */
4203                 key = wgetch(status_win);
4204
4205                 /* wgetch() with nodelay() enabled returns ERR when there's no
4206                  * input. */
4207                 if (key == ERR) {
4208                         request = REQ_NONE;
4209                         continue;
4210                 }
4211
4212                 request = get_keybinding(display[current_view]->keymap, key);
4213
4214                 /* Some low-level request handling. This keeps access to
4215                  * status_win restricted. */
4216                 switch (request) {
4217                 case REQ_PROMPT:
4218                 {
4219                         char *cmd = read_prompt(":");
4220
4221                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
4222                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
4223                                         opt_request = REQ_VIEW_DIFF;
4224                                 } else {
4225                                         opt_request = REQ_VIEW_PAGER;
4226                                 }
4227                                 break;
4228                         }
4229
4230                         request = REQ_NONE;
4231                         break;
4232                 }
4233                 case REQ_SEARCH:
4234                 case REQ_SEARCH_BACK:
4235                 {
4236                         const char *prompt = request == REQ_SEARCH
4237                                            ? "/" : "?";
4238                         char *search = read_prompt(prompt);
4239
4240                         if (search)
4241                                 string_ncopy(opt_search, search, strlen(search));
4242                         else
4243                                 request = REQ_NONE;
4244                         break;
4245                 }
4246                 case REQ_SCREEN_RESIZE:
4247                 {
4248                         int height, width;
4249
4250                         getmaxyx(stdscr, height, width);
4251
4252                         /* Resize the status view and let the view driver take
4253                          * care of resizing the displayed views. */
4254                         wresize(status_win, 1, width);
4255                         mvwin(status_win, height - 1, 0);
4256                         wrefresh(status_win);
4257                         break;
4258                 }
4259                 default:
4260                         break;
4261                 }
4262         }
4263
4264         quit(0);
4265
4266         return 0;
4267 }