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