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