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