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