Add notice about empty pager view
[tig] / tig.c
1 /* Copyright (c) 2006 Jonas Fonseca <fonseca@diku.dk>
2  *
3  * This program is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU General Public License as
5  * published by the Free Software Foundation; either version 2 of
6  * the License, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  */
13
14 #ifndef VERSION
15 #define VERSION "tig-0.6.git"
16 #endif
17
18 #ifndef DEBUG
19 #define NDEBUG
20 #endif
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <signal.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <time.h>
32
33 #include <sys/types.h>
34 #include <regex.h>
35
36 #include <locale.h>
37 #include <langinfo.h>
38 #include <iconv.h>
39
40 #include <curses.h>
41
42 #if __GNUC__ >= 3
43 #define __NORETURN __attribute__((__noreturn__))
44 #else
45 #define __NORETURN
46 #endif
47
48 static void __NORETURN die(const char *err, ...);
49 static void report(const char *msg, ...);
50 static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, int, char *, int));
51 static void set_nonblocking_input(bool loading);
52 static size_t utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed);
53
54 #define ABS(x)          ((x) >= 0  ? (x) : -(x))
55 #define MIN(x, y)       ((x) < (y) ? (x) :  (y))
56
57 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))
58 #define STRING_SIZE(x)  (sizeof(x) - 1)
59
60 #define SIZEOF_STR      1024    /* Default string size. */
61 #define SIZEOF_REF      256     /* Size of symbolic or SHA1 ID. */
62 #define SIZEOF_REV      41      /* Holds a SHA-1 and an ending NUL */
63
64 /* Revision graph */
65
66 #define REVGRAPH_INIT   'I'
67 #define REVGRAPH_MERGE  'M'
68 #define REVGRAPH_BRANCH '+'
69 #define REVGRAPH_COMMIT '*'
70 #define REVGRAPH_LINE   '|'
71
72 #define SIZEOF_REVGRAPH 19      /* Size of revision ancestry graphics. */
73
74 /* This color name can be used to refer to the default term colors. */
75 #define COLOR_DEFAULT   (-1)
76
77 #define ICONV_NONE      ((iconv_t) -1)
78
79 /* The format and size of the date column in the main view. */
80 #define DATE_FORMAT     "%Y-%m-%d %H:%M"
81 #define DATE_COLS       STRING_SIZE("2006-04-29 14:21 ")
82
83 #define AUTHOR_COLS     20
84
85 /* The default interval between line numbers. */
86 #define NUMBER_INTERVAL 1
87
88 #define TABSIZE         8
89
90 #define SCALE_SPLIT_VIEW(height)        ((height) * 2 / 3)
91
92 #define TIG_LS_REMOTE \
93         "git ls-remote $(git rev-parse --git-dir) 2>/dev/null"
94
95 #define TIG_DIFF_CMD \
96         "git show --root --patch-with-stat --find-copies-harder -B -C %s 2>/dev/null"
97
98 #define TIG_LOG_CMD     \
99         "git log --cc --stat -n100 %s 2>/dev/null"
100
101 #define TIG_MAIN_CMD \
102         "git log --topo-order --pretty=raw %s 2>/dev/null"
103
104 #define TIG_TREE_CMD    \
105         "git ls-tree %s %s"
106
107 #define TIG_BLOB_CMD    \
108         "git cat-file blob %s"
109
110 /* XXX: Needs to be defined to the empty string. */
111 #define TIG_HELP_CMD    ""
112 #define TIG_PAGER_CMD   ""
113
114 /* Some ascii-shorthands fitted into the ncurses namespace. */
115 #define KEY_TAB         '\t'
116 #define KEY_RETURN      '\r'
117 #define KEY_ESC         27
118
119
120 struct ref {
121         char *name;             /* Ref name; tag or head names are shortened. */
122         char id[SIZEOF_REV];    /* Commit SHA1 ID */
123         unsigned int tag:1;     /* Is it a tag? */
124         unsigned int remote:1;  /* Is it a remote ref? */
125         unsigned int next:1;    /* For ref lists: are there more refs? */
126 };
127
128 static struct ref **get_refs(char *id);
129
130 struct int_map {
131         const char *name;
132         int namelen;
133         int value;
134 };
135
136 static int
137 set_from_int_map(struct int_map *map, size_t map_size,
138                  int *value, const char *name, int namelen)
139 {
140
141         int i;
142
143         for (i = 0; i < map_size; i++)
144                 if (namelen == map[i].namelen &&
145                     !strncasecmp(name, map[i].name, namelen)) {
146                         *value = map[i].value;
147                         return OK;
148                 }
149
150         return ERR;
151 }
152
153
154 /*
155  * String helpers
156  */
157
158 static inline void
159 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
160 {
161         if (srclen > dstlen - 1)
162                 srclen = dstlen - 1;
163
164         strncpy(dst, src, srclen);
165         dst[srclen] = 0;
166 }
167
168 /* Shorthands for safely copying into a fixed buffer. */
169
170 #define string_copy(dst, src) \
171         string_ncopy_do(dst, sizeof(dst), src, sizeof(dst))
172
173 #define string_ncopy(dst, src, srclen) \
174         string_ncopy_do(dst, sizeof(dst), src, srclen)
175
176 #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         /* Draw one line; @lineno must be < view->height. */
1238         bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected);
1239         /* Read one line; updates view->line. */
1240         bool (*read)(struct view *view, char *data);
1241         /* Depending on view, change display based on current line. */
1242         bool (*enter)(struct view *view, struct line *line);
1243         /* Search for regex in a line. */
1244         bool (*grep)(struct view *view, struct line *line);
1245         /* Select line */
1246         void (*select)(struct view *view, struct line *line);
1247 };
1248
1249 static struct view_ops pager_ops;
1250 static struct view_ops main_ops;
1251 static struct view_ops tree_ops;
1252 static struct view_ops blob_ops;
1253
1254 #define VIEW_STR(name, cmd, env, ref, ops, map) \
1255         { name, cmd, #env, ref, ops, map}
1256
1257 #define VIEW_(id, name, ops, ref) \
1258         VIEW_STR(name, TIG_##id##_CMD,  TIG_##id##_CMD, ref, ops, KEYMAP_##id)
1259
1260
1261 static struct view views[] = {
1262         VIEW_(MAIN,  "main",  &main_ops,  ref_head),
1263         VIEW_(DIFF,  "diff",  &pager_ops, ref_commit),
1264         VIEW_(LOG,   "log",   &pager_ops, ref_head),
1265         VIEW_(TREE,  "tree",  &tree_ops,  ref_commit),
1266         VIEW_(BLOB,  "blob",  &blob_ops,  ref_blob),
1267         VIEW_(HELP,  "help",  &pager_ops, ""),
1268         VIEW_(PAGER, "pager", &pager_ops, ""),
1269 };
1270
1271 #define VIEW(req) (&views[(req) - REQ_OFFSET - 1])
1272
1273 #define foreach_view(view, i) \
1274         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
1275
1276 #define view_is_displayed(view) \
1277         (view == display[0] || view == display[1])
1278
1279 static bool
1280 draw_view_line(struct view *view, unsigned int lineno)
1281 {
1282         struct line *line;
1283         bool selected = (view->offset + lineno == view->lineno);
1284         bool draw_ok;
1285
1286         assert(view_is_displayed(view));
1287
1288         if (view->offset + lineno >= view->lines)
1289                 return FALSE;
1290
1291         line = &view->line[view->offset + lineno];
1292
1293         if (selected) {
1294                 line->selected = TRUE;
1295                 view->ops->select(view, line);
1296         } else if (line->selected) {
1297                 line->selected = FALSE;
1298                 wmove(view->win, lineno, 0);
1299                 wclrtoeol(view->win);
1300         }
1301
1302         scrollok(view->win, FALSE);
1303         draw_ok = view->ops->draw(view, line, lineno, selected);
1304         scrollok(view->win, TRUE);
1305
1306         return draw_ok;
1307 }
1308
1309 static void
1310 redraw_view_from(struct view *view, int lineno)
1311 {
1312         assert(0 <= lineno && lineno < view->height);
1313
1314         for (; lineno < view->height; lineno++) {
1315                 if (!draw_view_line(view, lineno))
1316                         break;
1317         }
1318
1319         redrawwin(view->win);
1320         if (input_mode)
1321                 wnoutrefresh(view->win);
1322         else
1323                 wrefresh(view->win);
1324 }
1325
1326 static void
1327 redraw_view(struct view *view)
1328 {
1329         wclear(view->win);
1330         redraw_view_from(view, 0);
1331 }
1332
1333
1334 static void
1335 update_view_title(struct view *view)
1336 {
1337         char buf[SIZEOF_STR];
1338         char state[SIZEOF_STR];
1339         size_t bufpos = 0, statelen = 0;
1340
1341         assert(view_is_displayed(view));
1342
1343         if (view->lines || view->pipe) {
1344                 unsigned int view_lines = view->offset + view->height;
1345                 unsigned int lines = view->lines
1346                                    ? MIN(view_lines, view->lines) * 100 / view->lines
1347                                    : 0;
1348
1349                 string_format_from(state, &statelen, "- %s %d of %d (%d%%)",
1350                                    view->ops->type,
1351                                    view->lineno + 1,
1352                                    view->lines,
1353                                    lines);
1354
1355                 if (view->pipe) {
1356                         time_t secs = time(NULL) - view->start_time;
1357
1358                         /* Three git seconds are a long time ... */
1359                         if (secs > 2)
1360                                 string_format_from(state, &statelen, " %lds", secs);
1361                 }
1362         }
1363
1364         string_format_from(buf, &bufpos, "[%s]", view->name);
1365         if (*view->ref && bufpos < view->width) {
1366                 size_t refsize = strlen(view->ref);
1367                 size_t minsize = bufpos + 1 + /* abbrev= */ 7 + 1 + statelen;
1368
1369                 if (minsize < view->width)
1370                         refsize = view->width - minsize + 7;
1371                 string_format_from(buf, &bufpos, " %.*s", refsize, view->ref);
1372         }
1373
1374         if (statelen && bufpos < view->width) {
1375                 string_format_from(buf, &bufpos, " %s", state);
1376         }
1377
1378         if (view == display[current_view])
1379                 wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
1380         else
1381                 wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
1382
1383         mvwaddnstr(view->title, 0, 0, buf, bufpos);
1384         wclrtoeol(view->title);
1385         wmove(view->title, 0, view->width - 1);
1386
1387         if (input_mode)
1388                 wnoutrefresh(view->title);
1389         else
1390                 wrefresh(view->title);
1391 }
1392
1393 static void
1394 resize_display(void)
1395 {
1396         int offset, i;
1397         struct view *base = display[0];
1398         struct view *view = display[1] ? display[1] : display[0];
1399
1400         /* Setup window dimensions */
1401
1402         getmaxyx(stdscr, base->height, base->width);
1403
1404         /* Make room for the status window. */
1405         base->height -= 1;
1406
1407         if (view != base) {
1408                 /* Horizontal split. */
1409                 view->width   = base->width;
1410                 view->height  = SCALE_SPLIT_VIEW(base->height);
1411                 base->height -= view->height;
1412
1413                 /* Make room for the title bar. */
1414                 view->height -= 1;
1415         }
1416
1417         /* Make room for the title bar. */
1418         base->height -= 1;
1419
1420         offset = 0;
1421
1422         foreach_displayed_view (view, i) {
1423                 if (!view->win) {
1424                         view->win = newwin(view->height, 0, offset, 0);
1425                         if (!view->win)
1426                                 die("Failed to create %s view", view->name);
1427
1428                         scrollok(view->win, TRUE);
1429
1430                         view->title = newwin(1, 0, offset + view->height, 0);
1431                         if (!view->title)
1432                                 die("Failed to create title window");
1433
1434                 } else {
1435                         wresize(view->win, view->height, view->width);
1436                         mvwin(view->win,   offset, 0);
1437                         mvwin(view->title, offset + view->height, 0);
1438                 }
1439
1440                 offset += view->height + 1;
1441         }
1442 }
1443
1444 static void
1445 redraw_display(void)
1446 {
1447         struct view *view;
1448         int i;
1449
1450         foreach_displayed_view (view, i) {
1451                 redraw_view(view);
1452                 update_view_title(view);
1453         }
1454 }
1455
1456 static void
1457 update_display_cursor(struct view *view)
1458 {
1459         /* Move the cursor to the right-most column of the cursor line.
1460          *
1461          * XXX: This could turn out to be a bit expensive, but it ensures that
1462          * the cursor does not jump around. */
1463         if (view->lines) {
1464                 wmove(view->win, view->lineno - view->offset, view->width - 1);
1465                 wrefresh(view->win);
1466         }
1467 }
1468
1469 /*
1470  * Navigation
1471  */
1472
1473 /* Scrolling backend */
1474 static void
1475 do_scroll_view(struct view *view, int lines)
1476 {
1477         bool redraw_current_line = FALSE;
1478
1479         /* The rendering expects the new offset. */
1480         view->offset += lines;
1481
1482         assert(0 <= view->offset && view->offset < view->lines);
1483         assert(lines);
1484
1485         /* Move current line into the view. */
1486         if (view->lineno < view->offset) {
1487                 view->lineno = view->offset;
1488                 redraw_current_line = TRUE;
1489         } else if (view->lineno >= view->offset + view->height) {
1490                 view->lineno = view->offset + view->height - 1;
1491                 redraw_current_line = TRUE;
1492         }
1493
1494         assert(view->offset <= view->lineno && view->lineno < view->lines);
1495
1496         /* Redraw the whole screen if scrolling is pointless. */
1497         if (view->height < ABS(lines)) {
1498                 redraw_view(view);
1499
1500         } else {
1501                 int line = lines > 0 ? view->height - lines : 0;
1502                 int end = line + ABS(lines);
1503
1504                 wscrl(view->win, lines);
1505
1506                 for (; line < end; line++) {
1507                         if (!draw_view_line(view, line))
1508                                 break;
1509                 }
1510
1511                 if (redraw_current_line)
1512                         draw_view_line(view, view->lineno - view->offset);
1513         }
1514
1515         redrawwin(view->win);
1516         wrefresh(view->win);
1517         report("");
1518 }
1519
1520 /* Scroll frontend */
1521 static void
1522 scroll_view(struct view *view, enum request request)
1523 {
1524         int lines = 1;
1525
1526         assert(view_is_displayed(view));
1527
1528         switch (request) {
1529         case REQ_SCROLL_PAGE_DOWN:
1530                 lines = view->height;
1531         case REQ_SCROLL_LINE_DOWN:
1532                 if (view->offset + lines > view->lines)
1533                         lines = view->lines - view->offset;
1534
1535                 if (lines == 0 || view->offset + view->height >= view->lines) {
1536                         report("Cannot scroll beyond the last line");
1537                         return;
1538                 }
1539                 break;
1540
1541         case REQ_SCROLL_PAGE_UP:
1542                 lines = view->height;
1543         case REQ_SCROLL_LINE_UP:
1544                 if (lines > view->offset)
1545                         lines = view->offset;
1546
1547                 if (lines == 0) {
1548                         report("Cannot scroll beyond the first line");
1549                         return;
1550                 }
1551
1552                 lines = -lines;
1553                 break;
1554
1555         default:
1556                 die("request %d not handled in switch", request);
1557         }
1558
1559         do_scroll_view(view, lines);
1560 }
1561
1562 /* Cursor moving */
1563 static void
1564 move_view(struct view *view, enum request request)
1565 {
1566         int scroll_steps = 0;
1567         int steps;
1568
1569         switch (request) {
1570         case REQ_MOVE_FIRST_LINE:
1571                 steps = -view->lineno;
1572                 break;
1573
1574         case REQ_MOVE_LAST_LINE:
1575                 steps = view->lines - view->lineno - 1;
1576                 break;
1577
1578         case REQ_MOVE_PAGE_UP:
1579                 steps = view->height > view->lineno
1580                       ? -view->lineno : -view->height;
1581                 break;
1582
1583         case REQ_MOVE_PAGE_DOWN:
1584                 steps = view->lineno + view->height >= view->lines
1585                       ? view->lines - view->lineno - 1 : view->height;
1586                 break;
1587
1588         case REQ_MOVE_UP:
1589                 steps = -1;
1590                 break;
1591
1592         case REQ_MOVE_DOWN:
1593                 steps = 1;
1594                 break;
1595
1596         default:
1597                 die("request %d not handled in switch", request);
1598         }
1599
1600         if (steps <= 0 && view->lineno == 0) {
1601                 report("Cannot move beyond the first line");
1602                 return;
1603
1604         } else if (steps >= 0 && view->lineno + 1 >= view->lines) {
1605                 report("Cannot move beyond the last line");
1606                 return;
1607         }
1608
1609         /* Move the current line */
1610         view->lineno += steps;
1611         assert(0 <= view->lineno && view->lineno < view->lines);
1612
1613         /* Check whether the view needs to be scrolled */
1614         if (view->lineno < view->offset ||
1615             view->lineno >= view->offset + view->height) {
1616                 scroll_steps = steps;
1617                 if (steps < 0 && -steps > view->offset) {
1618                         scroll_steps = -view->offset;
1619
1620                 } else if (steps > 0) {
1621                         if (view->lineno == view->lines - 1 &&
1622                             view->lines > view->height) {
1623                                 scroll_steps = view->lines - view->offset - 1;
1624                                 if (scroll_steps >= view->height)
1625                                         scroll_steps -= view->height - 1;
1626                         }
1627                 }
1628         }
1629
1630         if (!view_is_displayed(view)) {
1631                 view->offset += scroll_steps;
1632                 assert(0 <= view->offset && view->offset < view->lines);
1633                 view->ops->select(view, &view->line[view->lineno]);
1634                 return;
1635         }
1636
1637         /* Repaint the old "current" line if we be scrolling */
1638         if (ABS(steps) < view->height)
1639                 draw_view_line(view, view->lineno - steps - view->offset);
1640
1641         if (scroll_steps) {
1642                 do_scroll_view(view, scroll_steps);
1643                 return;
1644         }
1645
1646         /* Draw the current line */
1647         draw_view_line(view, view->lineno - view->offset);
1648
1649         redrawwin(view->win);
1650         wrefresh(view->win);
1651         report("");
1652 }
1653
1654
1655 /*
1656  * Searching
1657  */
1658
1659 static void search_view(struct view *view, enum request request);
1660
1661 static bool
1662 find_next_line(struct view *view, unsigned long lineno, struct line *line)
1663 {
1664         assert(view_is_displayed(view));
1665
1666         if (!view->ops->grep(view, line))
1667                 return FALSE;
1668
1669         if (lineno - view->offset >= view->height) {
1670                 view->offset = lineno;
1671                 view->lineno = lineno;
1672                 redraw_view(view);
1673
1674         } else {
1675                 unsigned long old_lineno = view->lineno - view->offset;
1676
1677                 view->lineno = lineno;
1678                 draw_view_line(view, old_lineno);
1679
1680                 draw_view_line(view, view->lineno - view->offset);
1681                 redrawwin(view->win);
1682                 wrefresh(view->win);
1683         }
1684
1685         report("Line %ld matches '%s'", lineno + 1, view->grep);
1686         return TRUE;
1687 }
1688
1689 static void
1690 find_next(struct view *view, enum request request)
1691 {
1692         unsigned long lineno = view->lineno;
1693         int direction;
1694
1695         if (!*view->grep) {
1696                 if (!*opt_search)
1697                         report("No previous search");
1698                 else
1699                         search_view(view, request);
1700                 return;
1701         }
1702
1703         switch (request) {
1704         case REQ_SEARCH:
1705         case REQ_FIND_NEXT:
1706                 direction = 1;
1707                 break;
1708
1709         case REQ_SEARCH_BACK:
1710         case REQ_FIND_PREV:
1711                 direction = -1;
1712                 break;
1713
1714         default:
1715                 return;
1716         }
1717
1718         if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
1719                 lineno += direction;
1720
1721         /* Note, lineno is unsigned long so will wrap around in which case it
1722          * will become bigger than view->lines. */
1723         for (; lineno < view->lines; lineno += direction) {
1724                 struct line *line = &view->line[lineno];
1725
1726                 if (find_next_line(view, lineno, line))
1727                         return;
1728         }
1729
1730         report("No match found for '%s'", view->grep);
1731 }
1732
1733 static void
1734 search_view(struct view *view, enum request request)
1735 {
1736         int regex_err;
1737
1738         if (view->regex) {
1739                 regfree(view->regex);
1740                 *view->grep = 0;
1741         } else {
1742                 view->regex = calloc(1, sizeof(*view->regex));
1743                 if (!view->regex)
1744                         return;
1745         }
1746
1747         regex_err = regcomp(view->regex, opt_search, REG_EXTENDED);
1748         if (regex_err != 0) {
1749                 char buf[SIZEOF_STR] = "unknown error";
1750
1751                 regerror(regex_err, view->regex, buf, sizeof(buf));
1752                 report("Search failed: %s", buf);
1753                 return;
1754         }
1755
1756         string_copy(view->grep, opt_search);
1757
1758         find_next(view, request);
1759 }
1760
1761 /*
1762  * Incremental updating
1763  */
1764
1765 static void
1766 end_update(struct view *view)
1767 {
1768         if (!view->pipe)
1769                 return;
1770         set_nonblocking_input(FALSE);
1771         if (view->pipe == stdin)
1772                 fclose(view->pipe);
1773         else
1774                 pclose(view->pipe);
1775         view->pipe = NULL;
1776 }
1777
1778 static bool
1779 begin_update(struct view *view)
1780 {
1781         const char *id = view->id;
1782
1783         if (view->pipe)
1784                 end_update(view);
1785
1786         if (opt_cmd[0]) {
1787                 string_copy(view->cmd, opt_cmd);
1788                 opt_cmd[0] = 0;
1789                 /* When running random commands, initially show the
1790                  * command in the title. However, it maybe later be
1791                  * overwritten if a commit line is selected. */
1792                 string_copy(view->ref, view->cmd);
1793
1794         } else if (view == VIEW(REQ_VIEW_TREE)) {
1795                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1796                 char path[SIZEOF_STR];
1797
1798                 if (strcmp(view->vid, view->id))
1799                         opt_path[0] = path[0] = 0;
1800                 else if (sq_quote(path, 0, opt_path) >= sizeof(path))
1801                         return FALSE;
1802
1803                 if (!string_format(view->cmd, format, id, path))
1804                         return FALSE;
1805
1806         } else {
1807                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1808
1809                 if (!string_format(view->cmd, format, id, id, id, id, id))
1810                         return FALSE;
1811
1812                 /* Put the current ref_* value to the view title ref
1813                  * member. This is needed by the blob view. Most other
1814                  * views sets it automatically after loading because the
1815                  * first line is a commit line. */
1816                 string_copy(view->ref, id);
1817         }
1818
1819         /* Special case for the pager view. */
1820         if (opt_pipe) {
1821                 view->pipe = opt_pipe;
1822                 opt_pipe = NULL;
1823         } else {
1824                 view->pipe = popen(view->cmd, "r");
1825         }
1826
1827         if (!view->pipe)
1828                 return FALSE;
1829
1830         set_nonblocking_input(TRUE);
1831
1832         view->offset = 0;
1833         view->lines  = 0;
1834         view->lineno = 0;
1835         string_copy_rev(view->vid, id);
1836
1837         if (view->line) {
1838                 int i;
1839
1840                 for (i = 0; i < view->lines; i++)
1841                         if (view->line[i].data)
1842                                 free(view->line[i].data);
1843
1844                 free(view->line);
1845                 view->line = NULL;
1846         }
1847
1848         view->start_time = time(NULL);
1849
1850         return TRUE;
1851 }
1852
1853 static struct line *
1854 realloc_lines(struct view *view, size_t line_size)
1855 {
1856         struct line *tmp = realloc(view->line, sizeof(*view->line) * line_size);
1857
1858         if (!tmp)
1859                 return NULL;
1860
1861         view->line = tmp;
1862         view->line_size = line_size;
1863         return view->line;
1864 }
1865
1866 static bool
1867 update_view(struct view *view)
1868 {
1869         char in_buffer[BUFSIZ];
1870         char out_buffer[BUFSIZ * 2];
1871         char *line;
1872         /* The number of lines to read. If too low it will cause too much
1873          * redrawing (and possible flickering), if too high responsiveness
1874          * will suffer. */
1875         unsigned long lines = view->height;
1876         int redraw_from = -1;
1877
1878         if (!view->pipe)
1879                 return TRUE;
1880
1881         /* Only redraw if lines are visible. */
1882         if (view->offset + view->height >= view->lines)
1883                 redraw_from = view->lines - view->offset;
1884
1885         /* FIXME: This is probably not perfect for backgrounded views. */
1886         if (!realloc_lines(view, view->lines + lines))
1887                 goto alloc_error;
1888
1889         while ((line = fgets(in_buffer, sizeof(in_buffer), view->pipe))) {
1890                 size_t linelen = strlen(line);
1891
1892                 if (linelen)
1893                         line[linelen - 1] = 0;
1894
1895                 if (opt_iconv != ICONV_NONE) {
1896                         char *inbuf = line;
1897                         size_t inlen = linelen;
1898
1899                         char *outbuf = out_buffer;
1900                         size_t outlen = sizeof(out_buffer);
1901
1902                         size_t ret;
1903
1904                         ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
1905                         if (ret != (size_t) -1) {
1906                                 line = out_buffer;
1907                                 linelen = strlen(out_buffer);
1908                         }
1909                 }
1910
1911                 if (!view->ops->read(view, line))
1912                         goto alloc_error;
1913
1914                 if (lines-- == 1)
1915                         break;
1916         }
1917
1918         {
1919                 int digits;
1920
1921                 lines = view->lines;
1922                 for (digits = 0; lines; digits++)
1923                         lines /= 10;
1924
1925                 /* Keep the displayed view in sync with line number scaling. */
1926                 if (digits != view->digits) {
1927                         view->digits = digits;
1928                         redraw_from = 0;
1929                 }
1930         }
1931
1932         if (!view_is_displayed(view))
1933                 goto check_pipe;
1934
1935         if (view == VIEW(REQ_VIEW_TREE)) {
1936                 /* Clear the view and redraw everything since the tree sorting
1937                  * might have rearranged things. */
1938                 redraw_view(view);
1939
1940         } else if (redraw_from >= 0) {
1941                 /* If this is an incremental update, redraw the previous line
1942                  * since for commits some members could have changed when
1943                  * loading the main view. */
1944                 if (redraw_from > 0)
1945                         redraw_from--;
1946
1947                 /* Since revision graph visualization requires knowledge
1948                  * about the parent commit, it causes a further one-off
1949                  * needed to be redrawn for incremental updates. */
1950                 if (redraw_from > 0 && opt_rev_graph)
1951                         redraw_from--;
1952
1953                 /* Incrementally draw avoids flickering. */
1954                 redraw_view_from(view, redraw_from);
1955         }
1956
1957         /* Update the title _after_ the redraw so that if the redraw picks up a
1958          * commit reference in view->ref it'll be available here. */
1959         update_view_title(view);
1960
1961 check_pipe:
1962         if (ferror(view->pipe)) {
1963                 report("Failed to read: %s", strerror(errno));
1964                 goto end;
1965
1966         } else if (feof(view->pipe)) {
1967                 report("");
1968                 goto end;
1969         }
1970
1971         return TRUE;
1972
1973 alloc_error:
1974         report("Allocation failure");
1975
1976 end:
1977         view->ops->read(view, NULL);
1978         end_update(view);
1979         return FALSE;
1980 }
1981
1982 static struct line *
1983 add_line_text(struct view *view, char *data, enum line_type type)
1984 {
1985         struct line *line = &view->line[view->lines];
1986
1987         if (!data)
1988                 return NULL;
1989
1990         line->data = strdup(data);
1991         if (!line->data)
1992                 return NULL;
1993
1994         line->type = type;
1995         view->lines++;
1996
1997         return line;
1998 }
1999
2000
2001 /*
2002  * View opening
2003  */
2004
2005 static void open_help_view(struct view *view)
2006 {
2007         char buf[BUFSIZ];
2008         int lines = ARRAY_SIZE(req_info) + 2;
2009         int i;
2010
2011         if (view->lines > 0)
2012                 return;
2013
2014         for (i = 0; i < ARRAY_SIZE(req_info); i++)
2015                 if (!req_info[i].request)
2016                         lines++;
2017
2018         view->line = calloc(lines, sizeof(*view->line));
2019         if (!view->line) {
2020                 report("Allocation failure");
2021                 return;
2022         }
2023
2024         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
2025
2026         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
2027                 char *key;
2028
2029                 if (!req_info[i].request) {
2030                         add_line_text(view, "", LINE_DEFAULT);
2031                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
2032                         continue;
2033                 }
2034
2035                 key = get_key(req_info[i].request);
2036                 if (!string_format(buf, "    %-25s %s", key, req_info[i].help))
2037                         continue;
2038
2039                 add_line_text(view, buf, LINE_DEFAULT);
2040         }
2041 }
2042
2043 enum open_flags {
2044         OPEN_DEFAULT = 0,       /* Use default view switching. */
2045         OPEN_SPLIT = 1,         /* Split current view. */
2046         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
2047         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2048 };
2049
2050 static void
2051 open_view(struct view *prev, enum request request, enum open_flags flags)
2052 {
2053         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
2054         bool split = !!(flags & OPEN_SPLIT);
2055         bool reload = !!(flags & OPEN_RELOAD);
2056         struct view *view = VIEW(request);
2057         int nviews = displayed_views();
2058         struct view *base_view = display[0];
2059
2060         if (view == prev && nviews == 1 && !reload) {
2061                 report("Already in %s view", view->name);
2062                 return;
2063         }
2064
2065         if (view == VIEW(REQ_VIEW_HELP)) {
2066                 open_help_view(view);
2067
2068         } else if ((reload || strcmp(view->vid, view->id)) &&
2069                    !begin_update(view)) {
2070                 report("Failed to load %s view", view->name);
2071                 return;
2072         }
2073
2074         if (split) {
2075                 display[1] = view;
2076                 if (!backgrounded)
2077                         current_view = 1;
2078         } else {
2079                 /* Maximize the current view. */
2080                 memset(display, 0, sizeof(display));
2081                 current_view = 0;
2082                 display[current_view] = view;
2083         }
2084
2085         /* Resize the view when switching between split- and full-screen,
2086          * or when switching between two different full-screen views. */
2087         if (nviews != displayed_views() ||
2088             (nviews == 1 && base_view != display[0]))
2089                 resize_display();
2090
2091         if (split && prev->lineno - prev->offset >= prev->height) {
2092                 /* Take the title line into account. */
2093                 int lines = prev->lineno - prev->offset - prev->height + 1;
2094
2095                 /* Scroll the view that was split if the current line is
2096                  * outside the new limited view. */
2097                 do_scroll_view(prev, lines);
2098         }
2099
2100         if (prev && view != prev) {
2101                 if (split && !backgrounded) {
2102                         /* "Blur" the previous view. */
2103                         update_view_title(prev);
2104                 }
2105
2106                 view->parent = prev;
2107         }
2108
2109         if (view->pipe && view->lines == 0) {
2110                 /* Clear the old view and let the incremental updating refill
2111                  * the screen. */
2112                 wclear(view->win);
2113                 report("");
2114         } else {
2115                 redraw_view(view);
2116                 report("");
2117         }
2118
2119         /* If the view is backgrounded the above calls to report()
2120          * won't redraw the view title. */
2121         if (backgrounded)
2122                 update_view_title(view);
2123 }
2124
2125
2126 /*
2127  * User request switch noodle
2128  */
2129
2130 static int
2131 view_driver(struct view *view, enum request request)
2132 {
2133         int i;
2134
2135         switch (request) {
2136         case REQ_MOVE_UP:
2137         case REQ_MOVE_DOWN:
2138         case REQ_MOVE_PAGE_UP:
2139         case REQ_MOVE_PAGE_DOWN:
2140         case REQ_MOVE_FIRST_LINE:
2141         case REQ_MOVE_LAST_LINE:
2142                 move_view(view, request);
2143                 break;
2144
2145         case REQ_SCROLL_LINE_DOWN:
2146         case REQ_SCROLL_LINE_UP:
2147         case REQ_SCROLL_PAGE_DOWN:
2148         case REQ_SCROLL_PAGE_UP:
2149                 scroll_view(view, request);
2150                 break;
2151
2152         case REQ_VIEW_BLOB:
2153                 if (!ref_blob[0]) {
2154                         report("No file chosen, press %s to open tree view",
2155                                get_key(REQ_VIEW_TREE));
2156                         break;
2157                 }
2158                 open_view(view, request, OPEN_DEFAULT);
2159                 break;
2160
2161         case REQ_VIEW_PAGER:
2162                 if (!VIEW(REQ_VIEW_PAGER)->lines) {
2163                         report("No pager content, press %s to run command from prompt",
2164                                get_key(REQ_PROMPT));
2165                         break;
2166                 }
2167                 open_view(view, request, OPEN_DEFAULT);
2168                 break;
2169
2170         case REQ_VIEW_MAIN:
2171         case REQ_VIEW_DIFF:
2172         case REQ_VIEW_LOG:
2173         case REQ_VIEW_TREE:
2174         case REQ_VIEW_HELP:
2175                 open_view(view, request, OPEN_DEFAULT);
2176                 break;
2177
2178         case REQ_NEXT:
2179         case REQ_PREVIOUS:
2180                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
2181
2182                 if ((view == VIEW(REQ_VIEW_DIFF) &&
2183                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
2184                    (view == VIEW(REQ_VIEW_BLOB) &&
2185                      view->parent == VIEW(REQ_VIEW_TREE))) {
2186                         view = view->parent;
2187                         move_view(view, request);
2188                         if (view_is_displayed(view))
2189                                 update_view_title(view);
2190                 } else {
2191                         move_view(view, request);
2192                         break;
2193                 }
2194                 /* Fall-through */
2195
2196         case REQ_ENTER:
2197                 if (!view->lines) {
2198                         report("Nothing to enter");
2199                         break;
2200                 }
2201                 return view->ops->enter(view, &view->line[view->lineno]);
2202
2203         case REQ_VIEW_NEXT:
2204         {
2205                 int nviews = displayed_views();
2206                 int next_view = (current_view + 1) % nviews;
2207
2208                 if (next_view == current_view) {
2209                         report("Only one view is displayed");
2210                         break;
2211                 }
2212
2213                 current_view = next_view;
2214                 /* Blur out the title of the previous view. */
2215                 update_view_title(view);
2216                 report("");
2217                 break;
2218         }
2219         case REQ_TOGGLE_LINENO:
2220                 opt_line_number = !opt_line_number;
2221                 redraw_display();
2222                 break;
2223
2224         case REQ_TOGGLE_REV_GRAPH:
2225                 opt_rev_graph = !opt_rev_graph;
2226                 redraw_display();
2227                 break;
2228
2229         case REQ_PROMPT:
2230                 /* Always reload^Wrerun commands from the prompt. */
2231                 open_view(view, opt_request, OPEN_RELOAD);
2232                 break;
2233
2234         case REQ_SEARCH:
2235         case REQ_SEARCH_BACK:
2236                 search_view(view, request);
2237                 break;
2238
2239         case REQ_FIND_NEXT:
2240         case REQ_FIND_PREV:
2241                 find_next(view, request);
2242                 break;
2243
2244         case REQ_STOP_LOADING:
2245                 for (i = 0; i < ARRAY_SIZE(views); i++) {
2246                         view = &views[i];
2247                         if (view->pipe)
2248                                 report("Stopped loading the %s view", view->name),
2249                         end_update(view);
2250                 }
2251                 break;
2252
2253         case REQ_SHOW_VERSION:
2254                 report("%s (built %s)", VERSION, __DATE__);
2255                 return TRUE;
2256
2257         case REQ_SCREEN_RESIZE:
2258                 resize_display();
2259                 /* Fall-through */
2260         case REQ_SCREEN_REDRAW:
2261                 redraw_display();
2262                 break;
2263
2264         case REQ_NONE:
2265                 doupdate();
2266                 return TRUE;
2267
2268         case REQ_VIEW_CLOSE:
2269                 /* XXX: Mark closed views by letting view->parent point to the
2270                  * view itself. Parents to closed view should never be
2271                  * followed. */
2272                 if (view->parent &&
2273                     view->parent->parent != view->parent) {
2274                         memset(display, 0, sizeof(display));
2275                         current_view = 0;
2276                         display[current_view] = view->parent;
2277                         view->parent = view;
2278                         resize_display();
2279                         redraw_display();
2280                         break;
2281                 }
2282                 /* Fall-through */
2283         case REQ_QUIT:
2284                 return FALSE;
2285
2286         default:
2287                 /* An unknown key will show most commonly used commands. */
2288                 report("Unknown key, press 'h' for help");
2289                 return TRUE;
2290         }
2291
2292         return TRUE;
2293 }
2294
2295
2296 /*
2297  * Pager backend
2298  */
2299
2300 static bool
2301 pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2302 {
2303         char *text = line->data;
2304         enum line_type type = line->type;
2305         int textlen = strlen(text);
2306         int attr;
2307
2308         wmove(view->win, lineno, 0);
2309
2310         if (selected) {
2311                 type = LINE_CURSOR;
2312                 wchgat(view->win, -1, 0, type, NULL);
2313         }
2314
2315         attr = get_line_attr(type);
2316         wattrset(view->win, attr);
2317
2318         if (opt_line_number || opt_tab_size < TABSIZE) {
2319                 static char spaces[] = "                    ";
2320                 int col_offset = 0, col = 0;
2321
2322                 if (opt_line_number) {
2323                         unsigned long real_lineno = view->offset + lineno + 1;
2324
2325                         if (real_lineno == 1 ||
2326                             (real_lineno % opt_num_interval) == 0) {
2327                                 wprintw(view->win, "%.*d", view->digits, real_lineno);
2328
2329                         } else {
2330                                 waddnstr(view->win, spaces,
2331                                          MIN(view->digits, STRING_SIZE(spaces)));
2332                         }
2333                         waddstr(view->win, ": ");
2334                         col_offset = view->digits + 2;
2335                 }
2336
2337                 while (text && col_offset + col < view->width) {
2338                         int cols_max = view->width - col_offset - col;
2339                         char *pos = text;
2340                         int cols;
2341
2342                         if (*text == '\t') {
2343                                 text++;
2344                                 assert(sizeof(spaces) > TABSIZE);
2345                                 pos = spaces;
2346                                 cols = opt_tab_size - (col % opt_tab_size);
2347
2348                         } else {
2349                                 text = strchr(text, '\t');
2350                                 cols = line ? text - pos : strlen(pos);
2351                         }
2352
2353                         waddnstr(view->win, pos, MIN(cols, cols_max));
2354                         col += cols;
2355                 }
2356
2357         } else {
2358                 int col = 0, pos = 0;
2359
2360                 for (; pos < textlen && col < view->width; pos++, col++)
2361                         if (text[pos] == '\t')
2362                                 col += TABSIZE - (col % TABSIZE) - 1;
2363
2364                 waddnstr(view->win, text, pos);
2365         }
2366
2367         return TRUE;
2368 }
2369
2370 static bool
2371 add_describe_ref(char *buf, size_t *bufpos, char *commit_id, const char *sep)
2372 {
2373         char refbuf[SIZEOF_STR];
2374         char *ref = NULL;
2375         FILE *pipe;
2376
2377         if (!string_format(refbuf, "git describe %s 2>/dev/null", commit_id))
2378                 return TRUE;
2379
2380         pipe = popen(refbuf, "r");
2381         if (!pipe)
2382                 return TRUE;
2383
2384         if ((ref = fgets(refbuf, sizeof(refbuf), pipe)))
2385                 ref = chomp_string(ref);
2386         pclose(pipe);
2387
2388         if (!ref || !*ref)
2389                 return TRUE;
2390
2391         /* This is the only fatal call, since it can "corrupt" the buffer. */
2392         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
2393                 return FALSE;
2394
2395         return TRUE;
2396 }
2397
2398 static void
2399 add_pager_refs(struct view *view, struct line *line)
2400 {
2401         char buf[SIZEOF_STR];
2402         char *commit_id = line->data + STRING_SIZE("commit ");
2403         struct ref **refs;
2404         size_t bufpos = 0, refpos = 0;
2405         const char *sep = "Refs: ";
2406         bool is_tag = FALSE;
2407
2408         assert(line->type == LINE_COMMIT);
2409
2410         refs = get_refs(commit_id);
2411         if (!refs) {
2412                 if (view == VIEW(REQ_VIEW_DIFF))
2413                         goto try_add_describe_ref;
2414                 return;
2415         }
2416
2417         do {
2418                 struct ref *ref = refs[refpos];
2419                 char *fmt = ref->tag    ? "%s[%s]" :
2420                             ref->remote ? "%s<%s>" : "%s%s";
2421
2422                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
2423                         return;
2424                 sep = ", ";
2425                 if (ref->tag)
2426                         is_tag = TRUE;
2427         } while (refs[refpos++]->next);
2428
2429         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
2430 try_add_describe_ref:
2431                 /* Add <tag>-g<commit_id> "fake" reference. */
2432                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
2433                         return;
2434         }
2435
2436         if (bufpos == 0)
2437                 return;
2438
2439         if (!realloc_lines(view, view->line_size + 1))
2440                 return;
2441
2442         add_line_text(view, buf, LINE_PP_REFS);
2443 }
2444
2445 static bool
2446 pager_read(struct view *view, char *data)
2447 {
2448         struct line *line;
2449
2450         if (!data)
2451                 return TRUE;
2452
2453         line = add_line_text(view, data, get_line_type(data));
2454         if (!line)
2455                 return FALSE;
2456
2457         if (line->type == LINE_COMMIT &&
2458             (view == VIEW(REQ_VIEW_DIFF) ||
2459              view == VIEW(REQ_VIEW_LOG)))
2460                 add_pager_refs(view, line);
2461
2462         return TRUE;
2463 }
2464
2465 static bool
2466 pager_enter(struct view *view, struct line *line)
2467 {
2468         int split = 0;
2469
2470         if (line->type == LINE_COMMIT &&
2471            (view == VIEW(REQ_VIEW_LOG) ||
2472             view == VIEW(REQ_VIEW_PAGER))) {
2473                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
2474                 split = 1;
2475         }
2476
2477         /* Always scroll the view even if it was split. That way
2478          * you can use Enter to scroll through the log view and
2479          * split open each commit diff. */
2480         scroll_view(view, REQ_SCROLL_LINE_DOWN);
2481
2482         /* FIXME: A minor workaround. Scrolling the view will call report("")
2483          * but if we are scrolling a non-current view this won't properly
2484          * update the view title. */
2485         if (split)
2486                 update_view_title(view);
2487
2488         return TRUE;
2489 }
2490
2491 static bool
2492 pager_grep(struct view *view, struct line *line)
2493 {
2494         regmatch_t pmatch;
2495         char *text = line->data;
2496
2497         if (!*text)
2498                 return FALSE;
2499
2500         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
2501                 return FALSE;
2502
2503         return TRUE;
2504 }
2505
2506 static void
2507 pager_select(struct view *view, struct line *line)
2508 {
2509         if (line->type == LINE_COMMIT) {
2510                 char *text = line->data + STRING_SIZE("commit ");
2511
2512                 if (view != VIEW(REQ_VIEW_PAGER))
2513                         string_copy_rev(view->ref, text);
2514                 string_copy_rev(ref_commit, text);
2515         }
2516 }
2517
2518 static struct view_ops pager_ops = {
2519         "line",
2520         pager_draw,
2521         pager_read,
2522         pager_enter,
2523         pager_grep,
2524         pager_select,
2525 };
2526
2527
2528 /*
2529  * Tree backend
2530  */
2531
2532 /* Parse output from git-ls-tree(1):
2533  *
2534  * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile
2535  * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README
2536  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
2537  * 100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38 web.conf
2538  */
2539
2540 #define SIZEOF_TREE_ATTR \
2541         STRING_SIZE("100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38\t")
2542
2543 #define TREE_UP_FORMAT "040000 tree %s\t.."
2544
2545 static int
2546 tree_compare_entry(enum line_type type1, char *name1,
2547                    enum line_type type2, char *name2)
2548 {
2549         if (type1 != type2) {
2550                 if (type1 == LINE_TREE_DIR)
2551                         return -1;
2552                 return 1;
2553         }
2554
2555         return strcmp(name1, name2);
2556 }
2557
2558 static bool
2559 tree_read(struct view *view, char *text)
2560 {
2561         size_t textlen = text ? strlen(text) : 0;
2562         char buf[SIZEOF_STR];
2563         unsigned long pos;
2564         enum line_type type;
2565         bool first_read = view->lines == 0;
2566
2567         if (textlen <= SIZEOF_TREE_ATTR)
2568                 return FALSE;
2569
2570         type = text[STRING_SIZE("100644 ")] == 't'
2571              ? LINE_TREE_DIR : LINE_TREE_FILE;
2572
2573         if (first_read) {
2574                 /* Add path info line */
2575                 if (!string_format(buf, "Directory path /%s", opt_path) ||
2576                     !realloc_lines(view, view->line_size + 1) ||
2577                     !add_line_text(view, buf, LINE_DEFAULT))
2578                         return FALSE;
2579
2580                 /* Insert "link" to parent directory. */
2581                 if (*opt_path) {
2582                         if (!string_format(buf, TREE_UP_FORMAT, view->ref) ||
2583                             !realloc_lines(view, view->line_size + 1) ||
2584                             !add_line_text(view, buf, LINE_TREE_DIR))
2585                                 return FALSE;
2586                 }
2587         }
2588
2589         /* Strip the path part ... */
2590         if (*opt_path) {
2591                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
2592                 size_t striplen = strlen(opt_path);
2593                 char *path = text + SIZEOF_TREE_ATTR;
2594
2595                 if (pathlen > striplen)
2596                         memmove(path, path + striplen,
2597                                 pathlen - striplen + 1);
2598         }
2599
2600         /* Skip "Directory ..." and ".." line. */
2601         for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
2602                 struct line *line = &view->line[pos];
2603                 char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR;
2604                 char *path2 = text + SIZEOF_TREE_ATTR;
2605                 int cmp = tree_compare_entry(line->type, path1, type, path2);
2606
2607                 if (cmp <= 0)
2608                         continue;
2609
2610                 text = strdup(text);
2611                 if (!text)
2612                         return FALSE;
2613
2614                 if (view->lines > pos)
2615                         memmove(&view->line[pos + 1], &view->line[pos],
2616                                 (view->lines - pos) * sizeof(*line));
2617
2618                 line = &view->line[pos];
2619                 line->data = text;
2620                 line->type = type;
2621                 view->lines++;
2622                 return TRUE;
2623         }
2624
2625         if (!add_line_text(view, text, type))
2626                 return FALSE;
2627
2628         /* Move the current line to the first tree entry. */
2629         if (first_read)
2630                 view->lineno++;
2631
2632         return TRUE;
2633 }
2634
2635 static bool
2636 tree_enter(struct view *view, struct line *line)
2637 {
2638         enum open_flags flags;
2639         enum request request;
2640
2641         switch (line->type) {
2642         case LINE_TREE_DIR:
2643                 /* Depending on whether it is a subdir or parent (updir?) link
2644                  * mangle the path buffer. */
2645                 if (line == &view->line[1] && *opt_path) {
2646                         size_t path_len = strlen(opt_path);
2647                         char *dirsep = opt_path + path_len - 1;
2648
2649                         while (dirsep > opt_path && dirsep[-1] != '/')
2650                                 dirsep--;
2651
2652                         dirsep[0] = 0;
2653
2654                 } else {
2655                         size_t pathlen = strlen(opt_path);
2656                         size_t origlen = pathlen;
2657                         char *data = line->data;
2658                         char *basename = data + SIZEOF_TREE_ATTR;
2659
2660                         if (!string_format_from(opt_path, &pathlen, "%s/", basename)) {
2661                                 opt_path[origlen] = 0;
2662                                 return TRUE;
2663                         }
2664                 }
2665
2666                 /* Trees and subtrees share the same ID, so they are not not
2667                  * unique like blobs. */
2668                 flags = OPEN_RELOAD;
2669                 request = REQ_VIEW_TREE;
2670                 break;
2671
2672         case LINE_TREE_FILE:
2673                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
2674                 request = REQ_VIEW_BLOB;
2675                 break;
2676
2677         default:
2678                 return TRUE;
2679         }
2680
2681         open_view(view, request, flags);
2682
2683         return TRUE;
2684 }
2685
2686 static void
2687 tree_select(struct view *view, struct line *line)
2688 {
2689         char *text = line->data + STRING_SIZE("100644 blob ");
2690
2691         if (line->type == LINE_TREE_FILE) {
2692                 string_copy_rev(ref_blob, text);
2693
2694         } else if (line->type != LINE_TREE_DIR) {
2695                 return;
2696         }
2697
2698         string_copy_rev(view->ref, text);
2699 }
2700
2701 static struct view_ops tree_ops = {
2702         "file",
2703         pager_draw,
2704         tree_read,
2705         tree_enter,
2706         pager_grep,
2707         tree_select,
2708 };
2709
2710 static bool
2711 blob_read(struct view *view, char *line)
2712 {
2713         return add_line_text(view, line, LINE_DEFAULT);
2714 }
2715
2716 static struct view_ops blob_ops = {
2717         "line",
2718         pager_draw,
2719         blob_read,
2720         pager_enter,
2721         pager_grep,
2722         pager_select,
2723 };
2724
2725
2726 /*
2727  * Revision graph
2728  */
2729
2730 struct commit {
2731         char id[SIZEOF_REV];            /* SHA1 ID. */
2732         char title[128];                /* First line of the commit message. */
2733         char author[75];                /* Author of the commit. */
2734         struct tm time;                 /* Date from the author ident. */
2735         struct ref **refs;              /* Repository references. */
2736         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
2737         size_t graph_size;              /* The width of the graph array. */
2738 };
2739
2740 /* Size of rev graph with no  "padding" columns */
2741 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
2742
2743 struct rev_graph {
2744         struct rev_graph *prev, *next, *parents;
2745         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
2746         size_t size;
2747         struct commit *commit;
2748         size_t pos;
2749 };
2750
2751 /* Parents of the commit being visualized. */
2752 static struct rev_graph graph_parents[4];
2753
2754 /* The current stack of revisions on the graph. */
2755 static struct rev_graph graph_stacks[4] = {
2756         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
2757         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
2758         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
2759         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
2760 };
2761
2762 static inline bool
2763 graph_parent_is_merge(struct rev_graph *graph)
2764 {
2765         return graph->parents->size > 1;
2766 }
2767
2768 static inline void
2769 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
2770 {
2771         struct commit *commit = graph->commit;
2772
2773         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
2774                 commit->graph[commit->graph_size++] = symbol;
2775 }
2776
2777 static void
2778 done_rev_graph(struct rev_graph *graph)
2779 {
2780         if (graph_parent_is_merge(graph) &&
2781             graph->pos < graph->size - 1 &&
2782             graph->next->size == graph->size + graph->parents->size - 1) {
2783                 size_t i = graph->pos + graph->parents->size - 1;
2784
2785                 graph->commit->graph_size = i * 2;
2786                 while (i < graph->next->size - 1) {
2787                         append_to_rev_graph(graph, ' ');
2788                         append_to_rev_graph(graph, '\\');
2789                         i++;
2790                 }
2791         }
2792
2793         graph->size = graph->pos = 0;
2794         graph->commit = NULL;
2795         memset(graph->parents, 0, sizeof(*graph->parents));
2796 }
2797
2798 static void
2799 push_rev_graph(struct rev_graph *graph, char *parent)
2800 {
2801         int i;
2802
2803         /* "Collapse" duplicate parents lines.
2804          *
2805          * FIXME: This needs to also update update the drawn graph but
2806          * for now it just serves as a method for pruning graph lines. */
2807         for (i = 0; i < graph->size; i++)
2808                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
2809                         return;
2810
2811         if (graph->size < SIZEOF_REVITEMS) {
2812                 string_ncopy(graph->rev[graph->size++], parent, SIZEOF_REV);
2813         }
2814 }
2815
2816 static chtype
2817 get_rev_graph_symbol(struct rev_graph *graph)
2818 {
2819         chtype symbol;
2820
2821         if (graph->parents->size == 0)
2822                 symbol = REVGRAPH_INIT;
2823         else if (graph_parent_is_merge(graph))
2824                 symbol = REVGRAPH_MERGE;
2825         else if (graph->pos >= graph->size)
2826                 symbol = REVGRAPH_BRANCH;
2827         else
2828                 symbol = REVGRAPH_COMMIT;
2829
2830         return symbol;
2831 }
2832
2833 static void
2834 draw_rev_graph(struct rev_graph *graph)
2835 {
2836         struct rev_filler {
2837                 chtype separator, line;
2838         };
2839         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
2840         static struct rev_filler fillers[] = {
2841                 { ' ',  REVGRAPH_LINE },
2842                 { '`',  '.' },
2843                 { '\'', ' ' },
2844                 { '/',  ' ' },
2845         };
2846         chtype symbol = get_rev_graph_symbol(graph);
2847         struct rev_filler *filler;
2848         size_t i;
2849
2850         filler = &fillers[DEFAULT];
2851
2852         for (i = 0; i < graph->pos; i++) {
2853                 append_to_rev_graph(graph, filler->line);
2854                 if (graph_parent_is_merge(graph->prev) &&
2855                     graph->prev->pos == i)
2856                         filler = &fillers[RSHARP];
2857
2858                 append_to_rev_graph(graph, filler->separator);
2859         }
2860
2861         /* Place the symbol for this revision. */
2862         append_to_rev_graph(graph, symbol);
2863
2864         if (graph->prev->size > graph->size)
2865                 filler = &fillers[RDIAG];
2866         else
2867                 filler = &fillers[DEFAULT];
2868
2869         i++;
2870
2871         for (; i < graph->size; i++) {
2872                 append_to_rev_graph(graph, filler->separator);
2873                 append_to_rev_graph(graph, filler->line);
2874                 if (graph_parent_is_merge(graph->prev) &&
2875                     i < graph->prev->pos + graph->parents->size)
2876                         filler = &fillers[RSHARP];
2877                 if (graph->prev->size > graph->size)
2878                         filler = &fillers[LDIAG];
2879         }
2880
2881         if (graph->prev->size > graph->size) {
2882                 append_to_rev_graph(graph, filler->separator);
2883                 if (filler->line != ' ')
2884                         append_to_rev_graph(graph, filler->line);
2885         }
2886 }
2887
2888 /* Prepare the next rev graph */
2889 static void
2890 prepare_rev_graph(struct rev_graph *graph)
2891 {
2892         size_t i;
2893
2894         /* First, traverse all lines of revisions up to the active one. */
2895         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
2896                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
2897                         break;
2898
2899                 push_rev_graph(graph->next, graph->rev[graph->pos]);
2900         }
2901
2902         /* Interleave the new revision parent(s). */
2903         for (i = 0; i < graph->parents->size; i++)
2904                 push_rev_graph(graph->next, graph->parents->rev[i]);
2905
2906         /* Lastly, put any remaining revisions. */
2907         for (i = graph->pos + 1; i < graph->size; i++)
2908                 push_rev_graph(graph->next, graph->rev[i]);
2909 }
2910
2911 static void
2912 update_rev_graph(struct rev_graph *graph)
2913 {
2914         /* If this is the finalizing update ... */
2915         if (graph->commit)
2916                 prepare_rev_graph(graph);
2917
2918         /* Graph visualization needs a one rev look-ahead,
2919          * so the first update doesn't visualize anything. */
2920         if (!graph->prev->commit)
2921                 return;
2922
2923         draw_rev_graph(graph->prev);
2924         done_rev_graph(graph->prev->prev);
2925 }
2926
2927
2928 /*
2929  * Main view backend
2930  */
2931
2932 static bool
2933 main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2934 {
2935         char buf[DATE_COLS + 1];
2936         struct commit *commit = line->data;
2937         enum line_type type;
2938         int col = 0;
2939         size_t timelen;
2940         size_t authorlen;
2941         int trimmed = 1;
2942
2943         if (!*commit->author)
2944                 return FALSE;
2945
2946         wmove(view->win, lineno, col);
2947
2948         if (selected) {
2949                 type = LINE_CURSOR;
2950                 wattrset(view->win, get_line_attr(type));
2951                 wchgat(view->win, -1, 0, type, NULL);
2952
2953         } else {
2954                 type = LINE_MAIN_COMMIT;
2955                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
2956         }
2957
2958         timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
2959         waddnstr(view->win, buf, timelen);
2960         waddstr(view->win, " ");
2961
2962         col += DATE_COLS;
2963         wmove(view->win, lineno, col);
2964         if (type != LINE_CURSOR)
2965                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
2966
2967         if (opt_utf8) {
2968                 authorlen = utf8_length(commit->author, AUTHOR_COLS - 2, &col, &trimmed);
2969         } else {
2970                 authorlen = strlen(commit->author);
2971                 if (authorlen > AUTHOR_COLS - 2) {
2972                         authorlen = AUTHOR_COLS - 2;
2973                         trimmed = 1;
2974                 }
2975         }
2976
2977         if (trimmed) {
2978                 waddnstr(view->win, commit->author, authorlen);
2979                 if (type != LINE_CURSOR)
2980                         wattrset(view->win, get_line_attr(LINE_MAIN_DELIM));
2981                 waddch(view->win, '~');
2982         } else {
2983                 waddstr(view->win, commit->author);
2984         }
2985
2986         col += AUTHOR_COLS;
2987         if (type != LINE_CURSOR)
2988                 wattrset(view->win, A_NORMAL);
2989
2990         if (opt_rev_graph && commit->graph_size) {
2991                 size_t i;
2992
2993                 wmove(view->win, lineno, col);
2994                 /* Using waddch() instead of waddnstr() ensures that
2995                  * they'll be rendered correctly for the cursor line. */
2996                 for (i = 0; i < commit->graph_size; i++)
2997                         waddch(view->win, commit->graph[i]);
2998
2999                 waddch(view->win, ' ');
3000                 col += commit->graph_size + 1;
3001         }
3002
3003         wmove(view->win, lineno, col);
3004
3005         if (commit->refs) {
3006                 size_t i = 0;
3007
3008                 do {
3009                         if (type == LINE_CURSOR)
3010                                 ;
3011                         else if (commit->refs[i]->tag)
3012                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
3013                         else if (commit->refs[i]->remote)
3014                                 wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
3015                         else
3016                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
3017                         waddstr(view->win, "[");
3018                         waddstr(view->win, commit->refs[i]->name);
3019                         waddstr(view->win, "]");
3020                         if (type != LINE_CURSOR)
3021                                 wattrset(view->win, A_NORMAL);
3022                         waddstr(view->win, " ");
3023                         col += strlen(commit->refs[i]->name) + STRING_SIZE("[] ");
3024                 } while (commit->refs[i++]->next);
3025         }
3026
3027         if (type != LINE_CURSOR)
3028                 wattrset(view->win, get_line_attr(type));
3029
3030         {
3031                 int titlelen = strlen(commit->title);
3032
3033                 if (col + titlelen > view->width)
3034                         titlelen = view->width - col;
3035
3036                 waddnstr(view->win, commit->title, titlelen);
3037         }
3038
3039         return TRUE;
3040 }
3041
3042 /* Reads git log --pretty=raw output and parses it into the commit struct. */
3043 static bool
3044 main_read(struct view *view, char *line)
3045 {
3046         static struct rev_graph *graph = graph_stacks;
3047         enum line_type type;
3048         struct commit *commit = view->lines
3049                               ? view->line[view->lines - 1].data : NULL;
3050
3051         if (!line) {
3052                 update_rev_graph(graph);
3053                 return TRUE;
3054         }
3055
3056         type = get_line_type(line);
3057
3058         switch (type) {
3059         case LINE_COMMIT:
3060                 commit = calloc(1, sizeof(struct commit));
3061                 if (!commit)
3062                         return FALSE;
3063
3064                 line += STRING_SIZE("commit ");
3065
3066                 view->line[view->lines++].data = commit;
3067                 string_copy_rev(commit->id, line);
3068                 commit->refs = get_refs(commit->id);
3069                 graph->commit = commit;
3070                 break;
3071
3072         case LINE_PARENT:
3073                 if (commit) {
3074                         line += STRING_SIZE("parent ");
3075                         push_rev_graph(graph->parents, line);
3076                 }
3077                 break;
3078
3079         case LINE_AUTHOR:
3080         {
3081                 /* Parse author lines where the name may be empty:
3082                  *      author  <email@address.tld> 1138474660 +0100
3083                  */
3084                 char *ident = line + STRING_SIZE("author ");
3085                 char *nameend = strchr(ident, '<');
3086                 char *emailend = strchr(ident, '>');
3087
3088                 if (!commit || !nameend || !emailend)
3089                         break;
3090
3091                 update_rev_graph(graph);
3092                 graph = graph->next;
3093
3094                 *nameend = *emailend = 0;
3095                 ident = chomp_string(ident);
3096                 if (!*ident) {
3097                         ident = chomp_string(nameend + 1);
3098                         if (!*ident)
3099                                 ident = "Unknown";
3100                 }
3101
3102                 string_copy(commit->author, ident);
3103
3104                 /* Parse epoch and timezone */
3105                 if (emailend[1] == ' ') {
3106                         char *secs = emailend + 2;
3107                         char *zone = strchr(secs, ' ');
3108                         time_t time = (time_t) atol(secs);
3109
3110                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
3111                                 long tz;
3112
3113                                 zone++;
3114                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
3115                                 tz += ('0' - zone[2]) * 60 * 60;
3116                                 tz += ('0' - zone[3]) * 60;
3117                                 tz += ('0' - zone[4]) * 60;
3118
3119                                 if (zone[0] == '-')
3120                                         tz = -tz;
3121
3122                                 time -= tz;
3123                         }
3124
3125                         gmtime_r(&time, &commit->time);
3126                 }
3127                 break;
3128         }
3129         default:
3130                 if (!commit)
3131                         break;
3132
3133                 /* Fill in the commit title if it has not already been set. */
3134                 if (commit->title[0])
3135                         break;
3136
3137                 /* Require titles to start with a non-space character at the
3138                  * offset used by git log. */
3139                 if (strncmp(line, "    ", 4))
3140                         break;
3141                 line += 4;
3142                 /* Well, if the title starts with a whitespace character,
3143                  * try to be forgiving.  Otherwise we end up with no title. */
3144                 while (isspace(*line))
3145                         line++;
3146                 if (*line == '\0')
3147                         break;
3148                 /* FIXME: More graceful handling of titles; append "..." to
3149                  * shortened titles, etc. */
3150
3151                 string_copy(commit->title, line);
3152         }
3153
3154         return TRUE;
3155 }
3156
3157 static bool
3158 main_enter(struct view *view, struct line *line)
3159 {
3160         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3161
3162         open_view(view, REQ_VIEW_DIFF, flags);
3163         return TRUE;
3164 }
3165
3166 static bool
3167 main_grep(struct view *view, struct line *line)
3168 {
3169         struct commit *commit = line->data;
3170         enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
3171         char buf[DATE_COLS + 1];
3172         regmatch_t pmatch;
3173
3174         for (state = S_TITLE; state < S_END; state++) {
3175                 char *text;
3176
3177                 switch (state) {
3178                 case S_TITLE:   text = commit->title;   break;
3179                 case S_AUTHOR:  text = commit->author;  break;
3180                 case S_DATE:
3181                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
3182                                 continue;
3183                         text = buf;
3184                         break;
3185
3186                 default:
3187                         return FALSE;
3188                 }
3189
3190                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
3191                         return TRUE;
3192         }
3193
3194         return FALSE;
3195 }
3196
3197 static void
3198 main_select(struct view *view, struct line *line)
3199 {
3200         struct commit *commit = line->data;
3201
3202         string_copy_rev(view->ref, commit->id);
3203         string_copy_rev(ref_commit, view->ref);
3204 }
3205
3206 static struct view_ops main_ops = {
3207         "commit",
3208         main_draw,
3209         main_read,
3210         main_enter,
3211         main_grep,
3212         main_select,
3213 };
3214
3215
3216 /*
3217  * Unicode / UTF-8 handling
3218  *
3219  * NOTE: Much of the following code for dealing with unicode is derived from
3220  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
3221  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
3222  */
3223
3224 /* I've (over)annotated a lot of code snippets because I am not entirely
3225  * confident that the approach taken by this small UTF-8 interface is correct.
3226  * --jonas */
3227
3228 static inline int
3229 unicode_width(unsigned long c)
3230 {
3231         if (c >= 0x1100 &&
3232            (c <= 0x115f                         /* Hangul Jamo */
3233             || c == 0x2329
3234             || c == 0x232a
3235             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
3236                                                 /* CJK ... Yi */
3237             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
3238             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
3239             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
3240             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
3241             || (c >= 0xffe0  && c <= 0xffe6)
3242             || (c >= 0x20000 && c <= 0x2fffd)
3243             || (c >= 0x30000 && c <= 0x3fffd)))
3244                 return 2;
3245
3246         return 1;
3247 }
3248
3249 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
3250  * Illegal bytes are set one. */
3251 static const unsigned char utf8_bytes[256] = {
3252         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,
3253         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,
3254         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,
3255         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,
3256         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,
3257         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,
3258         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,
3259         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,
3260 };
3261
3262 /* Decode UTF-8 multi-byte representation into a unicode character. */
3263 static inline unsigned long
3264 utf8_to_unicode(const char *string, size_t length)
3265 {
3266         unsigned long unicode;
3267
3268         switch (length) {
3269         case 1:
3270                 unicode  =   string[0];
3271                 break;
3272         case 2:
3273                 unicode  =  (string[0] & 0x1f) << 6;
3274                 unicode +=  (string[1] & 0x3f);
3275                 break;
3276         case 3:
3277                 unicode  =  (string[0] & 0x0f) << 12;
3278                 unicode += ((string[1] & 0x3f) << 6);
3279                 unicode +=  (string[2] & 0x3f);
3280                 break;
3281         case 4:
3282                 unicode  =  (string[0] & 0x0f) << 18;
3283                 unicode += ((string[1] & 0x3f) << 12);
3284                 unicode += ((string[2] & 0x3f) << 6);
3285                 unicode +=  (string[3] & 0x3f);
3286                 break;
3287         case 5:
3288                 unicode  =  (string[0] & 0x0f) << 24;
3289                 unicode += ((string[1] & 0x3f) << 18);
3290                 unicode += ((string[2] & 0x3f) << 12);
3291                 unicode += ((string[3] & 0x3f) << 6);
3292                 unicode +=  (string[4] & 0x3f);
3293                 break;
3294         case 6:
3295                 unicode  =  (string[0] & 0x01) << 30;
3296                 unicode += ((string[1] & 0x3f) << 24);
3297                 unicode += ((string[2] & 0x3f) << 18);
3298                 unicode += ((string[3] & 0x3f) << 12);
3299                 unicode += ((string[4] & 0x3f) << 6);
3300                 unicode +=  (string[5] & 0x3f);
3301                 break;
3302         default:
3303                 die("Invalid unicode length");
3304         }
3305
3306         /* Invalid characters could return the special 0xfffd value but NUL
3307          * should be just as good. */
3308         return unicode > 0xffff ? 0 : unicode;
3309 }
3310
3311 /* Calculates how much of string can be shown within the given maximum width
3312  * and sets trimmed parameter to non-zero value if all of string could not be
3313  * shown.
3314  *
3315  * Additionally, adds to coloffset how many many columns to move to align with
3316  * the expected position. Takes into account how multi-byte and double-width
3317  * characters will effect the cursor position.
3318  *
3319  * Returns the number of bytes to output from string to satisfy max_width. */
3320 static size_t
3321 utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed)
3322 {
3323         const char *start = string;
3324         const char *end = strchr(string, '\0');
3325         size_t mbwidth = 0;
3326         size_t width = 0;
3327
3328         *trimmed = 0;
3329
3330         while (string < end) {
3331                 int c = *(unsigned char *) string;
3332                 unsigned char bytes = utf8_bytes[c];
3333                 size_t ucwidth;
3334                 unsigned long unicode;
3335
3336                 if (string + bytes > end)
3337                         break;
3338
3339                 /* Change representation to figure out whether
3340                  * it is a single- or double-width character. */
3341
3342                 unicode = utf8_to_unicode(string, bytes);
3343                 /* FIXME: Graceful handling of invalid unicode character. */
3344                 if (!unicode)
3345                         break;
3346
3347                 ucwidth = unicode_width(unicode);
3348                 width  += ucwidth;
3349                 if (width > max_width) {
3350                         *trimmed = 1;
3351                         break;
3352                 }
3353
3354                 /* The column offset collects the differences between the
3355                  * number of bytes encoding a character and the number of
3356                  * columns will be used for rendering said character.
3357                  *
3358                  * So if some character A is encoded in 2 bytes, but will be
3359                  * represented on the screen using only 1 byte this will and up
3360                  * adding 1 to the multi-byte column offset.
3361                  *
3362                  * Assumes that no double-width character can be encoding in
3363                  * less than two bytes. */
3364                 if (bytes > ucwidth)
3365                         mbwidth += bytes - ucwidth;
3366
3367                 string  += bytes;
3368         }
3369
3370         *coloffset += mbwidth;
3371
3372         return string - start;
3373 }
3374
3375
3376 /*
3377  * Status management
3378  */
3379
3380 /* Whether or not the curses interface has been initialized. */
3381 static bool cursed = FALSE;
3382
3383 /* The status window is used for polling keystrokes. */
3384 static WINDOW *status_win;
3385
3386 static bool status_empty = TRUE;
3387
3388 /* Update status and title window. */
3389 static void
3390 report(const char *msg, ...)
3391 {
3392         struct view *view = display[current_view];
3393
3394         if (input_mode)
3395                 return;
3396
3397         if (!status_empty || *msg) {
3398                 va_list args;
3399
3400                 va_start(args, msg);
3401
3402                 wmove(status_win, 0, 0);
3403                 if (*msg) {
3404                         vwprintw(status_win, msg, args);
3405                         status_empty = FALSE;
3406                 } else {
3407                         status_empty = TRUE;
3408                 }
3409                 wclrtoeol(status_win);
3410                 wrefresh(status_win);
3411
3412                 va_end(args);
3413         }
3414
3415         update_view_title(view);
3416         update_display_cursor(view);
3417 }
3418
3419 /* Controls when nodelay should be in effect when polling user input. */
3420 static void
3421 set_nonblocking_input(bool loading)
3422 {
3423         static unsigned int loading_views;
3424
3425         if ((loading == FALSE && loading_views-- == 1) ||
3426             (loading == TRUE  && loading_views++ == 0))
3427                 nodelay(status_win, loading);
3428 }
3429
3430 static void
3431 init_display(void)
3432 {
3433         int x, y;
3434
3435         /* Initialize the curses library */
3436         if (isatty(STDIN_FILENO)) {
3437                 cursed = !!initscr();
3438         } else {
3439                 /* Leave stdin and stdout alone when acting as a pager. */
3440                 FILE *io = fopen("/dev/tty", "r+");
3441
3442                 if (!io)
3443                         die("Failed to open /dev/tty");
3444                 cursed = !!newterm(NULL, io, io);
3445         }
3446
3447         if (!cursed)
3448                 die("Failed to initialize curses");
3449
3450         nonl();         /* Tell curses not to do NL->CR/NL on output */
3451         cbreak();       /* Take input chars one at a time, no wait for \n */
3452         noecho();       /* Don't echo input */
3453         leaveok(stdscr, TRUE);
3454
3455         if (has_colors())
3456                 init_colors();
3457
3458         getmaxyx(stdscr, y, x);
3459         status_win = newwin(1, 0, y - 1, 0);
3460         if (!status_win)
3461                 die("Failed to create status window");
3462
3463         /* Enable keyboard mapping */
3464         keypad(status_win, TRUE);
3465         wbkgdset(status_win, get_line_attr(LINE_STATUS));
3466 }
3467
3468 static char *
3469 read_prompt(const char *prompt)
3470 {
3471         enum { READING, STOP, CANCEL } status = READING;
3472         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
3473         int pos = 0;
3474
3475         while (status == READING) {
3476                 struct view *view;
3477                 int i, key;
3478
3479                 input_mode = TRUE;
3480
3481                 foreach_view (view, i)
3482                         update_view(view);
3483
3484                 input_mode = FALSE;
3485
3486                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
3487                 wclrtoeol(status_win);
3488
3489                 /* Refresh, accept single keystroke of input */
3490                 key = wgetch(status_win);
3491                 switch (key) {
3492                 case KEY_RETURN:
3493                 case KEY_ENTER:
3494                 case '\n':
3495                         status = pos ? STOP : CANCEL;
3496                         break;
3497
3498                 case KEY_BACKSPACE:
3499                         if (pos > 0)
3500                                 pos--;
3501                         else
3502                                 status = CANCEL;
3503                         break;
3504
3505                 case KEY_ESC:
3506                         status = CANCEL;
3507                         break;
3508
3509                 case ERR:
3510                         break;
3511
3512                 default:
3513                         if (pos >= sizeof(buf)) {
3514                                 report("Input string too long");
3515                                 return NULL;
3516                         }
3517
3518                         if (isprint(key))
3519                                 buf[pos++] = (char) key;
3520                 }
3521         }
3522
3523         /* Clear the status window */
3524         status_empty = FALSE;
3525         report("");
3526
3527         if (status == CANCEL)
3528                 return NULL;
3529
3530         buf[pos++] = 0;
3531
3532         return buf;
3533 }
3534
3535 /*
3536  * Repository references
3537  */
3538
3539 static struct ref *refs;
3540 static size_t refs_size;
3541
3542 /* Id <-> ref store */
3543 static struct ref ***id_refs;
3544 static size_t id_refs_size;
3545
3546 static struct ref **
3547 get_refs(char *id)
3548 {
3549         struct ref ***tmp_id_refs;
3550         struct ref **ref_list = NULL;
3551         size_t ref_list_size = 0;
3552         size_t i;
3553
3554         for (i = 0; i < id_refs_size; i++)
3555                 if (!strcmp(id, id_refs[i][0]->id))
3556                         return id_refs[i];
3557
3558         tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
3559         if (!tmp_id_refs)
3560                 return NULL;
3561
3562         id_refs = tmp_id_refs;
3563
3564         for (i = 0; i < refs_size; i++) {
3565                 struct ref **tmp;
3566
3567                 if (strcmp(id, refs[i].id))
3568                         continue;
3569
3570                 tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
3571                 if (!tmp) {
3572                         if (ref_list)
3573                                 free(ref_list);
3574                         return NULL;
3575                 }
3576
3577                 ref_list = tmp;
3578                 if (ref_list_size > 0)
3579                         ref_list[ref_list_size - 1]->next = 1;
3580                 ref_list[ref_list_size] = &refs[i];
3581
3582                 /* XXX: The properties of the commit chains ensures that we can
3583                  * safely modify the shared ref. The repo references will
3584                  * always be similar for the same id. */
3585                 ref_list[ref_list_size]->next = 0;
3586                 ref_list_size++;
3587         }
3588
3589         if (ref_list)
3590                 id_refs[id_refs_size++] = ref_list;
3591
3592         return ref_list;
3593 }
3594
3595 static int
3596 read_ref(char *id, int idlen, char *name, int namelen)
3597 {
3598         struct ref *ref;
3599         bool tag = FALSE;
3600         bool remote = FALSE;
3601
3602         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
3603                 /* Commits referenced by tags has "^{}" appended. */
3604                 if (name[namelen - 1] != '}')
3605                         return OK;
3606
3607                 while (namelen > 0 && name[namelen] != '^')
3608                         namelen--;
3609
3610                 tag = TRUE;
3611                 namelen -= STRING_SIZE("refs/tags/");
3612                 name    += STRING_SIZE("refs/tags/");
3613
3614         } else if (!strncmp(name, "refs/remotes/", STRING_SIZE("refs/remotes/"))) {
3615                 remote = TRUE;
3616                 namelen -= STRING_SIZE("refs/remotes/");
3617                 name    += STRING_SIZE("refs/remotes/");
3618
3619         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
3620                 namelen -= STRING_SIZE("refs/heads/");
3621                 name    += STRING_SIZE("refs/heads/");
3622
3623         } else if (!strcmp(name, "HEAD")) {
3624                 return OK;
3625         }
3626
3627         refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
3628         if (!refs)
3629                 return ERR;
3630
3631         ref = &refs[refs_size++];
3632         ref->name = malloc(namelen + 1);
3633         if (!ref->name)
3634                 return ERR;
3635
3636         strncpy(ref->name, name, namelen);
3637         ref->name[namelen] = 0;
3638         ref->tag = tag;
3639         ref->remote = remote;
3640         string_copy_rev(ref->id, id);
3641
3642         return OK;
3643 }
3644
3645 static int
3646 load_refs(void)
3647 {
3648         const char *cmd_env = getenv("TIG_LS_REMOTE");
3649         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
3650
3651         return read_properties(popen(cmd, "r"), "\t", read_ref);
3652 }
3653
3654 static int
3655 read_repo_config_option(char *name, int namelen, char *value, int valuelen)
3656 {
3657         if (!strcmp(name, "i18n.commitencoding"))
3658                 string_copy(opt_encoding, value);
3659
3660         return OK;
3661 }
3662
3663 static int
3664 load_repo_config(void)
3665 {
3666         return read_properties(popen("git repo-config --list", "r"),
3667                                "=", read_repo_config_option);
3668 }
3669
3670 static int
3671 read_properties(FILE *pipe, const char *separators,
3672                 int (*read_property)(char *, int, char *, int))
3673 {
3674         char buffer[BUFSIZ];
3675         char *name;
3676         int state = OK;
3677
3678         if (!pipe)
3679                 return ERR;
3680
3681         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
3682                 char *value;
3683                 size_t namelen;
3684                 size_t valuelen;
3685
3686                 name = chomp_string(name);
3687                 namelen = strcspn(name, separators);
3688
3689                 if (name[namelen]) {
3690                         name[namelen] = 0;
3691                         value = chomp_string(name + namelen + 1);
3692                         valuelen = strlen(value);
3693
3694                 } else {
3695                         value = "";
3696                         valuelen = 0;
3697                 }
3698
3699                 state = read_property(name, namelen, value, valuelen);
3700         }
3701
3702         if (state != ERR && ferror(pipe))
3703                 state = ERR;
3704
3705         pclose(pipe);
3706
3707         return state;
3708 }
3709
3710
3711 /*
3712  * Main
3713  */
3714
3715 static void __NORETURN
3716 quit(int sig)
3717 {
3718         /* XXX: Restore tty modes and let the OS cleanup the rest! */
3719         if (cursed)
3720                 endwin();
3721         exit(0);
3722 }
3723
3724 static void __NORETURN
3725 die(const char *err, ...)
3726 {
3727         va_list args;
3728
3729         endwin();
3730
3731         va_start(args, err);
3732         fputs("tig: ", stderr);
3733         vfprintf(stderr, err, args);
3734         fputs("\n", stderr);
3735         va_end(args);
3736
3737         exit(1);
3738 }
3739
3740 int
3741 main(int argc, char *argv[])
3742 {
3743         struct view *view;
3744         enum request request;
3745         size_t i;
3746
3747         signal(SIGINT, quit);
3748
3749         if (setlocale(LC_ALL, "")) {
3750                 string_copy(opt_codeset, nl_langinfo(CODESET));
3751         }
3752
3753         if (load_options() == ERR)
3754                 die("Failed to load user config.");
3755
3756         /* Load the repo config file so options can be overwritten from
3757          * the command line.  */
3758         if (load_repo_config() == ERR)
3759                 die("Failed to load repo config.");
3760
3761         if (!parse_options(argc, argv))
3762                 return 0;
3763
3764         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
3765                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
3766                 if (opt_iconv == ICONV_NONE)
3767                         die("Failed to initialize character set conversion");
3768         }
3769
3770         if (load_refs() == ERR)
3771                 die("Failed to load refs.");
3772
3773         /* Require a git repository unless when running in pager mode. */
3774         if (refs_size == 0 && opt_request != REQ_VIEW_PAGER)
3775                 die("Not a git repository");
3776
3777         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
3778                 view->cmd_env = getenv(view->cmd_env);
3779
3780         request = opt_request;
3781
3782         init_display();
3783
3784         while (view_driver(display[current_view], request)) {
3785                 int key;
3786                 int i;
3787
3788                 foreach_view (view, i)
3789                         update_view(view);
3790
3791                 /* Refresh, accept single keystroke of input */
3792                 key = wgetch(status_win);
3793
3794                 /* wgetch() with nodelay() enabled returns ERR when there's no
3795                  * input. */
3796                 if (key == ERR) {
3797                         request = REQ_NONE;
3798                         continue;
3799                 }
3800
3801                 request = get_keybinding(display[current_view]->keymap, key);
3802
3803                 /* Some low-level request handling. This keeps access to
3804                  * status_win restricted. */
3805                 switch (request) {
3806                 case REQ_PROMPT:
3807                 {
3808                         char *cmd = read_prompt(":");
3809
3810                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
3811                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
3812                                         opt_request = REQ_VIEW_DIFF;
3813                                 } else {
3814                                         opt_request = REQ_VIEW_PAGER;
3815                                 }
3816                                 break;
3817                         }
3818
3819                         request = REQ_NONE;
3820                         break;
3821                 }
3822                 case REQ_SEARCH:
3823                 case REQ_SEARCH_BACK:
3824                 {
3825                         const char *prompt = request == REQ_SEARCH
3826                                            ? "/" : "?";
3827                         char *search = read_prompt(prompt);
3828
3829                         if (search)
3830                                 string_copy(opt_search, search);
3831                         else
3832                                 request = REQ_NONE;
3833                         break;
3834                 }
3835                 case REQ_SCREEN_RESIZE:
3836                 {
3837                         int height, width;
3838
3839                         getmaxyx(stdscr, height, width);
3840
3841                         /* Resize the status view and let the view driver take
3842                          * care of resizing the displayed views. */
3843                         wresize(status_win, 1, width);
3844                         mvwin(status_win, height - 1, 0);
3845                         wrefresh(status_win);
3846                         break;
3847                 }
3848                 default:
3849                         break;
3850                 }
3851         }
3852
3853         quit(0);
3854
3855         return 0;
3856 }