Improve sanity check error messages
[tig] / tig.c
1 /* Copyright (c) 2006-2007 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 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17
18 #ifndef TIG_VERSION
19 #define TIG_VERSION "unknown-version"
20 #endif
21
22 #ifndef DEBUG
23 #define NDEBUG
24 #endif
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <signal.h>
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <time.h>
38
39 #include <regex.h>
40
41 #include <locale.h>
42 #include <langinfo.h>
43 #include <iconv.h>
44
45 #include <curses.h>
46
47 #if __GNUC__ >= 3
48 #define __NORETURN __attribute__((__noreturn__))
49 #else
50 #define __NORETURN
51 #endif
52
53 static void __NORETURN die(const char *err, ...);
54 static void report(const char *msg, ...);
55 static int read_properties(FILE *pipe, const char *separators, int (*read)(char *, size_t, char *, size_t));
56 static void set_nonblocking_input(bool loading);
57 static size_t utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed);
58
59 #define ABS(x)          ((x) >= 0  ? (x) : -(x))
60 #define MIN(x, y)       ((x) < (y) ? (x) :  (y))
61
62 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))
63 #define STRING_SIZE(x)  (sizeof(x) - 1)
64
65 #define SIZEOF_STR      1024    /* Default string size. */
66 #define SIZEOF_REF      256     /* Size of symbolic or SHA1 ID. */
67 #define SIZEOF_REV      41      /* Holds a SHA-1 and an ending NUL */
68
69 /* Revision graph */
70
71 #define REVGRAPH_INIT   'I'
72 #define REVGRAPH_MERGE  'M'
73 #define REVGRAPH_BRANCH '+'
74 #define REVGRAPH_COMMIT '*'
75 #define REVGRAPH_LINE   '|'
76
77 #define SIZEOF_REVGRAPH 19      /* Size of revision ancestry graphics. */
78
79 /* This color name can be used to refer to the default term colors. */
80 #define COLOR_DEFAULT   (-1)
81
82 #define ICONV_NONE      ((iconv_t) -1)
83 #ifndef ICONV_CONST
84 #define ICONV_CONST     /* nothing */
85 #endif
86
87 /* The format and size of the date column in the main view. */
88 #define DATE_FORMAT     "%Y-%m-%d %H:%M"
89 #define DATE_COLS       STRING_SIZE("2006-04-29 14:21 ")
90
91 #define AUTHOR_COLS     20
92
93 /* The default interval between line numbers. */
94 #define NUMBER_INTERVAL 1
95
96 #define TABSIZE         8
97
98 #define SCALE_SPLIT_VIEW(height)        ((height) * 2 / 3)
99
100 #ifndef GIT_CONFIG
101 #define GIT_CONFIG "git config"
102 #endif
103
104 #define TIG_LS_REMOTE \
105         "git ls-remote $(git rev-parse --git-dir) 2>/dev/null"
106
107 #define TIG_DIFF_CMD \
108         "git show --root --patch-with-stat --find-copies-harder -B -C %s 2>/dev/null"
109
110 #define TIG_LOG_CMD     \
111         "git log --cc --stat -n100 %s 2>/dev/null"
112
113 #define TIG_MAIN_CMD \
114         "git log --topo-order --pretty=raw %s 2>/dev/null"
115
116 #define TIG_TREE_CMD    \
117         "git ls-tree %s %s"
118
119 #define TIG_BLOB_CMD    \
120         "git cat-file blob %s"
121
122 /* XXX: Needs to be defined to the empty string. */
123 #define TIG_HELP_CMD    ""
124 #define TIG_PAGER_CMD   ""
125 #define TIG_STATUS_CMD  ""
126 #define TIG_STAGE_CMD   ""
127
128 /* Some ascii-shorthands fitted into the ncurses namespace. */
129 #define KEY_TAB         '\t'
130 #define KEY_RETURN      '\r'
131 #define KEY_ESC         27
132
133
134 struct ref {
135         char *name;             /* Ref name; tag or head names are shortened. */
136         char id[SIZEOF_REV];    /* Commit SHA1 ID */
137         unsigned int tag:1;     /* Is it a tag? */
138         unsigned int remote:1;  /* Is it a remote ref? */
139         unsigned int next:1;    /* For ref lists: are there more refs? */
140 };
141
142 static struct ref **get_refs(char *id);
143
144 struct int_map {
145         const char *name;
146         int namelen;
147         int value;
148 };
149
150 static int
151 set_from_int_map(struct int_map *map, size_t map_size,
152                  int *value, const char *name, int namelen)
153 {
154
155         int i;
156
157         for (i = 0; i < map_size; i++)
158                 if (namelen == map[i].namelen &&
159                     !strncasecmp(name, map[i].name, namelen)) {
160                         *value = map[i].value;
161                         return OK;
162                 }
163
164         return ERR;
165 }
166
167
168 /*
169  * String helpers
170  */
171
172 static inline void
173 string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen)
174 {
175         if (srclen > dstlen - 1)
176                 srclen = dstlen - 1;
177
178         strncpy(dst, src, srclen);
179         dst[srclen] = 0;
180 }
181
182 /* Shorthands for safely copying into a fixed buffer. */
183
184 #define string_copy(dst, src) \
185         string_ncopy_do(dst, sizeof(dst), src, sizeof(src))
186
187 #define string_ncopy(dst, src, srclen) \
188         string_ncopy_do(dst, sizeof(dst), src, srclen)
189
190 #define string_copy_rev(dst, src) \
191         string_ncopy_do(dst, SIZEOF_REV, src, SIZEOF_REV - 1)
192
193 #define string_add(dst, from, src) \
194         string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src))
195
196 static char *
197 chomp_string(char *name)
198 {
199         int namelen;
200
201         while (isspace(*name))
202                 name++;
203
204         namelen = strlen(name) - 1;
205         while (namelen > 0 && isspace(name[namelen]))
206                 name[namelen--] = 0;
207
208         return name;
209 }
210
211 static bool
212 string_nformat(char *buf, size_t bufsize, size_t *bufpos, const char *fmt, ...)
213 {
214         va_list args;
215         size_t pos = bufpos ? *bufpos : 0;
216
217         va_start(args, fmt);
218         pos += vsnprintf(buf + pos, bufsize - pos, fmt, args);
219         va_end(args);
220
221         if (bufpos)
222                 *bufpos = pos;
223
224         return pos >= bufsize ? FALSE : TRUE;
225 }
226
227 #define string_format(buf, fmt, args...) \
228         string_nformat(buf, sizeof(buf), NULL, fmt, args)
229
230 #define string_format_from(buf, from, fmt, args...) \
231         string_nformat(buf, sizeof(buf), from, fmt, args)
232
233 static int
234 string_enum_compare(const char *str1, const char *str2, int len)
235 {
236         size_t i;
237
238 #define string_enum_sep(x) ((x) == '-' || (x) == '_' || (x) == '.')
239
240         /* Diff-Header == DIFF_HEADER */
241         for (i = 0; i < len; i++) {
242                 if (toupper(str1[i]) == toupper(str2[i]))
243                         continue;
244
245                 if (string_enum_sep(str1[i]) &&
246                     string_enum_sep(str2[i]))
247                         continue;
248
249                 return str1[i] - str2[i];
250         }
251
252         return 0;
253 }
254
255 /* Shell quoting
256  *
257  * NOTE: The following is a slightly modified copy of the git project's shell
258  * quoting routines found in the quote.c file.
259  *
260  * Help to copy the thing properly quoted for the shell safety.  any single
261  * quote is replaced with '\'', any exclamation point is replaced with '\!',
262  * and the whole thing is enclosed in a
263  *
264  * E.g.
265  *  original     sq_quote     result
266  *  name     ==> name      ==> 'name'
267  *  a b      ==> a b       ==> 'a b'
268  *  a'b      ==> a'\''b    ==> 'a'\''b'
269  *  a!b      ==> a'\!'b    ==> 'a'\!'b'
270  */
271
272 static size_t
273 sq_quote(char buf[SIZEOF_STR], size_t bufsize, const char *src)
274 {
275         char c;
276
277 #define BUFPUT(x) do { if (bufsize < SIZEOF_STR) buf[bufsize++] = (x); } while (0)
278
279         BUFPUT('\'');
280         while ((c = *src++)) {
281                 if (c == '\'' || c == '!') {
282                         BUFPUT('\'');
283                         BUFPUT('\\');
284                         BUFPUT(c);
285                         BUFPUT('\'');
286                 } else {
287                         BUFPUT(c);
288                 }
289         }
290         BUFPUT('\'');
291
292         if (bufsize < SIZEOF_STR)
293                 buf[bufsize] = 0;
294
295         return bufsize;
296 }
297
298
299 /*
300  * User requests
301  */
302
303 #define REQ_INFO \
304         /* XXX: Keep the view request first and in sync with views[]. */ \
305         REQ_GROUP("View switching") \
306         REQ_(VIEW_MAIN,         "Show main view"), \
307         REQ_(VIEW_DIFF,         "Show diff view"), \
308         REQ_(VIEW_LOG,          "Show log view"), \
309         REQ_(VIEW_TREE,         "Show tree view"), \
310         REQ_(VIEW_BLOB,         "Show blob view"), \
311         REQ_(VIEW_HELP,         "Show help page"), \
312         REQ_(VIEW_PAGER,        "Show pager view"), \
313         REQ_(VIEW_STATUS,       "Show status view"), \
314         REQ_(VIEW_STAGE,        "Show stage view"), \
315         \
316         REQ_GROUP("View manipulation") \
317         REQ_(ENTER,             "Enter current line and scroll"), \
318         REQ_(NEXT,              "Move to next"), \
319         REQ_(PREVIOUS,          "Move to previous"), \
320         REQ_(VIEW_NEXT,         "Move focus to next view"), \
321         REQ_(REFRESH,           "Reload and refresh"), \
322         REQ_(VIEW_CLOSE,        "Close the current view"), \
323         REQ_(QUIT,              "Close all views and quit"), \
324         \
325         REQ_GROUP("Cursor navigation") \
326         REQ_(MOVE_UP,           "Move cursor one line up"), \
327         REQ_(MOVE_DOWN,         "Move cursor one line down"), \
328         REQ_(MOVE_PAGE_DOWN,    "Move cursor one page down"), \
329         REQ_(MOVE_PAGE_UP,      "Move cursor one page up"), \
330         REQ_(MOVE_FIRST_LINE,   "Move cursor to first line"), \
331         REQ_(MOVE_LAST_LINE,    "Move cursor to last line"), \
332         \
333         REQ_GROUP("Scrolling") \
334         REQ_(SCROLL_LINE_UP,    "Scroll one line up"), \
335         REQ_(SCROLL_LINE_DOWN,  "Scroll one line down"), \
336         REQ_(SCROLL_PAGE_UP,    "Scroll one page up"), \
337         REQ_(SCROLL_PAGE_DOWN,  "Scroll one page down"), \
338         \
339         REQ_GROUP("Searching") \
340         REQ_(SEARCH,            "Search the view"), \
341         REQ_(SEARCH_BACK,       "Search backwards in the view"), \
342         REQ_(FIND_NEXT,         "Find next search match"), \
343         REQ_(FIND_PREV,         "Find previous search match"), \
344         \
345         REQ_GROUP("Misc") \
346         REQ_(PROMPT,            "Bring up the prompt"), \
347         REQ_(SCREEN_REDRAW,     "Redraw the screen"), \
348         REQ_(SCREEN_RESIZE,     "Resize the screen"), \
349         REQ_(SHOW_VERSION,      "Show version information"), \
350         REQ_(STOP_LOADING,      "Stop all loading views"), \
351         REQ_(TOGGLE_LINENO,     "Toggle line numbers"), \
352         REQ_(TOGGLE_REV_GRAPH,  "Toggle revision graph visualization"), \
353         REQ_(STATUS_UPDATE,     "Update file status"), \
354         REQ_(STATUS_MERGE,      "Merge file using external tool"), \
355         REQ_(EDIT,              "Open in editor"), \
356         REQ_(NONE,              "Do nothing")
357
358
359 /* User action requests. */
360 enum request {
361 #define REQ_GROUP(help)
362 #define REQ_(req, help) REQ_##req
363
364         /* Offset all requests to avoid conflicts with ncurses getch values. */
365         REQ_OFFSET = KEY_MAX + 1,
366         REQ_INFO
367
368 #undef  REQ_GROUP
369 #undef  REQ_
370 };
371
372 struct request_info {
373         enum request request;
374         char *name;
375         int namelen;
376         char *help;
377 };
378
379 static struct request_info req_info[] = {
380 #define REQ_GROUP(help) { 0, NULL, 0, (help) },
381 #define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) }
382         REQ_INFO
383 #undef  REQ_GROUP
384 #undef  REQ_
385 };
386
387 static enum request
388 get_request(const char *name)
389 {
390         int namelen = strlen(name);
391         int i;
392
393         for (i = 0; i < ARRAY_SIZE(req_info); i++)
394                 if (req_info[i].namelen == namelen &&
395                     !string_enum_compare(req_info[i].name, name, namelen))
396                         return req_info[i].request;
397
398         return REQ_NONE;
399 }
400
401
402 /*
403  * Options
404  */
405
406 static const char usage[] =
407 "tig " TIG_VERSION " (" __DATE__ ")\n"
408 "\n"
409 "Usage: tig [options]\n"
410 "   or: tig [options] [--] [git log options]\n"
411 "   or: tig [options] log  [git log options]\n"
412 "   or: tig [options] diff [git diff options]\n"
413 "   or: tig [options] show [git show options]\n"
414 "   or: tig [options] <    [git command output]\n"
415 "\n"
416 "Options:\n"
417 "  -l                          Start up in log view\n"
418 "  -d                          Start up in diff view\n"
419 "  -S                          Start up in status view\n"
420 "  -n[I], --line-number[=I]    Show line numbers with given interval\n"
421 "  -b[N], --tab-size[=N]       Set number of spaces for tab expansion\n"
422 "  --                          Mark end of tig options\n"
423 "  -v, --version               Show version and exit\n"
424 "  -h, --help                  Show help message and exit\n";
425
426 /* Option and state variables. */
427 static bool opt_line_number             = FALSE;
428 static bool opt_rev_graph               = FALSE;
429 static int opt_num_interval             = NUMBER_INTERVAL;
430 static int opt_tab_size                 = TABSIZE;
431 static enum request opt_request         = REQ_VIEW_MAIN;
432 static char opt_cmd[SIZEOF_STR]         = "";
433 static char opt_path[SIZEOF_STR]        = "";
434 static FILE *opt_pipe                   = NULL;
435 static char opt_encoding[20]            = "UTF-8";
436 static bool opt_utf8                    = TRUE;
437 static char opt_codeset[20]             = "UTF-8";
438 static iconv_t opt_iconv                = ICONV_NONE;
439 static char opt_search[SIZEOF_STR]      = "";
440 static char opt_cdup[SIZEOF_STR]        = "";
441 static char opt_git_dir[SIZEOF_STR]     = "";
442 static char opt_is_inside_work_tree     = -1; /* set to TRUE or FALSE */
443 static char opt_editor[SIZEOF_STR]      = "";
444
445 enum option_type {
446         OPT_NONE,
447         OPT_INT,
448 };
449
450 static bool
451 check_option(char *opt, char short_name, char *name, enum option_type type, ...)
452 {
453         va_list args;
454         char *value = "";
455         int *number;
456
457         if (opt[0] != '-')
458                 return FALSE;
459
460         if (opt[1] == '-') {
461                 int namelen = strlen(name);
462
463                 opt += 2;
464
465                 if (strncmp(opt, name, namelen))
466                         return FALSE;
467
468                 if (opt[namelen] == '=')
469                         value = opt + namelen + 1;
470
471         } else {
472                 if (!short_name || opt[1] != short_name)
473                         return FALSE;
474                 value = opt + 2;
475         }
476
477         va_start(args, type);
478         if (type == OPT_INT) {
479                 number = va_arg(args, int *);
480                 if (isdigit(*value))
481                         *number = atoi(value);
482         }
483         va_end(args);
484
485         return TRUE;
486 }
487
488 /* Returns the index of log or diff command or -1 to exit. */
489 static bool
490 parse_options(int argc, char *argv[])
491 {
492         int i;
493
494         for (i = 1; i < argc; i++) {
495                 char *opt = argv[i];
496
497                 if (!strcmp(opt, "log") ||
498                     !strcmp(opt, "diff") ||
499                     !strcmp(opt, "show")) {
500                         opt_request = opt[0] == 'l'
501                                     ? REQ_VIEW_LOG : REQ_VIEW_DIFF;
502                         break;
503                 }
504
505                 if (opt[0] && opt[0] != '-')
506                         break;
507
508                 if (!strcmp(opt, "-l")) {
509                         opt_request = REQ_VIEW_LOG;
510                         continue;
511                 }
512
513                 if (!strcmp(opt, "-d")) {
514                         opt_request = REQ_VIEW_DIFF;
515                         continue;
516                 }
517
518                 if (!strcmp(opt, "-S")) {
519                         opt_request = REQ_VIEW_STATUS;
520                         continue;
521                 }
522
523                 if (check_option(opt, 'n', "line-number", OPT_INT, &opt_num_interval)) {
524                         opt_line_number = TRUE;
525                         continue;
526                 }
527
528                 if (check_option(opt, 'b', "tab-size", OPT_INT, &opt_tab_size)) {
529                         opt_tab_size = MIN(opt_tab_size, TABSIZE);
530                         continue;
531                 }
532
533                 if (check_option(opt, 'v', "version", OPT_NONE)) {
534                         printf("tig version %s\n", TIG_VERSION);
535                         return FALSE;
536                 }
537
538                 if (check_option(opt, 'h', "help", OPT_NONE)) {
539                         printf(usage);
540                         return FALSE;
541                 }
542
543                 if (!strcmp(opt, "--")) {
544                         i++;
545                         break;
546                 }
547
548                 die("unknown option '%s'\n\n%s", opt, usage);
549         }
550
551         if (!isatty(STDIN_FILENO)) {
552                 opt_request = REQ_VIEW_PAGER;
553                 opt_pipe = stdin;
554
555         } else if (i < argc) {
556                 size_t buf_size;
557
558                 if (opt_request == REQ_VIEW_MAIN)
559                         /* XXX: This is vulnerable to the user overriding
560                          * options required for the main view parser. */
561                         string_copy(opt_cmd, "git log --pretty=raw");
562                 else
563                         string_copy(opt_cmd, "git");
564                 buf_size = strlen(opt_cmd);
565
566                 while (buf_size < sizeof(opt_cmd) && i < argc) {
567                         opt_cmd[buf_size++] = ' ';
568                         buf_size = sq_quote(opt_cmd, buf_size, argv[i++]);
569                 }
570
571                 if (buf_size >= sizeof(opt_cmd))
572                         die("command too long");
573
574                 opt_cmd[buf_size] = 0;
575         }
576
577         if (*opt_encoding && strcasecmp(opt_encoding, "UTF-8"))
578                 opt_utf8 = FALSE;
579
580         return TRUE;
581 }
582
583
584 /*
585  * Line-oriented content detection.
586  */
587
588 #define LINE_INFO \
589 LINE(DIFF_HEADER,  "diff --git ",       COLOR_YELLOW,   COLOR_DEFAULT,  0), \
590 LINE(DIFF_CHUNK,   "@@",                COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
591 LINE(DIFF_ADD,     "+",                 COLOR_GREEN,    COLOR_DEFAULT,  0), \
592 LINE(DIFF_DEL,     "-",                 COLOR_RED,      COLOR_DEFAULT,  0), \
593 LINE(DIFF_INDEX,        "index ",         COLOR_BLUE,   COLOR_DEFAULT,  0), \
594 LINE(DIFF_OLDMODE,      "old file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
595 LINE(DIFF_NEWMODE,      "new file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
596 LINE(DIFF_COPY_FROM,    "copy from",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
597 LINE(DIFF_COPY_TO,      "copy to",        COLOR_YELLOW, COLOR_DEFAULT,  0), \
598 LINE(DIFF_RENAME_FROM,  "rename from",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
599 LINE(DIFF_RENAME_TO,    "rename to",      COLOR_YELLOW, COLOR_DEFAULT,  0), \
600 LINE(DIFF_SIMILARITY,   "similarity ",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
601 LINE(DIFF_DISSIMILARITY,"dissimilarity ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
602 LINE(DIFF_TREE,         "diff-tree ",     COLOR_BLUE,   COLOR_DEFAULT,  0), \
603 LINE(PP_AUTHOR,    "Author: ",          COLOR_CYAN,     COLOR_DEFAULT,  0), \
604 LINE(PP_COMMIT,    "Commit: ",          COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
605 LINE(PP_MERGE,     "Merge: ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
606 LINE(PP_DATE,      "Date:   ",          COLOR_YELLOW,   COLOR_DEFAULT,  0), \
607 LINE(PP_ADATE,     "AuthorDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
608 LINE(PP_CDATE,     "CommitDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
609 LINE(PP_REFS,      "Refs: ",            COLOR_RED,      COLOR_DEFAULT,  0), \
610 LINE(COMMIT,       "commit ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
611 LINE(PARENT,       "parent ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
612 LINE(TREE,         "tree ",             COLOR_BLUE,     COLOR_DEFAULT,  0), \
613 LINE(AUTHOR,       "author ",           COLOR_CYAN,     COLOR_DEFAULT,  0), \
614 LINE(COMMITTER,    "committer ",        COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
615 LINE(SIGNOFF,      "    Signed-off-by", COLOR_YELLOW,   COLOR_DEFAULT,  0), \
616 LINE(ACKED,        "    Acked-by",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
617 LINE(DEFAULT,      "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
618 LINE(CURSOR,       "",                  COLOR_WHITE,    COLOR_GREEN,    A_BOLD), \
619 LINE(STATUS,       "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
620 LINE(TITLE_BLUR,   "",                  COLOR_WHITE,    COLOR_BLUE,     0), \
621 LINE(TITLE_FOCUS,  "",                  COLOR_WHITE,    COLOR_BLUE,     A_BOLD), \
622 LINE(MAIN_DATE,    "",                  COLOR_BLUE,     COLOR_DEFAULT,  0), \
623 LINE(MAIN_AUTHOR,  "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
624 LINE(MAIN_COMMIT,  "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
625 LINE(MAIN_DELIM,   "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
626 LINE(MAIN_TAG,     "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
627 LINE(MAIN_REMOTE,  "",                  COLOR_YELLOW,   COLOR_DEFAULT,  A_BOLD), \
628 LINE(MAIN_REF,     "",                  COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD), \
629 LINE(TREE_DIR,     "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
630 LINE(TREE_FILE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
631 LINE(STAT_SECTION, "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
632 LINE(STAT_NONE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
633 LINE(STAT_STAGED,  "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
634 LINE(STAT_UNSTAGED,"",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
635 LINE(STAT_UNTRACKED,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0)
636
637 enum line_type {
638 #define LINE(type, line, fg, bg, attr) \
639         LINE_##type
640         LINE_INFO
641 #undef  LINE
642 };
643
644 struct line_info {
645         const char *name;       /* Option name. */
646         int namelen;            /* Size of option name. */
647         const char *line;       /* The start of line to match. */
648         int linelen;            /* Size of string to match. */
649         int fg, bg, attr;       /* Color and text attributes for the lines. */
650 };
651
652 static struct line_info line_info[] = {
653 #define LINE(type, line, fg, bg, attr) \
654         { #type, STRING_SIZE(#type), (line), STRING_SIZE(line), (fg), (bg), (attr) }
655         LINE_INFO
656 #undef  LINE
657 };
658
659 static enum line_type
660 get_line_type(char *line)
661 {
662         int linelen = strlen(line);
663         enum line_type type;
664
665         for (type = 0; type < ARRAY_SIZE(line_info); type++)
666                 /* Case insensitive search matches Signed-off-by lines better. */
667                 if (linelen >= line_info[type].linelen &&
668                     !strncasecmp(line_info[type].line, line, line_info[type].linelen))
669                         return type;
670
671         return LINE_DEFAULT;
672 }
673
674 static inline int
675 get_line_attr(enum line_type type)
676 {
677         assert(type < ARRAY_SIZE(line_info));
678         return COLOR_PAIR(type) | line_info[type].attr;
679 }
680
681 static struct line_info *
682 get_line_info(char *name, int namelen)
683 {
684         enum line_type type;
685
686         for (type = 0; type < ARRAY_SIZE(line_info); type++)
687                 if (namelen == line_info[type].namelen &&
688                     !string_enum_compare(line_info[type].name, name, namelen))
689                         return &line_info[type];
690
691         return NULL;
692 }
693
694 static void
695 init_colors(void)
696 {
697         int default_bg = COLOR_BLACK;
698         int default_fg = COLOR_WHITE;
699         enum line_type type;
700
701         start_color();
702
703         if (use_default_colors() != ERR) {
704                 default_bg = -1;
705                 default_fg = -1;
706         }
707
708         for (type = 0; type < ARRAY_SIZE(line_info); type++) {
709                 struct line_info *info = &line_info[type];
710                 int bg = info->bg == COLOR_DEFAULT ? default_bg : info->bg;
711                 int fg = info->fg == COLOR_DEFAULT ? default_fg : info->fg;
712
713                 init_pair(type, fg, bg);
714         }
715 }
716
717 struct line {
718         enum line_type type;
719
720         /* State flags */
721         unsigned int selected:1;
722
723         void *data;             /* User data */
724 };
725
726
727 /*
728  * Keys
729  */
730
731 struct keybinding {
732         int alias;
733         enum request request;
734         struct keybinding *next;
735 };
736
737 static struct keybinding default_keybindings[] = {
738         /* View switching */
739         { 'm',          REQ_VIEW_MAIN },
740         { 'd',          REQ_VIEW_DIFF },
741         { 'l',          REQ_VIEW_LOG },
742         { 't',          REQ_VIEW_TREE },
743         { 'f',          REQ_VIEW_BLOB },
744         { 'p',          REQ_VIEW_PAGER },
745         { 'h',          REQ_VIEW_HELP },
746         { 'S',          REQ_VIEW_STATUS },
747         { 'c',          REQ_VIEW_STAGE },
748
749         /* View manipulation */
750         { 'q',          REQ_VIEW_CLOSE },
751         { KEY_TAB,      REQ_VIEW_NEXT },
752         { KEY_RETURN,   REQ_ENTER },
753         { KEY_UP,       REQ_PREVIOUS },
754         { KEY_DOWN,     REQ_NEXT },
755         { 'R',          REQ_REFRESH },
756
757         /* Cursor navigation */
758         { 'k',          REQ_MOVE_UP },
759         { 'j',          REQ_MOVE_DOWN },
760         { KEY_HOME,     REQ_MOVE_FIRST_LINE },
761         { KEY_END,      REQ_MOVE_LAST_LINE },
762         { KEY_NPAGE,    REQ_MOVE_PAGE_DOWN },
763         { ' ',          REQ_MOVE_PAGE_DOWN },
764         { KEY_PPAGE,    REQ_MOVE_PAGE_UP },
765         { 'b',          REQ_MOVE_PAGE_UP },
766         { '-',          REQ_MOVE_PAGE_UP },
767
768         /* Scrolling */
769         { KEY_IC,       REQ_SCROLL_LINE_UP },
770         { KEY_DC,       REQ_SCROLL_LINE_DOWN },
771         { 'w',          REQ_SCROLL_PAGE_UP },
772         { 's',          REQ_SCROLL_PAGE_DOWN },
773
774         /* Searching */
775         { '/',          REQ_SEARCH },
776         { '?',          REQ_SEARCH_BACK },
777         { 'n',          REQ_FIND_NEXT },
778         { 'N',          REQ_FIND_PREV },
779
780         /* Misc */
781         { 'Q',          REQ_QUIT },
782         { 'z',          REQ_STOP_LOADING },
783         { 'v',          REQ_SHOW_VERSION },
784         { 'r',          REQ_SCREEN_REDRAW },
785         { '.',          REQ_TOGGLE_LINENO },
786         { 'g',          REQ_TOGGLE_REV_GRAPH },
787         { ':',          REQ_PROMPT },
788         { 'u',          REQ_STATUS_UPDATE },
789         { 'M',          REQ_STATUS_MERGE },
790         { 'e',          REQ_EDIT },
791
792         /* Using the ncurses SIGWINCH handler. */
793         { KEY_RESIZE,   REQ_SCREEN_RESIZE },
794 };
795
796 #define KEYMAP_INFO \
797         KEYMAP_(GENERIC), \
798         KEYMAP_(MAIN), \
799         KEYMAP_(DIFF), \
800         KEYMAP_(LOG), \
801         KEYMAP_(TREE), \
802         KEYMAP_(BLOB), \
803         KEYMAP_(PAGER), \
804         KEYMAP_(HELP), \
805         KEYMAP_(STATUS), \
806         KEYMAP_(STAGE)
807
808 enum keymap {
809 #define KEYMAP_(name) KEYMAP_##name
810         KEYMAP_INFO
811 #undef  KEYMAP_
812 };
813
814 static struct int_map keymap_table[] = {
815 #define KEYMAP_(name) { #name, STRING_SIZE(#name), KEYMAP_##name }
816         KEYMAP_INFO
817 #undef  KEYMAP_
818 };
819
820 #define set_keymap(map, name) \
821         set_from_int_map(keymap_table, ARRAY_SIZE(keymap_table), map, name, strlen(name))
822
823 static struct keybinding *keybindings[ARRAY_SIZE(keymap_table)];
824
825 static void
826 add_keybinding(enum keymap keymap, enum request request, int key)
827 {
828         struct keybinding *keybinding;
829
830         keybinding = calloc(1, sizeof(*keybinding));
831         if (!keybinding)
832                 die("Failed to allocate keybinding");
833
834         keybinding->alias = key;
835         keybinding->request = request;
836         keybinding->next = keybindings[keymap];
837         keybindings[keymap] = keybinding;
838 }
839
840 /* Looks for a key binding first in the given map, then in the generic map, and
841  * lastly in the default keybindings. */
842 static enum request
843 get_keybinding(enum keymap keymap, int key)
844 {
845         struct keybinding *kbd;
846         int i;
847
848         for (kbd = keybindings[keymap]; kbd; kbd = kbd->next)
849                 if (kbd->alias == key)
850                         return kbd->request;
851
852         for (kbd = keybindings[KEYMAP_GENERIC]; kbd; kbd = kbd->next)
853                 if (kbd->alias == key)
854                         return kbd->request;
855
856         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++)
857                 if (default_keybindings[i].alias == key)
858                         return default_keybindings[i].request;
859
860         return (enum request) key;
861 }
862
863
864 struct key {
865         char *name;
866         int value;
867 };
868
869 static struct key key_table[] = {
870         { "Enter",      KEY_RETURN },
871         { "Space",      ' ' },
872         { "Backspace",  KEY_BACKSPACE },
873         { "Tab",        KEY_TAB },
874         { "Escape",     KEY_ESC },
875         { "Left",       KEY_LEFT },
876         { "Right",      KEY_RIGHT },
877         { "Up",         KEY_UP },
878         { "Down",       KEY_DOWN },
879         { "Insert",     KEY_IC },
880         { "Delete",     KEY_DC },
881         { "Hash",       '#' },
882         { "Home",       KEY_HOME },
883         { "End",        KEY_END },
884         { "PageUp",     KEY_PPAGE },
885         { "PageDown",   KEY_NPAGE },
886         { "F1",         KEY_F(1) },
887         { "F2",         KEY_F(2) },
888         { "F3",         KEY_F(3) },
889         { "F4",         KEY_F(4) },
890         { "F5",         KEY_F(5) },
891         { "F6",         KEY_F(6) },
892         { "F7",         KEY_F(7) },
893         { "F8",         KEY_F(8) },
894         { "F9",         KEY_F(9) },
895         { "F10",        KEY_F(10) },
896         { "F11",        KEY_F(11) },
897         { "F12",        KEY_F(12) },
898 };
899
900 static int
901 get_key_value(const char *name)
902 {
903         int i;
904
905         for (i = 0; i < ARRAY_SIZE(key_table); i++)
906                 if (!strcasecmp(key_table[i].name, name))
907                         return key_table[i].value;
908
909         if (strlen(name) == 1 && isprint(*name))
910                 return (int) *name;
911
912         return ERR;
913 }
914
915 static char *
916 get_key_name(int key_value)
917 {
918         static char key_char[] = "'X'";
919         char *seq = NULL;
920         int key;
921
922         for (key = 0; key < ARRAY_SIZE(key_table); key++)
923                 if (key_table[key].value == key_value)
924                         seq = key_table[key].name;
925
926         if (seq == NULL &&
927             key_value < 127 &&
928             isprint(key_value)) {
929                 key_char[1] = (char) key_value;
930                 seq = key_char;
931         }
932
933         return seq ? seq : "'?'";
934 }
935
936 static char *
937 get_key(enum request request)
938 {
939         static char buf[BUFSIZ];
940         static char key_char[] = "'X'";
941         size_t pos = 0;
942         char *sep = "";
943         int i;
944
945         buf[pos] = 0;
946
947         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
948                 struct keybinding *keybinding = &default_keybindings[i];
949                 char *seq = NULL;
950                 int key;
951
952                 if (keybinding->request != request)
953                         continue;
954
955                 for (key = 0; key < ARRAY_SIZE(key_table); key++)
956                         if (key_table[key].value == keybinding->alias)
957                                 seq = key_table[key].name;
958
959                 if (seq == NULL &&
960                     keybinding->alias < 127 &&
961                     isprint(keybinding->alias)) {
962                         key_char[1] = (char) keybinding->alias;
963                         seq = key_char;
964                 }
965
966                 if (!seq)
967                         seq = "'?'";
968
969                 if (!string_format_from(buf, &pos, "%s%s", sep, seq))
970                         return "Too many keybindings!";
971                 sep = ", ";
972         }
973
974         return buf;
975 }
976
977 struct run_request {
978         enum keymap keymap;
979         int key;
980         char cmd[SIZEOF_STR];
981 };
982
983 static struct run_request *run_request;
984 static size_t run_requests;
985
986 static enum request
987 add_run_request(enum keymap keymap, int key, int argc, char **argv)
988 {
989         struct run_request *tmp;
990         struct run_request req = { keymap, key };
991         size_t bufpos;
992
993         for (bufpos = 0; argc > 0; argc--, argv++)
994                 if (!string_format_from(req.cmd, &bufpos, "%s ", *argv))
995                         return REQ_NONE;
996
997         req.cmd[bufpos - 1] = 0;
998
999         tmp = realloc(run_request, (run_requests + 1) * sizeof(*run_request));
1000         if (!tmp)
1001                 return REQ_NONE;
1002
1003         run_request = tmp;
1004         run_request[run_requests++] = req;
1005
1006         return REQ_NONE + run_requests;
1007 }
1008
1009 static struct run_request *
1010 get_run_request(enum request request)
1011 {
1012         if (request <= REQ_NONE)
1013                 return NULL;
1014         return &run_request[request - REQ_NONE - 1];
1015 }
1016
1017 static void
1018 add_builtin_run_requests(void)
1019 {
1020         struct {
1021                 enum keymap keymap;
1022                 int key;
1023                 char *argv[1];
1024         } reqs[] = {
1025                 { KEYMAP_MAIN,    'C', { "git cherry-pick %(commit)" } },
1026                 { KEYMAP_GENERIC, 'G', { "git gc" } },
1027         };
1028         int i;
1029
1030         for (i = 0; i < ARRAY_SIZE(reqs); i++) {
1031                 enum request req;
1032
1033                 req = add_run_request(reqs[i].keymap, reqs[i].key, 1, reqs[i].argv);
1034                 if (req != REQ_NONE)
1035                         add_keybinding(reqs[i].keymap, req, reqs[i].key);
1036         }
1037 }
1038
1039 /*
1040  * User config file handling.
1041  */
1042
1043 static struct int_map color_map[] = {
1044 #define COLOR_MAP(name) { #name, STRING_SIZE(#name), COLOR_##name }
1045         COLOR_MAP(DEFAULT),
1046         COLOR_MAP(BLACK),
1047         COLOR_MAP(BLUE),
1048         COLOR_MAP(CYAN),
1049         COLOR_MAP(GREEN),
1050         COLOR_MAP(MAGENTA),
1051         COLOR_MAP(RED),
1052         COLOR_MAP(WHITE),
1053         COLOR_MAP(YELLOW),
1054 };
1055
1056 #define set_color(color, name) \
1057         set_from_int_map(color_map, ARRAY_SIZE(color_map), color, name, strlen(name))
1058
1059 static struct int_map attr_map[] = {
1060 #define ATTR_MAP(name) { #name, STRING_SIZE(#name), A_##name }
1061         ATTR_MAP(NORMAL),
1062         ATTR_MAP(BLINK),
1063         ATTR_MAP(BOLD),
1064         ATTR_MAP(DIM),
1065         ATTR_MAP(REVERSE),
1066         ATTR_MAP(STANDOUT),
1067         ATTR_MAP(UNDERLINE),
1068 };
1069
1070 #define set_attribute(attr, name) \
1071         set_from_int_map(attr_map, ARRAY_SIZE(attr_map), attr, name, strlen(name))
1072
1073 static int   config_lineno;
1074 static bool  config_errors;
1075 static char *config_msg;
1076
1077 /* Wants: object fgcolor bgcolor [attr] */
1078 static int
1079 option_color_command(int argc, char *argv[])
1080 {
1081         struct line_info *info;
1082
1083         if (argc != 3 && argc != 4) {
1084                 config_msg = "Wrong number of arguments given to color command";
1085                 return ERR;
1086         }
1087
1088         info = get_line_info(argv[0], strlen(argv[0]));
1089         if (!info) {
1090                 config_msg = "Unknown color name";
1091                 return ERR;
1092         }
1093
1094         if (set_color(&info->fg, argv[1]) == ERR ||
1095             set_color(&info->bg, argv[2]) == ERR) {
1096                 config_msg = "Unknown color";
1097                 return ERR;
1098         }
1099
1100         if (argc == 4 && set_attribute(&info->attr, argv[3]) == ERR) {
1101                 config_msg = "Unknown attribute";
1102                 return ERR;
1103         }
1104
1105         return OK;
1106 }
1107
1108 /* Wants: name = value */
1109 static int
1110 option_set_command(int argc, char *argv[])
1111 {
1112         if (argc != 3) {
1113                 config_msg = "Wrong number of arguments given to set command";
1114                 return ERR;
1115         }
1116
1117         if (strcmp(argv[1], "=")) {
1118                 config_msg = "No value assigned";
1119                 return ERR;
1120         }
1121
1122         if (!strcmp(argv[0], "show-rev-graph")) {
1123                 opt_rev_graph = (!strcmp(argv[2], "1") ||
1124                                  !strcmp(argv[2], "true") ||
1125                                  !strcmp(argv[2], "yes"));
1126                 return OK;
1127         }
1128
1129         if (!strcmp(argv[0], "line-number-interval")) {
1130                 opt_num_interval = atoi(argv[2]);
1131                 return OK;
1132         }
1133
1134         if (!strcmp(argv[0], "tab-size")) {
1135                 opt_tab_size = atoi(argv[2]);
1136                 return OK;
1137         }
1138
1139         if (!strcmp(argv[0], "commit-encoding")) {
1140                 char *arg = argv[2];
1141                 int delimiter = *arg;
1142                 int i;
1143
1144                 switch (delimiter) {
1145                 case '"':
1146                 case '\'':
1147                         for (arg++, i = 0; arg[i]; i++)
1148                                 if (arg[i] == delimiter) {
1149                                         arg[i] = 0;
1150                                         break;
1151                                 }
1152                 default:
1153                         string_ncopy(opt_encoding, arg, strlen(arg));
1154                         return OK;
1155                 }
1156         }
1157
1158         config_msg = "Unknown variable name";
1159         return ERR;
1160 }
1161
1162 /* Wants: mode request key */
1163 static int
1164 option_bind_command(int argc, char *argv[])
1165 {
1166         enum request request;
1167         int keymap;
1168         int key;
1169
1170         if (argc < 3) {
1171                 config_msg = "Wrong number of arguments given to bind command";
1172                 return ERR;
1173         }
1174
1175         if (set_keymap(&keymap, argv[0]) == ERR) {
1176                 config_msg = "Unknown key map";
1177                 return ERR;
1178         }
1179
1180         key = get_key_value(argv[1]);
1181         if (key == ERR) {
1182                 config_msg = "Unknown key";
1183                 return ERR;
1184         }
1185
1186         request = get_request(argv[2]);
1187         if (request = REQ_NONE) {
1188                 const char *obsolete[] = { "cherry-pick" };
1189                 size_t namelen = strlen(argv[2]);
1190                 int i;
1191
1192                 for (i = 0; i < ARRAY_SIZE(obsolete); i++) {
1193                         if (namelen == strlen(obsolete[i]) &&
1194                             !string_enum_compare(obsolete[i], argv[2], namelen)) {
1195                                 config_msg = "Obsolete request name";
1196                                 return ERR;
1197                         }
1198                 }
1199         }
1200         if (request == REQ_NONE && *argv[2]++ == '!')
1201                 request = add_run_request(keymap, key, argc - 2, argv + 2);
1202         if (request == REQ_NONE) {
1203                 config_msg = "Unknown request name";
1204                 return ERR;
1205         }
1206
1207         add_keybinding(keymap, request, key);
1208
1209         return OK;
1210 }
1211
1212 static int
1213 set_option(char *opt, char *value)
1214 {
1215         char *argv[16];
1216         int valuelen;
1217         int argc = 0;
1218
1219         /* Tokenize */
1220         while (argc < ARRAY_SIZE(argv) && (valuelen = strcspn(value, " \t"))) {
1221                 argv[argc++] = value;
1222
1223                 value += valuelen;
1224                 if (!*value)
1225                         break;
1226
1227                 *value++ = 0;
1228                 while (isspace(*value))
1229                         value++;
1230         }
1231
1232         if (!strcmp(opt, "color"))
1233                 return option_color_command(argc, argv);
1234
1235         if (!strcmp(opt, "set"))
1236                 return option_set_command(argc, argv);
1237
1238         if (!strcmp(opt, "bind"))
1239                 return option_bind_command(argc, argv);
1240
1241         config_msg = "Unknown option command";
1242         return ERR;
1243 }
1244
1245 static int
1246 read_option(char *opt, size_t optlen, char *value, size_t valuelen)
1247 {
1248         int status = OK;
1249
1250         config_lineno++;
1251         config_msg = "Internal error";
1252
1253         /* Check for comment markers, since read_properties() will
1254          * only ensure opt and value are split at first " \t". */
1255         optlen = strcspn(opt, "#");
1256         if (optlen == 0)
1257                 return OK;
1258
1259         if (opt[optlen] != 0) {
1260                 config_msg = "No option value";
1261                 status = ERR;
1262
1263         }  else {
1264                 /* Look for comment endings in the value. */
1265                 size_t len = strcspn(value, "#");
1266
1267                 if (len < valuelen) {
1268                         valuelen = len;
1269                         value[valuelen] = 0;
1270                 }
1271
1272                 status = set_option(opt, value);
1273         }
1274
1275         if (status == ERR) {
1276                 fprintf(stderr, "Error on line %d, near '%.*s': %s\n",
1277                         config_lineno, (int) optlen, opt, config_msg);
1278                 config_errors = TRUE;
1279         }
1280
1281         /* Always keep going if errors are encountered. */
1282         return OK;
1283 }
1284
1285 static int
1286 load_options(void)
1287 {
1288         char *home = getenv("HOME");
1289         char buf[SIZEOF_STR];
1290         FILE *file;
1291
1292         config_lineno = 0;
1293         config_errors = FALSE;
1294
1295         add_builtin_run_requests();
1296
1297         if (!home || !string_format(buf, "%s/.tigrc", home))
1298                 return ERR;
1299
1300         /* It's ok that the file doesn't exist. */
1301         file = fopen(buf, "r");
1302         if (!file)
1303                 return OK;
1304
1305         if (read_properties(file, " \t", read_option) == ERR ||
1306             config_errors == TRUE)
1307                 fprintf(stderr, "Errors while loading %s.\n", buf);
1308
1309         return OK;
1310 }
1311
1312
1313 /*
1314  * The viewer
1315  */
1316
1317 struct view;
1318 struct view_ops;
1319
1320 /* The display array of active views and the index of the current view. */
1321 static struct view *display[2];
1322 static unsigned int current_view;
1323
1324 /* Reading from the prompt? */
1325 static bool input_mode = FALSE;
1326
1327 #define foreach_displayed_view(view, i) \
1328         for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
1329
1330 #define displayed_views()       (display[1] != NULL ? 2 : 1)
1331
1332 /* Current head and commit ID */
1333 static char ref_blob[SIZEOF_REF]        = "";
1334 static char ref_commit[SIZEOF_REF]      = "HEAD";
1335 static char ref_head[SIZEOF_REF]        = "HEAD";
1336
1337 struct view {
1338         const char *name;       /* View name */
1339         const char *cmd_fmt;    /* Default command line format */
1340         const char *cmd_env;    /* Command line set via environment */
1341         const char *id;         /* Points to either of ref_{head,commit,blob} */
1342
1343         struct view_ops *ops;   /* View operations */
1344
1345         enum keymap keymap;     /* What keymap does this view have */
1346
1347         char cmd[SIZEOF_STR];   /* Command buffer */
1348         char ref[SIZEOF_REF];   /* Hovered commit reference */
1349         char vid[SIZEOF_REF];   /* View ID. Set to id member when updating. */
1350
1351         int height, width;      /* The width and height of the main window */
1352         WINDOW *win;            /* The main window */
1353         WINDOW *title;          /* The title window living below the main window */
1354
1355         /* Navigation */
1356         unsigned long offset;   /* Offset of the window top */
1357         unsigned long lineno;   /* Current line number */
1358
1359         /* Searching */
1360         char grep[SIZEOF_STR];  /* Search string */
1361         regex_t *regex;         /* Pre-compiled regex */
1362
1363         /* If non-NULL, points to the view that opened this view. If this view
1364          * is closed tig will switch back to the parent view. */
1365         struct view *parent;
1366
1367         /* Buffering */
1368         unsigned long lines;    /* Total number of lines */
1369         struct line *line;      /* Line index */
1370         unsigned long line_size;/* Total number of allocated lines */
1371         unsigned int digits;    /* Number of digits in the lines member. */
1372
1373         /* Loading */
1374         FILE *pipe;
1375         time_t start_time;
1376 };
1377
1378 struct view_ops {
1379         /* What type of content being displayed. Used in the title bar. */
1380         const char *type;
1381         /* Open and reads in all view content. */
1382         bool (*open)(struct view *view);
1383         /* Read one line; updates view->line. */
1384         bool (*read)(struct view *view, char *data);
1385         /* Draw one line; @lineno must be < view->height. */
1386         bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected);
1387         /* Depending on view handle a special requests. */
1388         enum request (*request)(struct view *view, enum request request, struct line *line);
1389         /* Search for regex in a line. */
1390         bool (*grep)(struct view *view, struct line *line);
1391         /* Select line */
1392         void (*select)(struct view *view, struct line *line);
1393 };
1394
1395 static struct view_ops pager_ops;
1396 static struct view_ops main_ops;
1397 static struct view_ops tree_ops;
1398 static struct view_ops blob_ops;
1399 static struct view_ops help_ops;
1400 static struct view_ops status_ops;
1401 static struct view_ops stage_ops;
1402
1403 #define VIEW_STR(name, cmd, env, ref, ops, map) \
1404         { name, cmd, #env, ref, ops, map}
1405
1406 #define VIEW_(id, name, ops, ref) \
1407         VIEW_STR(name, TIG_##id##_CMD,  TIG_##id##_CMD, ref, ops, KEYMAP_##id)
1408
1409
1410 static struct view views[] = {
1411         VIEW_(MAIN,   "main",   &main_ops,   ref_head),
1412         VIEW_(DIFF,   "diff",   &pager_ops,  ref_commit),
1413         VIEW_(LOG,    "log",    &pager_ops,  ref_head),
1414         VIEW_(TREE,   "tree",   &tree_ops,   ref_commit),
1415         VIEW_(BLOB,   "blob",   &blob_ops,   ref_blob),
1416         VIEW_(HELP,   "help",   &help_ops,   ""),
1417         VIEW_(PAGER,  "pager",  &pager_ops,  "stdin"),
1418         VIEW_(STATUS, "status", &status_ops, ""),
1419         VIEW_(STAGE,  "stage",  &stage_ops,  ""),
1420 };
1421
1422 #define VIEW(req) (&views[(req) - REQ_OFFSET - 1])
1423
1424 #define foreach_view(view, i) \
1425         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
1426
1427 #define view_is_displayed(view) \
1428         (view == display[0] || view == display[1])
1429
1430 static bool
1431 draw_view_line(struct view *view, unsigned int lineno)
1432 {
1433         struct line *line;
1434         bool selected = (view->offset + lineno == view->lineno);
1435         bool draw_ok;
1436
1437         assert(view_is_displayed(view));
1438
1439         if (view->offset + lineno >= view->lines)
1440                 return FALSE;
1441
1442         line = &view->line[view->offset + lineno];
1443
1444         if (selected) {
1445                 line->selected = TRUE;
1446                 view->ops->select(view, line);
1447         } else if (line->selected) {
1448                 line->selected = FALSE;
1449                 wmove(view->win, lineno, 0);
1450                 wclrtoeol(view->win);
1451         }
1452
1453         scrollok(view->win, FALSE);
1454         draw_ok = view->ops->draw(view, line, lineno, selected);
1455         scrollok(view->win, TRUE);
1456
1457         return draw_ok;
1458 }
1459
1460 static void
1461 redraw_view_from(struct view *view, int lineno)
1462 {
1463         assert(0 <= lineno && lineno < view->height);
1464
1465         for (; lineno < view->height; lineno++) {
1466                 if (!draw_view_line(view, lineno))
1467                         break;
1468         }
1469
1470         redrawwin(view->win);
1471         if (input_mode)
1472                 wnoutrefresh(view->win);
1473         else
1474                 wrefresh(view->win);
1475 }
1476
1477 static void
1478 redraw_view(struct view *view)
1479 {
1480         wclear(view->win);
1481         redraw_view_from(view, 0);
1482 }
1483
1484
1485 static void
1486 update_view_title(struct view *view)
1487 {
1488         char buf[SIZEOF_STR];
1489         char state[SIZEOF_STR];
1490         size_t bufpos = 0, statelen = 0;
1491
1492         assert(view_is_displayed(view));
1493
1494         if (view != VIEW(REQ_VIEW_STATUS) && (view->lines || view->pipe)) {
1495                 unsigned int view_lines = view->offset + view->height;
1496                 unsigned int lines = view->lines
1497                                    ? MIN(view_lines, view->lines) * 100 / view->lines
1498                                    : 0;
1499
1500                 string_format_from(state, &statelen, "- %s %d of %d (%d%%)",
1501                                    view->ops->type,
1502                                    view->lineno + 1,
1503                                    view->lines,
1504                                    lines);
1505
1506                 if (view->pipe) {
1507                         time_t secs = time(NULL) - view->start_time;
1508
1509                         /* Three git seconds are a long time ... */
1510                         if (secs > 2)
1511                                 string_format_from(state, &statelen, " %lds", secs);
1512                 }
1513         }
1514
1515         string_format_from(buf, &bufpos, "[%s]", view->name);
1516         if (*view->ref && bufpos < view->width) {
1517                 size_t refsize = strlen(view->ref);
1518                 size_t minsize = bufpos + 1 + /* abbrev= */ 7 + 1 + statelen;
1519
1520                 if (minsize < view->width)
1521                         refsize = view->width - minsize + 7;
1522                 string_format_from(buf, &bufpos, " %.*s", (int) refsize, view->ref);
1523         }
1524
1525         if (statelen && bufpos < view->width) {
1526                 string_format_from(buf, &bufpos, " %s", state);
1527         }
1528
1529         if (view == display[current_view])
1530                 wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
1531         else
1532                 wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
1533
1534         mvwaddnstr(view->title, 0, 0, buf, bufpos);
1535         wclrtoeol(view->title);
1536         wmove(view->title, 0, view->width - 1);
1537
1538         if (input_mode)
1539                 wnoutrefresh(view->title);
1540         else
1541                 wrefresh(view->title);
1542 }
1543
1544 static void
1545 resize_display(void)
1546 {
1547         int offset, i;
1548         struct view *base = display[0];
1549         struct view *view = display[1] ? display[1] : display[0];
1550
1551         /* Setup window dimensions */
1552
1553         getmaxyx(stdscr, base->height, base->width);
1554
1555         /* Make room for the status window. */
1556         base->height -= 1;
1557
1558         if (view != base) {
1559                 /* Horizontal split. */
1560                 view->width   = base->width;
1561                 view->height  = SCALE_SPLIT_VIEW(base->height);
1562                 base->height -= view->height;
1563
1564                 /* Make room for the title bar. */
1565                 view->height -= 1;
1566         }
1567
1568         /* Make room for the title bar. */
1569         base->height -= 1;
1570
1571         offset = 0;
1572
1573         foreach_displayed_view (view, i) {
1574                 if (!view->win) {
1575                         view->win = newwin(view->height, 0, offset, 0);
1576                         if (!view->win)
1577                                 die("Failed to create %s view", view->name);
1578
1579                         scrollok(view->win, TRUE);
1580
1581                         view->title = newwin(1, 0, offset + view->height, 0);
1582                         if (!view->title)
1583                                 die("Failed to create title window");
1584
1585                 } else {
1586                         wresize(view->win, view->height, view->width);
1587                         mvwin(view->win,   offset, 0);
1588                         mvwin(view->title, offset + view->height, 0);
1589                 }
1590
1591                 offset += view->height + 1;
1592         }
1593 }
1594
1595 static void
1596 redraw_display(void)
1597 {
1598         struct view *view;
1599         int i;
1600
1601         foreach_displayed_view (view, i) {
1602                 redraw_view(view);
1603                 update_view_title(view);
1604         }
1605 }
1606
1607 static void
1608 update_display_cursor(struct view *view)
1609 {
1610         /* Move the cursor to the right-most column of the cursor line.
1611          *
1612          * XXX: This could turn out to be a bit expensive, but it ensures that
1613          * the cursor does not jump around. */
1614         if (view->lines) {
1615                 wmove(view->win, view->lineno - view->offset, view->width - 1);
1616                 wrefresh(view->win);
1617         }
1618 }
1619
1620 /*
1621  * Navigation
1622  */
1623
1624 /* Scrolling backend */
1625 static void
1626 do_scroll_view(struct view *view, int lines)
1627 {
1628         bool redraw_current_line = FALSE;
1629
1630         /* The rendering expects the new offset. */
1631         view->offset += lines;
1632
1633         assert(0 <= view->offset && view->offset < view->lines);
1634         assert(lines);
1635
1636         /* Move current line into the view. */
1637         if (view->lineno < view->offset) {
1638                 view->lineno = view->offset;
1639                 redraw_current_line = TRUE;
1640         } else if (view->lineno >= view->offset + view->height) {
1641                 view->lineno = view->offset + view->height - 1;
1642                 redraw_current_line = TRUE;
1643         }
1644
1645         assert(view->offset <= view->lineno && view->lineno < view->lines);
1646
1647         /* Redraw the whole screen if scrolling is pointless. */
1648         if (view->height < ABS(lines)) {
1649                 redraw_view(view);
1650
1651         } else {
1652                 int line = lines > 0 ? view->height - lines : 0;
1653                 int end = line + ABS(lines);
1654
1655                 wscrl(view->win, lines);
1656
1657                 for (; line < end; line++) {
1658                         if (!draw_view_line(view, line))
1659                                 break;
1660                 }
1661
1662                 if (redraw_current_line)
1663                         draw_view_line(view, view->lineno - view->offset);
1664         }
1665
1666         redrawwin(view->win);
1667         wrefresh(view->win);
1668         report("");
1669 }
1670
1671 /* Scroll frontend */
1672 static void
1673 scroll_view(struct view *view, enum request request)
1674 {
1675         int lines = 1;
1676
1677         assert(view_is_displayed(view));
1678
1679         switch (request) {
1680         case REQ_SCROLL_PAGE_DOWN:
1681                 lines = view->height;
1682         case REQ_SCROLL_LINE_DOWN:
1683                 if (view->offset + lines > view->lines)
1684                         lines = view->lines - view->offset;
1685
1686                 if (lines == 0 || view->offset + view->height >= view->lines) {
1687                         report("Cannot scroll beyond the last line");
1688                         return;
1689                 }
1690                 break;
1691
1692         case REQ_SCROLL_PAGE_UP:
1693                 lines = view->height;
1694         case REQ_SCROLL_LINE_UP:
1695                 if (lines > view->offset)
1696                         lines = view->offset;
1697
1698                 if (lines == 0) {
1699                         report("Cannot scroll beyond the first line");
1700                         return;
1701                 }
1702
1703                 lines = -lines;
1704                 break;
1705
1706         default:
1707                 die("request %d not handled in switch", request);
1708         }
1709
1710         do_scroll_view(view, lines);
1711 }
1712
1713 /* Cursor moving */
1714 static void
1715 move_view(struct view *view, enum request request)
1716 {
1717         int scroll_steps = 0;
1718         int steps;
1719
1720         switch (request) {
1721         case REQ_MOVE_FIRST_LINE:
1722                 steps = -view->lineno;
1723                 break;
1724
1725         case REQ_MOVE_LAST_LINE:
1726                 steps = view->lines - view->lineno - 1;
1727                 break;
1728
1729         case REQ_MOVE_PAGE_UP:
1730                 steps = view->height > view->lineno
1731                       ? -view->lineno : -view->height;
1732                 break;
1733
1734         case REQ_MOVE_PAGE_DOWN:
1735                 steps = view->lineno + view->height >= view->lines
1736                       ? view->lines - view->lineno - 1 : view->height;
1737                 break;
1738
1739         case REQ_MOVE_UP:
1740                 steps = -1;
1741                 break;
1742
1743         case REQ_MOVE_DOWN:
1744                 steps = 1;
1745                 break;
1746
1747         default:
1748                 die("request %d not handled in switch", request);
1749         }
1750
1751         if (steps <= 0 && view->lineno == 0) {
1752                 report("Cannot move beyond the first line");
1753                 return;
1754
1755         } else if (steps >= 0 && view->lineno + 1 >= view->lines) {
1756                 report("Cannot move beyond the last line");
1757                 return;
1758         }
1759
1760         /* Move the current line */
1761         view->lineno += steps;
1762         assert(0 <= view->lineno && view->lineno < view->lines);
1763
1764         /* Check whether the view needs to be scrolled */
1765         if (view->lineno < view->offset ||
1766             view->lineno >= view->offset + view->height) {
1767                 scroll_steps = steps;
1768                 if (steps < 0 && -steps > view->offset) {
1769                         scroll_steps = -view->offset;
1770
1771                 } else if (steps > 0) {
1772                         if (view->lineno == view->lines - 1 &&
1773                             view->lines > view->height) {
1774                                 scroll_steps = view->lines - view->offset - 1;
1775                                 if (scroll_steps >= view->height)
1776                                         scroll_steps -= view->height - 1;
1777                         }
1778                 }
1779         }
1780
1781         if (!view_is_displayed(view)) {
1782                 view->offset += scroll_steps;
1783                 assert(0 <= view->offset && view->offset < view->lines);
1784                 view->ops->select(view, &view->line[view->lineno]);
1785                 return;
1786         }
1787
1788         /* Repaint the old "current" line if we be scrolling */
1789         if (ABS(steps) < view->height)
1790                 draw_view_line(view, view->lineno - steps - view->offset);
1791
1792         if (scroll_steps) {
1793                 do_scroll_view(view, scroll_steps);
1794                 return;
1795         }
1796
1797         /* Draw the current line */
1798         draw_view_line(view, view->lineno - view->offset);
1799
1800         redrawwin(view->win);
1801         wrefresh(view->win);
1802         report("");
1803 }
1804
1805
1806 /*
1807  * Searching
1808  */
1809
1810 static void search_view(struct view *view, enum request request);
1811
1812 static bool
1813 find_next_line(struct view *view, unsigned long lineno, struct line *line)
1814 {
1815         assert(view_is_displayed(view));
1816
1817         if (!view->ops->grep(view, line))
1818                 return FALSE;
1819
1820         if (lineno - view->offset >= view->height) {
1821                 view->offset = lineno;
1822                 view->lineno = lineno;
1823                 redraw_view(view);
1824
1825         } else {
1826                 unsigned long old_lineno = view->lineno - view->offset;
1827
1828                 view->lineno = lineno;
1829                 draw_view_line(view, old_lineno);
1830
1831                 draw_view_line(view, view->lineno - view->offset);
1832                 redrawwin(view->win);
1833                 wrefresh(view->win);
1834         }
1835
1836         report("Line %ld matches '%s'", lineno + 1, view->grep);
1837         return TRUE;
1838 }
1839
1840 static void
1841 find_next(struct view *view, enum request request)
1842 {
1843         unsigned long lineno = view->lineno;
1844         int direction;
1845
1846         if (!*view->grep) {
1847                 if (!*opt_search)
1848                         report("No previous search");
1849                 else
1850                         search_view(view, request);
1851                 return;
1852         }
1853
1854         switch (request) {
1855         case REQ_SEARCH:
1856         case REQ_FIND_NEXT:
1857                 direction = 1;
1858                 break;
1859
1860         case REQ_SEARCH_BACK:
1861         case REQ_FIND_PREV:
1862                 direction = -1;
1863                 break;
1864
1865         default:
1866                 return;
1867         }
1868
1869         if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
1870                 lineno += direction;
1871
1872         /* Note, lineno is unsigned long so will wrap around in which case it
1873          * will become bigger than view->lines. */
1874         for (; lineno < view->lines; lineno += direction) {
1875                 struct line *line = &view->line[lineno];
1876
1877                 if (find_next_line(view, lineno, line))
1878                         return;
1879         }
1880
1881         report("No match found for '%s'", view->grep);
1882 }
1883
1884 static void
1885 search_view(struct view *view, enum request request)
1886 {
1887         int regex_err;
1888
1889         if (view->regex) {
1890                 regfree(view->regex);
1891                 *view->grep = 0;
1892         } else {
1893                 view->regex = calloc(1, sizeof(*view->regex));
1894                 if (!view->regex)
1895                         return;
1896         }
1897
1898         regex_err = regcomp(view->regex, opt_search, REG_EXTENDED);
1899         if (regex_err != 0) {
1900                 char buf[SIZEOF_STR] = "unknown error";
1901
1902                 regerror(regex_err, view->regex, buf, sizeof(buf));
1903                 report("Search failed: %s", buf);
1904                 return;
1905         }
1906
1907         string_copy(view->grep, opt_search);
1908
1909         find_next(view, request);
1910 }
1911
1912 /*
1913  * Incremental updating
1914  */
1915
1916 static void
1917 end_update(struct view *view)
1918 {
1919         if (!view->pipe)
1920                 return;
1921         set_nonblocking_input(FALSE);
1922         if (view->pipe == stdin)
1923                 fclose(view->pipe);
1924         else
1925                 pclose(view->pipe);
1926         view->pipe = NULL;
1927 }
1928
1929 static bool
1930 begin_update(struct view *view)
1931 {
1932         if (view->pipe)
1933                 end_update(view);
1934
1935         if (opt_cmd[0]) {
1936                 string_copy(view->cmd, opt_cmd);
1937                 opt_cmd[0] = 0;
1938                 /* When running random commands, initially show the
1939                  * command in the title. However, it maybe later be
1940                  * overwritten if a commit line is selected. */
1941                 if (view == VIEW(REQ_VIEW_PAGER))
1942                         string_copy(view->ref, view->cmd);
1943                 else
1944                         view->ref[0] = 0;
1945
1946         } else if (view == VIEW(REQ_VIEW_TREE)) {
1947                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1948                 char path[SIZEOF_STR];
1949
1950                 if (strcmp(view->vid, view->id))
1951                         opt_path[0] = path[0] = 0;
1952                 else if (sq_quote(path, 0, opt_path) >= sizeof(path))
1953                         return FALSE;
1954
1955                 if (!string_format(view->cmd, format, view->id, path))
1956                         return FALSE;
1957
1958         } else {
1959                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1960                 const char *id = view->id;
1961
1962                 if (!string_format(view->cmd, format, id, id, id, id, id))
1963                         return FALSE;
1964
1965                 /* Put the current ref_* value to the view title ref
1966                  * member. This is needed by the blob view. Most other
1967                  * views sets it automatically after loading because the
1968                  * first line is a commit line. */
1969                 string_copy_rev(view->ref, view->id);
1970         }
1971
1972         /* Special case for the pager view. */
1973         if (opt_pipe) {
1974                 view->pipe = opt_pipe;
1975                 opt_pipe = NULL;
1976         } else {
1977                 view->pipe = popen(view->cmd, "r");
1978         }
1979
1980         if (!view->pipe)
1981                 return FALSE;
1982
1983         set_nonblocking_input(TRUE);
1984
1985         view->offset = 0;
1986         view->lines  = 0;
1987         view->lineno = 0;
1988         string_copy_rev(view->vid, view->id);
1989
1990         if (view->line) {
1991                 int i;
1992
1993                 for (i = 0; i < view->lines; i++)
1994                         if (view->line[i].data)
1995                                 free(view->line[i].data);
1996
1997                 free(view->line);
1998                 view->line = NULL;
1999         }
2000
2001         view->start_time = time(NULL);
2002
2003         return TRUE;
2004 }
2005
2006 static struct line *
2007 realloc_lines(struct view *view, size_t line_size)
2008 {
2009         struct line *tmp = realloc(view->line, sizeof(*view->line) * line_size);
2010
2011         if (!tmp)
2012                 return NULL;
2013
2014         view->line = tmp;
2015         view->line_size = line_size;
2016         return view->line;
2017 }
2018
2019 static bool
2020 update_view(struct view *view)
2021 {
2022         char in_buffer[BUFSIZ];
2023         char out_buffer[BUFSIZ * 2];
2024         char *line;
2025         /* The number of lines to read. If too low it will cause too much
2026          * redrawing (and possible flickering), if too high responsiveness
2027          * will suffer. */
2028         unsigned long lines = view->height;
2029         int redraw_from = -1;
2030
2031         if (!view->pipe)
2032                 return TRUE;
2033
2034         /* Only redraw if lines are visible. */
2035         if (view->offset + view->height >= view->lines)
2036                 redraw_from = view->lines - view->offset;
2037
2038         /* FIXME: This is probably not perfect for backgrounded views. */
2039         if (!realloc_lines(view, view->lines + lines))
2040                 goto alloc_error;
2041
2042         while ((line = fgets(in_buffer, sizeof(in_buffer), view->pipe))) {
2043                 size_t linelen = strlen(line);
2044
2045                 if (linelen)
2046                         line[linelen - 1] = 0;
2047
2048                 if (opt_iconv != ICONV_NONE) {
2049                         ICONV_CONST char *inbuf = line;
2050                         size_t inlen = linelen;
2051
2052                         char *outbuf = out_buffer;
2053                         size_t outlen = sizeof(out_buffer);
2054
2055                         size_t ret;
2056
2057                         ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
2058                         if (ret != (size_t) -1) {
2059                                 line = out_buffer;
2060                                 linelen = strlen(out_buffer);
2061                         }
2062                 }
2063
2064                 if (!view->ops->read(view, line))
2065                         goto alloc_error;
2066
2067                 if (lines-- == 1)
2068                         break;
2069         }
2070
2071         {
2072                 int digits;
2073
2074                 lines = view->lines;
2075                 for (digits = 0; lines; digits++)
2076                         lines /= 10;
2077
2078                 /* Keep the displayed view in sync with line number scaling. */
2079                 if (digits != view->digits) {
2080                         view->digits = digits;
2081                         redraw_from = 0;
2082                 }
2083         }
2084
2085         if (!view_is_displayed(view))
2086                 goto check_pipe;
2087
2088         if (view == VIEW(REQ_VIEW_TREE)) {
2089                 /* Clear the view and redraw everything since the tree sorting
2090                  * might have rearranged things. */
2091                 redraw_view(view);
2092
2093         } else if (redraw_from >= 0) {
2094                 /* If this is an incremental update, redraw the previous line
2095                  * since for commits some members could have changed when
2096                  * loading the main view. */
2097                 if (redraw_from > 0)
2098                         redraw_from--;
2099
2100                 /* Since revision graph visualization requires knowledge
2101                  * about the parent commit, it causes a further one-off
2102                  * needed to be redrawn for incremental updates. */
2103                 if (redraw_from > 0 && opt_rev_graph)
2104                         redraw_from--;
2105
2106                 /* Incrementally draw avoids flickering. */
2107                 redraw_view_from(view, redraw_from);
2108         }
2109
2110         /* Update the title _after_ the redraw so that if the redraw picks up a
2111          * commit reference in view->ref it'll be available here. */
2112         update_view_title(view);
2113
2114 check_pipe:
2115         if (ferror(view->pipe)) {
2116                 report("Failed to read: %s", strerror(errno));
2117                 goto end;
2118
2119         } else if (feof(view->pipe)) {
2120                 report("");
2121                 goto end;
2122         }
2123
2124         return TRUE;
2125
2126 alloc_error:
2127         report("Allocation failure");
2128
2129 end:
2130         view->ops->read(view, NULL);
2131         end_update(view);
2132         return FALSE;
2133 }
2134
2135 static struct line *
2136 add_line_data(struct view *view, void *data, enum line_type type)
2137 {
2138         struct line *line = &view->line[view->lines++];
2139
2140         memset(line, 0, sizeof(*line));
2141         line->type = type;
2142         line->data = data;
2143
2144         return line;
2145 }
2146
2147 static struct line *
2148 add_line_text(struct view *view, char *data, enum line_type type)
2149 {
2150         if (data)
2151                 data = strdup(data);
2152
2153         return data ? add_line_data(view, data, type) : NULL;
2154 }
2155
2156
2157 /*
2158  * View opening
2159  */
2160
2161 enum open_flags {
2162         OPEN_DEFAULT = 0,       /* Use default view switching. */
2163         OPEN_SPLIT = 1,         /* Split current view. */
2164         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
2165         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2166 };
2167
2168 static void
2169 open_view(struct view *prev, enum request request, enum open_flags flags)
2170 {
2171         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
2172         bool split = !!(flags & OPEN_SPLIT);
2173         bool reload = !!(flags & OPEN_RELOAD);
2174         struct view *view = VIEW(request);
2175         int nviews = displayed_views();
2176         struct view *base_view = display[0];
2177
2178         if (view == prev && nviews == 1 && !reload) {
2179                 report("Already in %s view", view->name);
2180                 return;
2181         }
2182
2183         if (view->ops->open) {
2184                 if (!view->ops->open(view)) {
2185                         report("Failed to load %s view", view->name);
2186                         return;
2187                 }
2188
2189         } else if ((reload || strcmp(view->vid, view->id)) &&
2190                    !begin_update(view)) {
2191                 report("Failed to load %s view", view->name);
2192                 return;
2193         }
2194
2195         if (split) {
2196                 display[1] = view;
2197                 if (!backgrounded)
2198                         current_view = 1;
2199         } else {
2200                 /* Maximize the current view. */
2201                 memset(display, 0, sizeof(display));
2202                 current_view = 0;
2203                 display[current_view] = view;
2204         }
2205
2206         /* Resize the view when switching between split- and full-screen,
2207          * or when switching between two different full-screen views. */
2208         if (nviews != displayed_views() ||
2209             (nviews == 1 && base_view != display[0]))
2210                 resize_display();
2211
2212         if (split && prev->lineno - prev->offset >= prev->height) {
2213                 /* Take the title line into account. */
2214                 int lines = prev->lineno - prev->offset - prev->height + 1;
2215
2216                 /* Scroll the view that was split if the current line is
2217                  * outside the new limited view. */
2218                 do_scroll_view(prev, lines);
2219         }
2220
2221         if (prev && view != prev) {
2222                 if (split && !backgrounded) {
2223                         /* "Blur" the previous view. */
2224                         update_view_title(prev);
2225                 }
2226
2227                 view->parent = prev;
2228         }
2229
2230         if (view->pipe && view->lines == 0) {
2231                 /* Clear the old view and let the incremental updating refill
2232                  * the screen. */
2233                 wclear(view->win);
2234                 report("");
2235         } else {
2236                 redraw_view(view);
2237                 report("");
2238         }
2239
2240         /* If the view is backgrounded the above calls to report()
2241          * won't redraw the view title. */
2242         if (backgrounded)
2243                 update_view_title(view);
2244 }
2245
2246 static void
2247 open_external_viewer(const char *cmd)
2248 {
2249         def_prog_mode();           /* save current tty modes */
2250         endwin();                  /* restore original tty modes */
2251         system(cmd);
2252         fprintf(stderr, "Press Enter to continue");
2253         getc(stdin);
2254         reset_prog_mode();
2255         redraw_display();
2256 }
2257
2258 static void
2259 open_mergetool(const char *file)
2260 {
2261         char cmd[SIZEOF_STR];
2262         char file_sq[SIZEOF_STR];
2263
2264         if (sq_quote(file_sq, 0, file) < sizeof(file_sq) &&
2265             string_format(cmd, "git mergetool %s", file_sq)) {
2266                 open_external_viewer(cmd);
2267         }
2268 }
2269
2270 static void
2271 open_editor(bool from_root, const char *file)
2272 {
2273         char cmd[SIZEOF_STR];
2274         char file_sq[SIZEOF_STR];
2275         char *editor;
2276         char *prefix = from_root ? opt_cdup : "";
2277
2278         editor = getenv("GIT_EDITOR");
2279         if (!editor && *opt_editor)
2280                 editor = opt_editor;
2281         if (!editor)
2282                 editor = getenv("VISUAL");
2283         if (!editor)
2284                 editor = getenv("EDITOR");
2285         if (!editor)
2286                 editor = "vi";
2287
2288         if (sq_quote(file_sq, 0, file) < sizeof(file_sq) &&
2289             string_format(cmd, "%s %s%s", editor, prefix, file_sq)) {
2290                 open_external_viewer(cmd);
2291         }
2292 }
2293
2294 static void
2295 open_run_request(enum request request)
2296 {
2297         struct run_request *req = get_run_request(request);
2298         char buf[SIZEOF_STR * 2];
2299         size_t bufpos;
2300         char *cmd;
2301
2302         if (!req) {
2303                 report("Unknown run request");
2304                 return;
2305         }
2306
2307         bufpos = 0;
2308         cmd = req->cmd;
2309
2310         while (cmd) {
2311                 char *next = strstr(cmd, "%(");
2312                 int len = next - cmd;
2313                 char *value;
2314
2315                 if (!next) {
2316                         len = strlen(cmd);
2317                         value = "";
2318
2319                 } else if (!strncmp(next, "%(head)", 7)) {
2320                         value = ref_head;
2321
2322                 } else if (!strncmp(next, "%(commit)", 9)) {
2323                         value = ref_commit;
2324
2325                 } else if (!strncmp(next, "%(blob)", 7)) {
2326                         value = ref_blob;
2327
2328                 } else {
2329                         report("Unknown replacement in run request: `%s`", req->cmd);
2330                         return;
2331                 }
2332
2333                 if (!string_format_from(buf, &bufpos, "%.*s%s", len, cmd, value))
2334                         return;
2335
2336                 if (next)
2337                         next = strchr(next, ')') + 1;
2338                 cmd = next;
2339         }
2340
2341         open_external_viewer(buf);
2342 }
2343
2344 /*
2345  * User request switch noodle
2346  */
2347
2348 static int
2349 view_driver(struct view *view, enum request request)
2350 {
2351         int i;
2352
2353         if (request == REQ_NONE) {
2354                 doupdate();
2355                 return TRUE;
2356         }
2357
2358         if (request > REQ_NONE) {
2359                 open_run_request(request);
2360                 return TRUE;
2361         }
2362
2363         if (view && view->lines) {
2364                 request = view->ops->request(view, request, &view->line[view->lineno]);
2365                 if (request == REQ_NONE)
2366                         return TRUE;
2367         }
2368
2369         switch (request) {
2370         case REQ_MOVE_UP:
2371         case REQ_MOVE_DOWN:
2372         case REQ_MOVE_PAGE_UP:
2373         case REQ_MOVE_PAGE_DOWN:
2374         case REQ_MOVE_FIRST_LINE:
2375         case REQ_MOVE_LAST_LINE:
2376                 move_view(view, request);
2377                 break;
2378
2379         case REQ_SCROLL_LINE_DOWN:
2380         case REQ_SCROLL_LINE_UP:
2381         case REQ_SCROLL_PAGE_DOWN:
2382         case REQ_SCROLL_PAGE_UP:
2383                 scroll_view(view, request);
2384                 break;
2385
2386         case REQ_VIEW_BLOB:
2387                 if (!ref_blob[0]) {
2388                         report("No file chosen, press %s to open tree view",
2389                                get_key(REQ_VIEW_TREE));
2390                         break;
2391                 }
2392                 open_view(view, request, OPEN_DEFAULT);
2393                 break;
2394
2395         case REQ_VIEW_PAGER:
2396                 if (!opt_pipe && !VIEW(REQ_VIEW_PAGER)->lines) {
2397                         report("No pager content, press %s to run command from prompt",
2398                                get_key(REQ_PROMPT));
2399                         break;
2400                 }
2401                 open_view(view, request, OPEN_DEFAULT);
2402                 break;
2403
2404         case REQ_VIEW_STAGE:
2405                 if (!VIEW(REQ_VIEW_STAGE)->lines) {
2406                         report("No stage content, press %s to open the status view and choose file",
2407                                get_key(REQ_VIEW_STATUS));
2408                         break;
2409                 }
2410                 open_view(view, request, OPEN_DEFAULT);
2411                 break;
2412
2413         case REQ_VIEW_STATUS:
2414                 if (opt_is_inside_work_tree == FALSE) {
2415                         report("The status view requires a working tree");
2416                         break;
2417                 }
2418                 open_view(view, request, OPEN_DEFAULT);
2419                 break;
2420
2421         case REQ_VIEW_MAIN:
2422         case REQ_VIEW_DIFF:
2423         case REQ_VIEW_LOG:
2424         case REQ_VIEW_TREE:
2425         case REQ_VIEW_HELP:
2426                 open_view(view, request, OPEN_DEFAULT);
2427                 break;
2428
2429         case REQ_NEXT:
2430         case REQ_PREVIOUS:
2431                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
2432
2433                 if ((view == VIEW(REQ_VIEW_DIFF) &&
2434                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
2435                    (view == VIEW(REQ_VIEW_STAGE) &&
2436                      view->parent == VIEW(REQ_VIEW_STATUS)) ||
2437                    (view == VIEW(REQ_VIEW_BLOB) &&
2438                      view->parent == VIEW(REQ_VIEW_TREE))) {
2439                         int line;
2440
2441                         view = view->parent;
2442                         line = view->lineno;
2443                         move_view(view, request);
2444                         if (view_is_displayed(view))
2445                                 update_view_title(view);
2446                         if (line != view->lineno)
2447                                 view->ops->request(view, REQ_ENTER,
2448                                                    &view->line[view->lineno]);
2449
2450                 } else {
2451                         move_view(view, request);
2452                 }
2453                 break;
2454
2455         case REQ_VIEW_NEXT:
2456         {
2457                 int nviews = displayed_views();
2458                 int next_view = (current_view + 1) % nviews;
2459
2460                 if (next_view == current_view) {
2461                         report("Only one view is displayed");
2462                         break;
2463                 }
2464
2465                 current_view = next_view;
2466                 /* Blur out the title of the previous view. */
2467                 update_view_title(view);
2468                 report("");
2469                 break;
2470         }
2471         case REQ_REFRESH:
2472                 report("Refreshing is not yet supported for the %s view", view->name);
2473                 break;
2474
2475         case REQ_TOGGLE_LINENO:
2476                 opt_line_number = !opt_line_number;
2477                 redraw_display();
2478                 break;
2479
2480         case REQ_TOGGLE_REV_GRAPH:
2481                 opt_rev_graph = !opt_rev_graph;
2482                 redraw_display();
2483                 break;
2484
2485         case REQ_PROMPT:
2486                 /* Always reload^Wrerun commands from the prompt. */
2487                 open_view(view, opt_request, OPEN_RELOAD);
2488                 break;
2489
2490         case REQ_SEARCH:
2491         case REQ_SEARCH_BACK:
2492                 search_view(view, request);
2493                 break;
2494
2495         case REQ_FIND_NEXT:
2496         case REQ_FIND_PREV:
2497                 find_next(view, request);
2498                 break;
2499
2500         case REQ_STOP_LOADING:
2501                 for (i = 0; i < ARRAY_SIZE(views); i++) {
2502                         view = &views[i];
2503                         if (view->pipe)
2504                                 report("Stopped loading the %s view", view->name),
2505                         end_update(view);
2506                 }
2507                 break;
2508
2509         case REQ_SHOW_VERSION:
2510                 report("tig-%s (built %s)", TIG_VERSION, __DATE__);
2511                 return TRUE;
2512
2513         case REQ_SCREEN_RESIZE:
2514                 resize_display();
2515                 /* Fall-through */
2516         case REQ_SCREEN_REDRAW:
2517                 redraw_display();
2518                 break;
2519
2520         case REQ_EDIT:
2521                 report("Nothing to edit");
2522                 break;
2523
2524
2525         case REQ_ENTER:
2526                 report("Nothing to enter");
2527                 break;
2528
2529
2530         case REQ_VIEW_CLOSE:
2531                 /* XXX: Mark closed views by letting view->parent point to the
2532                  * view itself. Parents to closed view should never be
2533                  * followed. */
2534                 if (view->parent &&
2535                     view->parent->parent != view->parent) {
2536                         memset(display, 0, sizeof(display));
2537                         current_view = 0;
2538                         display[current_view] = view->parent;
2539                         view->parent = view;
2540                         resize_display();
2541                         redraw_display();
2542                         break;
2543                 }
2544                 /* Fall-through */
2545         case REQ_QUIT:
2546                 return FALSE;
2547
2548         default:
2549                 /* An unknown key will show most commonly used commands. */
2550                 report("Unknown key, press 'h' for help");
2551                 return TRUE;
2552         }
2553
2554         return TRUE;
2555 }
2556
2557
2558 /*
2559  * Pager backend
2560  */
2561
2562 static bool
2563 pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2564 {
2565         char *text = line->data;
2566         enum line_type type = line->type;
2567         int textlen = strlen(text);
2568         int attr;
2569
2570         wmove(view->win, lineno, 0);
2571
2572         if (selected) {
2573                 type = LINE_CURSOR;
2574                 wchgat(view->win, -1, 0, type, NULL);
2575         }
2576
2577         attr = get_line_attr(type);
2578         wattrset(view->win, attr);
2579
2580         if (opt_line_number || opt_tab_size < TABSIZE) {
2581                 static char spaces[] = "                    ";
2582                 int col_offset = 0, col = 0;
2583
2584                 if (opt_line_number) {
2585                         unsigned long real_lineno = view->offset + lineno + 1;
2586
2587                         if (real_lineno == 1 ||
2588                             (real_lineno % opt_num_interval) == 0) {
2589                                 wprintw(view->win, "%.*d", view->digits, real_lineno);
2590
2591                         } else {
2592                                 waddnstr(view->win, spaces,
2593                                          MIN(view->digits, STRING_SIZE(spaces)));
2594                         }
2595                         waddstr(view->win, ": ");
2596                         col_offset = view->digits + 2;
2597                 }
2598
2599                 while (text && col_offset + col < view->width) {
2600                         int cols_max = view->width - col_offset - col;
2601                         char *pos = text;
2602                         int cols;
2603
2604                         if (*text == '\t') {
2605                                 text++;
2606                                 assert(sizeof(spaces) > TABSIZE);
2607                                 pos = spaces;
2608                                 cols = opt_tab_size - (col % opt_tab_size);
2609
2610                         } else {
2611                                 text = strchr(text, '\t');
2612                                 cols = line ? text - pos : strlen(pos);
2613                         }
2614
2615                         waddnstr(view->win, pos, MIN(cols, cols_max));
2616                         col += cols;
2617                 }
2618
2619         } else {
2620                 int col = 0, pos = 0;
2621
2622                 for (; pos < textlen && col < view->width; pos++, col++)
2623                         if (text[pos] == '\t')
2624                                 col += TABSIZE - (col % TABSIZE) - 1;
2625
2626                 waddnstr(view->win, text, pos);
2627         }
2628
2629         return TRUE;
2630 }
2631
2632 static bool
2633 add_describe_ref(char *buf, size_t *bufpos, char *commit_id, const char *sep)
2634 {
2635         char refbuf[SIZEOF_STR];
2636         char *ref = NULL;
2637         FILE *pipe;
2638
2639         if (!string_format(refbuf, "git describe %s 2>/dev/null", commit_id))
2640                 return TRUE;
2641
2642         pipe = popen(refbuf, "r");
2643         if (!pipe)
2644                 return TRUE;
2645
2646         if ((ref = fgets(refbuf, sizeof(refbuf), pipe)))
2647                 ref = chomp_string(ref);
2648         pclose(pipe);
2649
2650         if (!ref || !*ref)
2651                 return TRUE;
2652
2653         /* This is the only fatal call, since it can "corrupt" the buffer. */
2654         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
2655                 return FALSE;
2656
2657         return TRUE;
2658 }
2659
2660 static void
2661 add_pager_refs(struct view *view, struct line *line)
2662 {
2663         char buf[SIZEOF_STR];
2664         char *commit_id = line->data + STRING_SIZE("commit ");
2665         struct ref **refs;
2666         size_t bufpos = 0, refpos = 0;
2667         const char *sep = "Refs: ";
2668         bool is_tag = FALSE;
2669
2670         assert(line->type == LINE_COMMIT);
2671
2672         refs = get_refs(commit_id);
2673         if (!refs) {
2674                 if (view == VIEW(REQ_VIEW_DIFF))
2675                         goto try_add_describe_ref;
2676                 return;
2677         }
2678
2679         do {
2680                 struct ref *ref = refs[refpos];
2681                 char *fmt = ref->tag    ? "%s[%s]" :
2682                             ref->remote ? "%s<%s>" : "%s%s";
2683
2684                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
2685                         return;
2686                 sep = ", ";
2687                 if (ref->tag)
2688                         is_tag = TRUE;
2689         } while (refs[refpos++]->next);
2690
2691         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
2692 try_add_describe_ref:
2693                 /* Add <tag>-g<commit_id> "fake" reference. */
2694                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
2695                         return;
2696         }
2697
2698         if (bufpos == 0)
2699                 return;
2700
2701         if (!realloc_lines(view, view->line_size + 1))
2702                 return;
2703
2704         add_line_text(view, buf, LINE_PP_REFS);
2705 }
2706
2707 static bool
2708 pager_read(struct view *view, char *data)
2709 {
2710         struct line *line;
2711
2712         if (!data)
2713                 return TRUE;
2714
2715         line = add_line_text(view, data, get_line_type(data));
2716         if (!line)
2717                 return FALSE;
2718
2719         if (line->type == LINE_COMMIT &&
2720             (view == VIEW(REQ_VIEW_DIFF) ||
2721              view == VIEW(REQ_VIEW_LOG)))
2722                 add_pager_refs(view, line);
2723
2724         return TRUE;
2725 }
2726
2727 static enum request
2728 pager_request(struct view *view, enum request request, struct line *line)
2729 {
2730         int split = 0;
2731
2732         if (request != REQ_ENTER)
2733                 return request;
2734
2735         if (line->type == LINE_COMMIT &&
2736            (view == VIEW(REQ_VIEW_LOG) ||
2737             view == VIEW(REQ_VIEW_PAGER))) {
2738                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
2739                 split = 1;
2740         }
2741
2742         /* Always scroll the view even if it was split. That way
2743          * you can use Enter to scroll through the log view and
2744          * split open each commit diff. */
2745         scroll_view(view, REQ_SCROLL_LINE_DOWN);
2746
2747         /* FIXME: A minor workaround. Scrolling the view will call report("")
2748          * but if we are scrolling a non-current view this won't properly
2749          * update the view title. */
2750         if (split)
2751                 update_view_title(view);
2752
2753         return REQ_NONE;
2754 }
2755
2756 static bool
2757 pager_grep(struct view *view, struct line *line)
2758 {
2759         regmatch_t pmatch;
2760         char *text = line->data;
2761
2762         if (!*text)
2763                 return FALSE;
2764
2765         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
2766                 return FALSE;
2767
2768         return TRUE;
2769 }
2770
2771 static void
2772 pager_select(struct view *view, struct line *line)
2773 {
2774         if (line->type == LINE_COMMIT) {
2775                 char *text = line->data + STRING_SIZE("commit ");
2776
2777                 if (view != VIEW(REQ_VIEW_PAGER))
2778                         string_copy_rev(view->ref, text);
2779                 string_copy_rev(ref_commit, text);
2780         }
2781 }
2782
2783 static struct view_ops pager_ops = {
2784         "line",
2785         NULL,
2786         pager_read,
2787         pager_draw,
2788         pager_request,
2789         pager_grep,
2790         pager_select,
2791 };
2792
2793
2794 /*
2795  * Help backend
2796  */
2797
2798 static bool
2799 help_open(struct view *view)
2800 {
2801         char buf[BUFSIZ];
2802         int lines = ARRAY_SIZE(req_info) + 2;
2803         int i;
2804
2805         if (view->lines > 0)
2806                 return TRUE;
2807
2808         for (i = 0; i < ARRAY_SIZE(req_info); i++)
2809                 if (!req_info[i].request)
2810                         lines++;
2811
2812         lines += run_requests + 1;
2813
2814         view->line = calloc(lines, sizeof(*view->line));
2815         if (!view->line)
2816                 return FALSE;
2817
2818         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
2819
2820         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
2821                 char *key;
2822
2823                 if (req_info[i].request == REQ_NONE)
2824                         continue;
2825
2826                 if (!req_info[i].request) {
2827                         add_line_text(view, "", LINE_DEFAULT);
2828                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
2829                         continue;
2830                 }
2831
2832                 key = get_key(req_info[i].request);
2833                 if (!*key)
2834                         key = "(no key defined)";
2835
2836                 if (!string_format(buf, "    %-25s %s", key, req_info[i].help))
2837                         continue;
2838
2839                 add_line_text(view, buf, LINE_DEFAULT);
2840         }
2841
2842         if (run_requests) {
2843                 add_line_text(view, "", LINE_DEFAULT);
2844                 add_line_text(view, "External commands:", LINE_DEFAULT);
2845         }
2846
2847         for (i = 0; i < run_requests; i++) {
2848                 struct run_request *req = get_run_request(REQ_NONE + i + 1);
2849                 char *key;
2850
2851                 if (!req)
2852                         continue;
2853
2854                 key = get_key_name(req->key);
2855                 if (!*key)
2856                         key = "(no key defined)";
2857
2858                 if (!string_format(buf, "    %-10s %-14s `%s`",
2859                                    keymap_table[req->keymap].name,
2860                                    key, req->cmd))
2861                         continue;
2862
2863                 add_line_text(view, buf, LINE_DEFAULT);
2864         }
2865
2866         return TRUE;
2867 }
2868
2869 static struct view_ops help_ops = {
2870         "line",
2871         help_open,
2872         NULL,
2873         pager_draw,
2874         pager_request,
2875         pager_grep,
2876         pager_select,
2877 };
2878
2879
2880 /*
2881  * Tree backend
2882  */
2883
2884 struct tree_stack_entry {
2885         struct tree_stack_entry *prev;  /* Entry below this in the stack */
2886         unsigned long lineno;           /* Line number to restore */
2887         char *name;                     /* Position of name in opt_path */
2888 };
2889
2890 /* The top of the path stack. */
2891 static struct tree_stack_entry *tree_stack = NULL;
2892 unsigned long tree_lineno = 0;
2893
2894 static void
2895 pop_tree_stack_entry(void)
2896 {
2897         struct tree_stack_entry *entry = tree_stack;
2898
2899         tree_lineno = entry->lineno;
2900         entry->name[0] = 0;
2901         tree_stack = entry->prev;
2902         free(entry);
2903 }
2904
2905 static void
2906 push_tree_stack_entry(char *name, unsigned long lineno)
2907 {
2908         struct tree_stack_entry *entry = calloc(1, sizeof(*entry));
2909         size_t pathlen = strlen(opt_path);
2910
2911         if (!entry)
2912                 return;
2913
2914         entry->prev = tree_stack;
2915         entry->name = opt_path + pathlen;
2916         tree_stack = entry;
2917
2918         if (!string_format_from(opt_path, &pathlen, "%s/", name)) {
2919                 pop_tree_stack_entry();
2920                 return;
2921         }
2922
2923         /* Move the current line to the first tree entry. */
2924         tree_lineno = 1;
2925         entry->lineno = lineno;
2926 }
2927
2928 /* Parse output from git-ls-tree(1):
2929  *
2930  * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile
2931  * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README
2932  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
2933  * 100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38 web.conf
2934  */
2935
2936 #define SIZEOF_TREE_ATTR \
2937         STRING_SIZE("100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38\t")
2938
2939 #define TREE_UP_FORMAT "040000 tree %s\t.."
2940
2941 static int
2942 tree_compare_entry(enum line_type type1, char *name1,
2943                    enum line_type type2, char *name2)
2944 {
2945         if (type1 != type2) {
2946                 if (type1 == LINE_TREE_DIR)
2947                         return -1;
2948                 return 1;
2949         }
2950
2951         return strcmp(name1, name2);
2952 }
2953
2954 static bool
2955 tree_read(struct view *view, char *text)
2956 {
2957         size_t textlen = text ? strlen(text) : 0;
2958         char buf[SIZEOF_STR];
2959         unsigned long pos;
2960         enum line_type type;
2961         bool first_read = view->lines == 0;
2962
2963         if (textlen <= SIZEOF_TREE_ATTR)
2964                 return FALSE;
2965
2966         type = text[STRING_SIZE("100644 ")] == 't'
2967              ? LINE_TREE_DIR : LINE_TREE_FILE;
2968
2969         if (first_read) {
2970                 /* Add path info line */
2971                 if (!string_format(buf, "Directory path /%s", opt_path) ||
2972                     !realloc_lines(view, view->line_size + 1) ||
2973                     !add_line_text(view, buf, LINE_DEFAULT))
2974                         return FALSE;
2975
2976                 /* Insert "link" to parent directory. */
2977                 if (*opt_path) {
2978                         if (!string_format(buf, TREE_UP_FORMAT, view->ref) ||
2979                             !realloc_lines(view, view->line_size + 1) ||
2980                             !add_line_text(view, buf, LINE_TREE_DIR))
2981                                 return FALSE;
2982                 }
2983         }
2984
2985         /* Strip the path part ... */
2986         if (*opt_path) {
2987                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
2988                 size_t striplen = strlen(opt_path);
2989                 char *path = text + SIZEOF_TREE_ATTR;
2990
2991                 if (pathlen > striplen)
2992                         memmove(path, path + striplen,
2993                                 pathlen - striplen + 1);
2994         }
2995
2996         /* Skip "Directory ..." and ".." line. */
2997         for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
2998                 struct line *line = &view->line[pos];
2999                 char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR;
3000                 char *path2 = text + SIZEOF_TREE_ATTR;
3001                 int cmp = tree_compare_entry(line->type, path1, type, path2);
3002
3003                 if (cmp <= 0)
3004                         continue;
3005
3006                 text = strdup(text);
3007                 if (!text)
3008                         return FALSE;
3009
3010                 if (view->lines > pos)
3011                         memmove(&view->line[pos + 1], &view->line[pos],
3012                                 (view->lines - pos) * sizeof(*line));
3013
3014                 line = &view->line[pos];
3015                 line->data = text;
3016                 line->type = type;
3017                 view->lines++;
3018                 return TRUE;
3019         }
3020
3021         if (!add_line_text(view, text, type))
3022                 return FALSE;
3023
3024         if (tree_lineno > view->lineno) {
3025                 view->lineno = tree_lineno;
3026                 tree_lineno = 0;
3027         }
3028
3029         return TRUE;
3030 }
3031
3032 static enum request
3033 tree_request(struct view *view, enum request request, struct line *line)
3034 {
3035         enum open_flags flags;
3036
3037         if (request != REQ_ENTER)
3038                 return request;
3039
3040         /* Cleanup the stack if the tree view is at a different tree. */
3041         while (!*opt_path && tree_stack)
3042                 pop_tree_stack_entry();
3043
3044         switch (line->type) {
3045         case LINE_TREE_DIR:
3046                 /* Depending on whether it is a subdir or parent (updir?) link
3047                  * mangle the path buffer. */
3048                 if (line == &view->line[1] && *opt_path) {
3049                         pop_tree_stack_entry();
3050
3051                 } else {
3052                         char *data = line->data;
3053                         char *basename = data + SIZEOF_TREE_ATTR;
3054
3055                         push_tree_stack_entry(basename, view->lineno);
3056                 }
3057
3058                 /* Trees and subtrees share the same ID, so they are not not
3059                  * unique like blobs. */
3060                 flags = OPEN_RELOAD;
3061                 request = REQ_VIEW_TREE;
3062                 break;
3063
3064         case LINE_TREE_FILE:
3065                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3066                 request = REQ_VIEW_BLOB;
3067                 break;
3068
3069         default:
3070                 return TRUE;
3071         }
3072
3073         open_view(view, request, flags);
3074         if (request == REQ_VIEW_TREE) {
3075                 view->lineno = tree_lineno;
3076         }
3077
3078         return REQ_NONE;
3079 }
3080
3081 static void
3082 tree_select(struct view *view, struct line *line)
3083 {
3084         char *text = line->data + STRING_SIZE("100644 blob ");
3085
3086         if (line->type == LINE_TREE_FILE) {
3087                 string_copy_rev(ref_blob, text);
3088
3089         } else if (line->type != LINE_TREE_DIR) {
3090                 return;
3091         }
3092
3093         string_copy_rev(view->ref, text);
3094 }
3095
3096 static struct view_ops tree_ops = {
3097         "file",
3098         NULL,
3099         tree_read,
3100         pager_draw,
3101         tree_request,
3102         pager_grep,
3103         tree_select,
3104 };
3105
3106 static bool
3107 blob_read(struct view *view, char *line)
3108 {
3109         return add_line_text(view, line, LINE_DEFAULT) != NULL;
3110 }
3111
3112 static struct view_ops blob_ops = {
3113         "line",
3114         NULL,
3115         blob_read,
3116         pager_draw,
3117         pager_request,
3118         pager_grep,
3119         pager_select,
3120 };
3121
3122
3123 /*
3124  * Status backend
3125  */
3126
3127 struct status {
3128         char status;
3129         struct {
3130                 mode_t mode;
3131                 char rev[SIZEOF_REV];
3132         } old;
3133         struct {
3134                 mode_t mode;
3135                 char rev[SIZEOF_REV];
3136         } new;
3137         char name[SIZEOF_STR];
3138 };
3139
3140 static struct status stage_status;
3141 static enum line_type stage_line_type;
3142
3143 /* Get fields from the diff line:
3144  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
3145  */
3146 static inline bool
3147 status_get_diff(struct status *file, char *buf, size_t bufsize)
3148 {
3149         char *old_mode = buf +  1;
3150         char *new_mode = buf +  8;
3151         char *old_rev  = buf + 15;
3152         char *new_rev  = buf + 56;
3153         char *status   = buf + 97;
3154
3155         if (bufsize != 99 ||
3156             old_mode[-1] != ':' ||
3157             new_mode[-1] != ' ' ||
3158             old_rev[-1]  != ' ' ||
3159             new_rev[-1]  != ' ' ||
3160             status[-1]   != ' ')
3161                 return FALSE;
3162
3163         file->status = *status;
3164
3165         string_copy_rev(file->old.rev, old_rev);
3166         string_copy_rev(file->new.rev, new_rev);
3167
3168         file->old.mode = strtoul(old_mode, NULL, 8);
3169         file->new.mode = strtoul(new_mode, NULL, 8);
3170
3171         file->name[0] = 0;
3172
3173         return TRUE;
3174 }
3175
3176 static bool
3177 status_run(struct view *view, const char cmd[], bool diff, enum line_type type)
3178 {
3179         struct status *file = NULL;
3180         struct status *unmerged = NULL;
3181         char buf[SIZEOF_STR * 4];
3182         size_t bufsize = 0;
3183         FILE *pipe;
3184
3185         pipe = popen(cmd, "r");
3186         if (!pipe)
3187                 return FALSE;
3188
3189         add_line_data(view, NULL, type);
3190
3191         while (!feof(pipe) && !ferror(pipe)) {
3192                 char *sep;
3193                 size_t readsize;
3194
3195                 readsize = fread(buf + bufsize, 1, sizeof(buf) - bufsize, pipe);
3196                 if (!readsize)
3197                         break;
3198                 bufsize += readsize;
3199
3200                 /* Process while we have NUL chars. */
3201                 while ((sep = memchr(buf, 0, bufsize))) {
3202                         size_t sepsize = sep - buf + 1;
3203
3204                         if (!file) {
3205                                 if (!realloc_lines(view, view->line_size + 1))
3206                                         goto error_out;
3207
3208                                 file = calloc(1, sizeof(*file));
3209                                 if (!file)
3210                                         goto error_out;
3211
3212                                 add_line_data(view, file, type);
3213                         }
3214
3215                         /* Parse diff info part. */
3216                         if (!diff) {
3217                                 file->status = '?';
3218
3219                         } else if (!file->status) {
3220                                 if (!status_get_diff(file, buf, sepsize))
3221                                         goto error_out;
3222
3223                                 bufsize -= sepsize;
3224                                 memmove(buf, sep + 1, bufsize);
3225
3226                                 sep = memchr(buf, 0, bufsize);
3227                                 if (!sep)
3228                                         break;
3229                                 sepsize = sep - buf + 1;
3230
3231                                 /* Collapse all 'M'odified entries that
3232                                  * follow a associated 'U'nmerged entry.
3233                                  */
3234                                 if (file->status == 'U') {
3235                                         unmerged = file;
3236
3237                                 } else if (unmerged) {
3238                                         int collapse = !strcmp(buf, unmerged->name);
3239
3240                                         unmerged = NULL;
3241                                         if (collapse) {
3242                                                 free(file);
3243                                                 view->lines--;
3244                                                 continue;
3245                                         }
3246                                 }
3247                         }
3248
3249                         /* git-ls-files just delivers a NUL separated
3250                          * list of file names similar to the second half
3251                          * of the git-diff-* output. */
3252                         string_ncopy(file->name, buf, sepsize);
3253                         bufsize -= sepsize;
3254                         memmove(buf, sep + 1, bufsize);
3255                         file = NULL;
3256                 }
3257         }
3258
3259         if (ferror(pipe)) {
3260 error_out:
3261                 pclose(pipe);
3262                 return FALSE;
3263         }
3264
3265         if (!view->line[view->lines - 1].data)
3266                 add_line_data(view, NULL, LINE_STAT_NONE);
3267
3268         pclose(pipe);
3269         return TRUE;
3270 }
3271
3272 /* Don't show unmerged entries in the staged section. */
3273 #define STATUS_DIFF_INDEX_CMD "git diff-index -z --diff-filter=ACDMRTXB --cached HEAD"
3274 #define STATUS_DIFF_FILES_CMD "git diff-files -z"
3275 #define STATUS_LIST_OTHER_CMD \
3276         "git ls-files -z --others --exclude-per-directory=.gitignore"
3277
3278 #define STATUS_DIFF_SHOW_CMD \
3279         "git diff --root --patch-with-stat --find-copies-harder -B -C %s -- %s 2>/dev/null"
3280
3281 /* First parse staged info using git-diff-index(1), then parse unstaged
3282  * info using git-diff-files(1), and finally untracked files using
3283  * git-ls-files(1). */
3284 static bool
3285 status_open(struct view *view)
3286 {
3287         struct stat statbuf;
3288         char exclude[SIZEOF_STR];
3289         char cmd[SIZEOF_STR];
3290         unsigned long prev_lineno = view->lineno;
3291         size_t i;
3292
3293         for (i = 0; i < view->lines; i++)
3294                 free(view->line[i].data);
3295         free(view->line);
3296         view->lines = view->line_size = view->lineno = 0;
3297         view->line = NULL;
3298
3299         if (!realloc_lines(view, view->line_size + 6))
3300                 return FALSE;
3301
3302         if (!string_format(exclude, "%s/info/exclude", opt_git_dir))
3303                 return FALSE;
3304
3305         string_copy(cmd, STATUS_LIST_OTHER_CMD);
3306
3307         if (stat(exclude, &statbuf) >= 0) {
3308                 size_t cmdsize = strlen(cmd);
3309
3310                 if (!string_format_from(cmd, &cmdsize, " %s", "--exclude-from=") ||
3311                     sq_quote(cmd, cmdsize, exclude) >= sizeof(cmd))
3312                         return FALSE;
3313         }
3314
3315         if (!status_run(view, STATUS_DIFF_INDEX_CMD, TRUE, LINE_STAT_STAGED) ||
3316             !status_run(view, STATUS_DIFF_FILES_CMD, TRUE, LINE_STAT_UNSTAGED) ||
3317             !status_run(view, cmd, FALSE, LINE_STAT_UNTRACKED))
3318                 return FALSE;
3319
3320         /* If all went well restore the previous line number to stay in
3321          * the context. */
3322         if (prev_lineno < view->lines)
3323                 view->lineno = prev_lineno;
3324         else
3325                 view->lineno = view->lines - 1;
3326
3327         return TRUE;
3328 }
3329
3330 static bool
3331 status_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
3332 {
3333         struct status *status = line->data;
3334
3335         wmove(view->win, lineno, 0);
3336
3337         if (selected) {
3338                 wattrset(view->win, get_line_attr(LINE_CURSOR));
3339                 wchgat(view->win, -1, 0, LINE_CURSOR, NULL);
3340
3341         } else if (!status && line->type != LINE_STAT_NONE) {
3342                 wattrset(view->win, get_line_attr(LINE_STAT_SECTION));
3343                 wchgat(view->win, -1, 0, LINE_STAT_SECTION, NULL);
3344
3345         } else {
3346                 wattrset(view->win, get_line_attr(line->type));
3347         }
3348
3349         if (!status) {
3350                 char *text;
3351
3352                 switch (line->type) {
3353                 case LINE_STAT_STAGED:
3354                         text = "Changes to be committed:";
3355                         break;
3356
3357                 case LINE_STAT_UNSTAGED:
3358                         text = "Changed but not updated:";
3359                         break;
3360
3361                 case LINE_STAT_UNTRACKED:
3362                         text = "Untracked files:";
3363                         break;
3364
3365                 case LINE_STAT_NONE:
3366                         text = "    (no files)";
3367                         break;
3368
3369                 default:
3370                         return FALSE;
3371                 }
3372
3373                 waddstr(view->win, text);
3374                 return TRUE;
3375         }
3376
3377         waddch(view->win, status->status);
3378         if (!selected)
3379                 wattrset(view->win, A_NORMAL);
3380         wmove(view->win, lineno, 4);
3381         waddstr(view->win, status->name);
3382
3383         return TRUE;
3384 }
3385
3386 static enum request
3387 status_enter(struct view *view, struct line *line)
3388 {
3389         struct status *status = line->data;
3390         char path[SIZEOF_STR] = "";
3391         char *info;
3392         size_t cmdsize = 0;
3393
3394         if (line->type == LINE_STAT_NONE ||
3395             (!status && line[1].type == LINE_STAT_NONE)) {
3396                 report("No file to diff");
3397                 return REQ_NONE;
3398         }
3399
3400         if (status && sq_quote(path, 0, status->name) >= sizeof(path))
3401                 return REQ_QUIT;
3402
3403         if (opt_cdup[0] &&
3404             line->type != LINE_STAT_UNTRACKED &&
3405             !string_format_from(opt_cmd, &cmdsize, "cd %s;", opt_cdup))
3406                 return REQ_QUIT;
3407
3408         switch (line->type) {
3409         case LINE_STAT_STAGED:
3410                 if (!string_format_from(opt_cmd, &cmdsize, STATUS_DIFF_SHOW_CMD,
3411                                         "--cached", path))
3412                         return REQ_QUIT;
3413                 if (status)
3414                         info = "Staged changes to %s";
3415                 else
3416                         info = "Staged changes";
3417                 break;
3418
3419         case LINE_STAT_UNSTAGED:
3420                 if (!string_format_from(opt_cmd, &cmdsize, STATUS_DIFF_SHOW_CMD,
3421                                         "", path))
3422                         return REQ_QUIT;
3423                 if (status)
3424                         info = "Unstaged changes to %s";
3425                 else
3426                         info = "Unstaged changes";
3427                 break;
3428
3429         case LINE_STAT_UNTRACKED:
3430                 if (opt_pipe)
3431                         return REQ_QUIT;
3432
3433
3434                 if (!status) {
3435                         report("No file to show");
3436                         return REQ_NONE;
3437                 }
3438
3439                 opt_pipe = fopen(status->name, "r");
3440                 info = "Untracked file %s";
3441                 break;
3442
3443         default:
3444                 die("line type %d not handled in switch", line->type);
3445         }
3446
3447         open_view(view, REQ_VIEW_STAGE, OPEN_RELOAD | OPEN_SPLIT);
3448         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
3449                 if (status) {
3450                         stage_status = *status;
3451                 } else {
3452                         memset(&stage_status, 0, sizeof(stage_status));
3453                 }
3454
3455                 stage_line_type = line->type;
3456                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.name);
3457         }
3458
3459         return REQ_NONE;
3460 }
3461
3462
3463 static bool
3464 status_update_file(struct view *view, struct status *status, enum line_type type)
3465 {
3466         char cmd[SIZEOF_STR];
3467         char buf[SIZEOF_STR];
3468         size_t cmdsize = 0;
3469         size_t bufsize = 0;
3470         size_t written = 0;
3471         FILE *pipe;
3472
3473         if (opt_cdup[0] &&
3474             type != LINE_STAT_UNTRACKED &&
3475             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
3476                 return FALSE;
3477
3478         switch (type) {
3479         case LINE_STAT_STAGED:
3480                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
3481                                         status->old.mode,
3482                                         status->old.rev,
3483                                         status->name, 0))
3484                         return FALSE;
3485
3486                 string_add(cmd, cmdsize, "git update-index -z --index-info");
3487                 break;
3488
3489         case LINE_STAT_UNSTAGED:
3490         case LINE_STAT_UNTRACKED:
3491                 if (!string_format_from(buf, &bufsize, "%s%c", status->name, 0))
3492                         return FALSE;
3493
3494                 string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin");
3495                 break;
3496
3497         default:
3498                 die("line type %d not handled in switch", type);
3499         }
3500
3501         pipe = popen(cmd, "w");
3502         if (!pipe)
3503                 return FALSE;
3504
3505         while (!ferror(pipe) && written < bufsize) {
3506                 written += fwrite(buf + written, 1, bufsize - written, pipe);
3507         }
3508
3509         pclose(pipe);
3510
3511         if (written != bufsize)
3512                 return FALSE;
3513
3514         return TRUE;
3515 }
3516
3517 static void
3518 status_update(struct view *view)
3519 {
3520         struct line *line = &view->line[view->lineno];
3521
3522         assert(view->lines);
3523
3524         if (!line->data) {
3525                 while (++line < view->line + view->lines && line->data) {
3526                         if (!status_update_file(view, line->data, line->type))
3527                                 report("Failed to update file status");
3528                 }
3529
3530                 if (!line[-1].data) {
3531                         report("Nothing to update");
3532                         return;
3533                 }
3534
3535         } else if (!status_update_file(view, line->data, line->type)) {
3536                 report("Failed to update file status");
3537         }
3538 }
3539
3540 static enum request
3541 status_request(struct view *view, enum request request, struct line *line)
3542 {
3543         struct status *status = line->data;
3544
3545         switch (request) {
3546         case REQ_STATUS_UPDATE:
3547                 status_update(view);
3548                 break;
3549
3550         case REQ_STATUS_MERGE:
3551                 open_mergetool(status->name);
3552                 break;
3553
3554         case REQ_EDIT:
3555                 if (!status)
3556                         return request;
3557
3558                 open_editor(status->status != '?', status->name);
3559                 break;
3560
3561         case REQ_ENTER:
3562                 /* After returning the status view has been split to
3563                  * show the stage view. No further reloading is
3564                  * necessary. */
3565                 status_enter(view, line);
3566                 return REQ_NONE;
3567
3568         case REQ_REFRESH:
3569                 /* Simply reload the view. */
3570                 break;
3571
3572         default:
3573                 return request;
3574         }
3575
3576         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
3577
3578         return REQ_NONE;
3579 }
3580
3581 static void
3582 status_select(struct view *view, struct line *line)
3583 {
3584         struct status *status = line->data;
3585         char file[SIZEOF_STR] = "all files";
3586         char *text;
3587         char *key;
3588
3589         if (status && !string_format(file, "'%s'", status->name))
3590                 return;
3591
3592         if (!status && line[1].type == LINE_STAT_NONE)
3593                 line++;
3594
3595         switch (line->type) {
3596         case LINE_STAT_STAGED:
3597                 text = "Press %s to unstage %s for commit";
3598                 break;
3599
3600         case LINE_STAT_UNSTAGED:
3601                 text = "Press %s to stage %s for commit";
3602                 break;
3603
3604         case LINE_STAT_UNTRACKED:
3605                 text = "Press %s to stage %s for addition";
3606                 break;
3607
3608         case LINE_STAT_NONE:
3609                 text = "Nothing to update";
3610                 break;
3611
3612         default:
3613                 die("line type %d not handled in switch", line->type);
3614         }
3615
3616         if (status && status->status == 'U') {
3617                 text = "Press %s to resolve conflict in %s";
3618                 key = get_key(REQ_STATUS_MERGE);
3619
3620         } else {
3621                 key = get_key(REQ_STATUS_UPDATE);
3622         }
3623
3624         string_format(view->ref, text, key, file);
3625 }
3626
3627 static bool
3628 status_grep(struct view *view, struct line *line)
3629 {
3630         struct status *status = line->data;
3631         enum { S_STATUS, S_NAME, S_END } state;
3632         char buf[2] = "?";
3633         regmatch_t pmatch;
3634
3635         if (!status)
3636                 return FALSE;
3637
3638         for (state = S_STATUS; state < S_END; state++) {
3639                 char *text;
3640
3641                 switch (state) {
3642                 case S_NAME:    text = status->name;    break;
3643                 case S_STATUS:
3644                         buf[0] = status->status;
3645                         text = buf;
3646                         break;
3647
3648                 default:
3649                         return FALSE;
3650                 }
3651
3652                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
3653                         return TRUE;
3654         }
3655
3656         return FALSE;
3657 }
3658
3659 static struct view_ops status_ops = {
3660         "file",
3661         status_open,
3662         NULL,
3663         status_draw,
3664         status_request,
3665         status_grep,
3666         status_select,
3667 };
3668
3669
3670 static bool
3671 stage_diff_line(FILE *pipe, struct line *line)
3672 {
3673         char *buf = line->data;
3674         size_t bufsize = strlen(buf);
3675         size_t written = 0;
3676
3677         while (!ferror(pipe) && written < bufsize) {
3678                 written += fwrite(buf + written, 1, bufsize - written, pipe);
3679         }
3680
3681         fputc('\n', pipe);
3682
3683         return written == bufsize;
3684 }
3685
3686 static struct line *
3687 stage_diff_hdr(struct view *view, struct line *line)
3688 {
3689         int diff_hdr_dir = line->type == LINE_DIFF_CHUNK ? -1 : 1;
3690         struct line *diff_hdr;
3691
3692         if (line->type == LINE_DIFF_CHUNK)
3693                 diff_hdr = line - 1;
3694         else
3695                 diff_hdr = view->line + 1;
3696
3697         while (diff_hdr > view->line && diff_hdr < view->line + view->lines) {
3698                 if (diff_hdr->type == LINE_DIFF_HEADER)
3699                         return diff_hdr;
3700
3701                 diff_hdr += diff_hdr_dir;
3702         }
3703
3704         return NULL;
3705 }
3706
3707 static bool
3708 stage_update_chunk(struct view *view, struct line *line)
3709 {
3710         char cmd[SIZEOF_STR];
3711         size_t cmdsize = 0;
3712         struct line *diff_hdr, *diff_chunk, *diff_end;
3713         FILE *pipe;
3714
3715         diff_hdr = stage_diff_hdr(view, line);
3716         if (!diff_hdr)
3717                 return FALSE;
3718
3719         if (opt_cdup[0] &&
3720             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
3721                 return FALSE;
3722
3723         if (!string_format_from(cmd, &cmdsize,
3724                                 "git apply --cached %s - && "
3725                                 "git update-index -q --unmerged --refresh 2>/dev/null",
3726                                 stage_line_type == LINE_STAT_STAGED ? "-R" : ""))
3727                 return FALSE;
3728
3729         pipe = popen(cmd, "w");
3730         if (!pipe)
3731                 return FALSE;
3732
3733         diff_end = view->line + view->lines;
3734         if (line->type != LINE_DIFF_CHUNK) {
3735                 diff_chunk = diff_hdr;
3736
3737         } else {
3738                 for (diff_chunk = line + 1; diff_chunk < diff_end; diff_chunk++)
3739                         if (diff_chunk->type == LINE_DIFF_CHUNK ||
3740                             diff_chunk->type == LINE_DIFF_HEADER)
3741                                 diff_end = diff_chunk;
3742
3743                 diff_chunk = line;
3744
3745                 while (diff_hdr->type != LINE_DIFF_CHUNK) {
3746                         switch (diff_hdr->type) {
3747                         case LINE_DIFF_HEADER:
3748                         case LINE_DIFF_INDEX:
3749                         case LINE_DIFF_ADD:
3750                         case LINE_DIFF_DEL:
3751                                 break;
3752
3753                         default:
3754                                 diff_hdr++;
3755                                 continue;
3756                         }
3757
3758                         if (!stage_diff_line(pipe, diff_hdr++)) {
3759                                 pclose(pipe);
3760                                 return FALSE;
3761                         }
3762                 }
3763         }
3764
3765         while (diff_chunk < diff_end && stage_diff_line(pipe, diff_chunk))
3766                 diff_chunk++;
3767
3768         pclose(pipe);
3769
3770         if (diff_chunk != diff_end)
3771                 return FALSE;
3772
3773         return TRUE;
3774 }
3775
3776 static void
3777 stage_update(struct view *view, struct line *line)
3778 {
3779         if (stage_line_type != LINE_STAT_UNTRACKED &&
3780             (line->type == LINE_DIFF_CHUNK || !stage_status.status)) {
3781                 if (!stage_update_chunk(view, line)) {
3782                         report("Failed to apply chunk");
3783                         return;
3784                 }
3785
3786         } else if (!status_update_file(view, &stage_status, stage_line_type)) {
3787                 report("Failed to update file");
3788                 return;
3789         }
3790
3791         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
3792
3793         view = VIEW(REQ_VIEW_STATUS);
3794         if (view_is_displayed(view))
3795                 status_enter(view, &view->line[view->lineno]);
3796 }
3797
3798 static enum request
3799 stage_request(struct view *view, enum request request, struct line *line)
3800 {
3801         switch (request) {
3802         case REQ_STATUS_UPDATE:
3803                 stage_update(view, line);
3804                 break;
3805
3806         case REQ_EDIT:
3807                 if (!stage_status.name[0])
3808                         return request;
3809
3810                 open_editor(stage_status.status != '?', stage_status.name);
3811                 break;
3812
3813         case REQ_ENTER:
3814                 pager_request(view, request, line);
3815                 break;
3816
3817         default:
3818                 return request;
3819         }
3820
3821         return REQ_NONE;
3822 }
3823
3824 static struct view_ops stage_ops = {
3825         "line",
3826         NULL,
3827         pager_read,
3828         pager_draw,
3829         stage_request,
3830         pager_grep,
3831         pager_select,
3832 };
3833
3834
3835 /*
3836  * Revision graph
3837  */
3838
3839 struct commit {
3840         char id[SIZEOF_REV];            /* SHA1 ID. */
3841         char title[128];                /* First line of the commit message. */
3842         char author[75];                /* Author of the commit. */
3843         struct tm time;                 /* Date from the author ident. */
3844         struct ref **refs;              /* Repository references. */
3845         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
3846         size_t graph_size;              /* The width of the graph array. */
3847 };
3848
3849 /* Size of rev graph with no  "padding" columns */
3850 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
3851
3852 struct rev_graph {
3853         struct rev_graph *prev, *next, *parents;
3854         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
3855         size_t size;
3856         struct commit *commit;
3857         size_t pos;
3858 };
3859
3860 /* Parents of the commit being visualized. */
3861 static struct rev_graph graph_parents[4];
3862
3863 /* The current stack of revisions on the graph. */
3864 static struct rev_graph graph_stacks[4] = {
3865         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
3866         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
3867         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
3868         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
3869 };
3870
3871 static inline bool
3872 graph_parent_is_merge(struct rev_graph *graph)
3873 {
3874         return graph->parents->size > 1;
3875 }
3876
3877 static inline void
3878 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
3879 {
3880         struct commit *commit = graph->commit;
3881
3882         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
3883                 commit->graph[commit->graph_size++] = symbol;
3884 }
3885
3886 static void
3887 done_rev_graph(struct rev_graph *graph)
3888 {
3889         if (graph_parent_is_merge(graph) &&
3890             graph->pos < graph->size - 1 &&
3891             graph->next->size == graph->size + graph->parents->size - 1) {
3892                 size_t i = graph->pos + graph->parents->size - 1;
3893
3894                 graph->commit->graph_size = i * 2;
3895                 while (i < graph->next->size - 1) {
3896                         append_to_rev_graph(graph, ' ');
3897                         append_to_rev_graph(graph, '\\');
3898                         i++;
3899                 }
3900         }
3901
3902         graph->size = graph->pos = 0;
3903         graph->commit = NULL;
3904         memset(graph->parents, 0, sizeof(*graph->parents));
3905 }
3906
3907 static void
3908 push_rev_graph(struct rev_graph *graph, char *parent)
3909 {
3910         int i;
3911
3912         /* "Collapse" duplicate parents lines.
3913          *
3914          * FIXME: This needs to also update update the drawn graph but
3915          * for now it just serves as a method for pruning graph lines. */
3916         for (i = 0; i < graph->size; i++)
3917                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
3918                         return;
3919
3920         if (graph->size < SIZEOF_REVITEMS) {
3921                 string_copy_rev(graph->rev[graph->size++], parent);
3922         }
3923 }
3924
3925 static chtype
3926 get_rev_graph_symbol(struct rev_graph *graph)
3927 {
3928         chtype symbol;
3929
3930         if (graph->parents->size == 0)
3931                 symbol = REVGRAPH_INIT;
3932         else if (graph_parent_is_merge(graph))
3933                 symbol = REVGRAPH_MERGE;
3934         else if (graph->pos >= graph->size)
3935                 symbol = REVGRAPH_BRANCH;
3936         else
3937                 symbol = REVGRAPH_COMMIT;
3938
3939         return symbol;
3940 }
3941
3942 static void
3943 draw_rev_graph(struct rev_graph *graph)
3944 {
3945         struct rev_filler {
3946                 chtype separator, line;
3947         };
3948         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
3949         static struct rev_filler fillers[] = {
3950                 { ' ',  REVGRAPH_LINE },
3951                 { '`',  '.' },
3952                 { '\'', ' ' },
3953                 { '/',  ' ' },
3954         };
3955         chtype symbol = get_rev_graph_symbol(graph);
3956         struct rev_filler *filler;
3957         size_t i;
3958
3959         filler = &fillers[DEFAULT];
3960
3961         for (i = 0; i < graph->pos; i++) {
3962                 append_to_rev_graph(graph, filler->line);
3963                 if (graph_parent_is_merge(graph->prev) &&
3964                     graph->prev->pos == i)
3965                         filler = &fillers[RSHARP];
3966
3967                 append_to_rev_graph(graph, filler->separator);
3968         }
3969
3970         /* Place the symbol for this revision. */
3971         append_to_rev_graph(graph, symbol);
3972
3973         if (graph->prev->size > graph->size)
3974                 filler = &fillers[RDIAG];
3975         else
3976                 filler = &fillers[DEFAULT];
3977
3978         i++;
3979
3980         for (; i < graph->size; i++) {
3981                 append_to_rev_graph(graph, filler->separator);
3982                 append_to_rev_graph(graph, filler->line);
3983                 if (graph_parent_is_merge(graph->prev) &&
3984                     i < graph->prev->pos + graph->parents->size)
3985                         filler = &fillers[RSHARP];
3986                 if (graph->prev->size > graph->size)
3987                         filler = &fillers[LDIAG];
3988         }
3989
3990         if (graph->prev->size > graph->size) {
3991                 append_to_rev_graph(graph, filler->separator);
3992                 if (filler->line != ' ')
3993                         append_to_rev_graph(graph, filler->line);
3994         }
3995 }
3996
3997 /* Prepare the next rev graph */
3998 static void
3999 prepare_rev_graph(struct rev_graph *graph)
4000 {
4001         size_t i;
4002
4003         /* First, traverse all lines of revisions up to the active one. */
4004         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
4005                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
4006                         break;
4007
4008                 push_rev_graph(graph->next, graph->rev[graph->pos]);
4009         }
4010
4011         /* Interleave the new revision parent(s). */
4012         for (i = 0; i < graph->parents->size; i++)
4013                 push_rev_graph(graph->next, graph->parents->rev[i]);
4014
4015         /* Lastly, put any remaining revisions. */
4016         for (i = graph->pos + 1; i < graph->size; i++)
4017                 push_rev_graph(graph->next, graph->rev[i]);
4018 }
4019
4020 static void
4021 update_rev_graph(struct rev_graph *graph)
4022 {
4023         /* If this is the finalizing update ... */
4024         if (graph->commit)
4025                 prepare_rev_graph(graph);
4026
4027         /* Graph visualization needs a one rev look-ahead,
4028          * so the first update doesn't visualize anything. */
4029         if (!graph->prev->commit)
4030                 return;
4031
4032         draw_rev_graph(graph->prev);
4033         done_rev_graph(graph->prev->prev);
4034 }
4035
4036
4037 /*
4038  * Main view backend
4039  */
4040
4041 static bool
4042 main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
4043 {
4044         char buf[DATE_COLS + 1];
4045         struct commit *commit = line->data;
4046         enum line_type type;
4047         int col = 0;
4048         size_t timelen;
4049         size_t authorlen;
4050         int trimmed = 1;
4051
4052         if (!*commit->author)
4053                 return FALSE;
4054
4055         wmove(view->win, lineno, col);
4056
4057         if (selected) {
4058                 type = LINE_CURSOR;
4059                 wattrset(view->win, get_line_attr(type));
4060                 wchgat(view->win, -1, 0, type, NULL);
4061
4062         } else {
4063                 type = LINE_MAIN_COMMIT;
4064                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
4065         }
4066
4067         timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
4068         waddnstr(view->win, buf, timelen);
4069         waddstr(view->win, " ");
4070
4071         col += DATE_COLS;
4072         wmove(view->win, lineno, col);
4073         if (type != LINE_CURSOR)
4074                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
4075
4076         if (opt_utf8) {
4077                 authorlen = utf8_length(commit->author, AUTHOR_COLS - 2, &col, &trimmed);
4078         } else {
4079                 authorlen = strlen(commit->author);
4080                 if (authorlen > AUTHOR_COLS - 2) {
4081                         authorlen = AUTHOR_COLS - 2;
4082                         trimmed = 1;
4083                 }
4084         }
4085
4086         if (trimmed) {
4087                 waddnstr(view->win, commit->author, authorlen);
4088                 if (type != LINE_CURSOR)
4089                         wattrset(view->win, get_line_attr(LINE_MAIN_DELIM));
4090                 waddch(view->win, '~');
4091         } else {
4092                 waddstr(view->win, commit->author);
4093         }
4094
4095         col += AUTHOR_COLS;
4096         if (type != LINE_CURSOR)
4097                 wattrset(view->win, A_NORMAL);
4098
4099         if (opt_rev_graph && commit->graph_size) {
4100                 size_t i;
4101
4102                 wmove(view->win, lineno, col);
4103                 /* Using waddch() instead of waddnstr() ensures that
4104                  * they'll be rendered correctly for the cursor line. */
4105                 for (i = 0; i < commit->graph_size; i++)
4106                         waddch(view->win, commit->graph[i]);
4107
4108                 waddch(view->win, ' ');
4109                 col += commit->graph_size + 1;
4110         }
4111
4112         wmove(view->win, lineno, col);
4113
4114         if (commit->refs) {
4115                 size_t i = 0;
4116
4117                 do {
4118                         if (type == LINE_CURSOR)
4119                                 ;
4120                         else if (commit->refs[i]->tag)
4121                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
4122                         else if (commit->refs[i]->remote)
4123                                 wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
4124                         else
4125                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
4126                         waddstr(view->win, "[");
4127                         waddstr(view->win, commit->refs[i]->name);
4128                         waddstr(view->win, "]");
4129                         if (type != LINE_CURSOR)
4130                                 wattrset(view->win, A_NORMAL);
4131                         waddstr(view->win, " ");
4132                         col += strlen(commit->refs[i]->name) + STRING_SIZE("[] ");
4133                 } while (commit->refs[i++]->next);
4134         }
4135
4136         if (type != LINE_CURSOR)
4137                 wattrset(view->win, get_line_attr(type));
4138
4139         {
4140                 int titlelen = strlen(commit->title);
4141
4142                 if (col + titlelen > view->width)
4143                         titlelen = view->width - col;
4144
4145                 waddnstr(view->win, commit->title, titlelen);
4146         }
4147
4148         return TRUE;
4149 }
4150
4151 /* Reads git log --pretty=raw output and parses it into the commit struct. */
4152 static bool
4153 main_read(struct view *view, char *line)
4154 {
4155         static struct rev_graph *graph = graph_stacks;
4156         enum line_type type;
4157         struct commit *commit;
4158
4159         if (!line) {
4160                 update_rev_graph(graph);
4161                 return TRUE;
4162         }
4163
4164         type = get_line_type(line);
4165         if (type == LINE_COMMIT) {
4166                 commit = calloc(1, sizeof(struct commit));
4167                 if (!commit)
4168                         return FALSE;
4169
4170                 string_copy_rev(commit->id, line + STRING_SIZE("commit "));
4171                 commit->refs = get_refs(commit->id);
4172                 graph->commit = commit;
4173                 add_line_data(view, commit, LINE_MAIN_COMMIT);
4174                 return TRUE;
4175         }
4176
4177         if (!view->lines)
4178                 return TRUE;
4179         commit = view->line[view->lines - 1].data;
4180
4181         switch (type) {
4182         case LINE_PARENT:
4183                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
4184                 break;
4185
4186         case LINE_AUTHOR:
4187         {
4188                 /* Parse author lines where the name may be empty:
4189                  *      author  <email@address.tld> 1138474660 +0100
4190                  */
4191                 char *ident = line + STRING_SIZE("author ");
4192                 char *nameend = strchr(ident, '<');
4193                 char *emailend = strchr(ident, '>');
4194
4195                 if (!nameend || !emailend)
4196                         break;
4197
4198                 update_rev_graph(graph);
4199                 graph = graph->next;
4200
4201                 *nameend = *emailend = 0;
4202                 ident = chomp_string(ident);
4203                 if (!*ident) {
4204                         ident = chomp_string(nameend + 1);
4205                         if (!*ident)
4206                                 ident = "Unknown";
4207                 }
4208
4209                 string_ncopy(commit->author, ident, strlen(ident));
4210
4211                 /* Parse epoch and timezone */
4212                 if (emailend[1] == ' ') {
4213                         char *secs = emailend + 2;
4214                         char *zone = strchr(secs, ' ');
4215                         time_t time = (time_t) atol(secs);
4216
4217                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
4218                                 long tz;
4219
4220                                 zone++;
4221                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
4222                                 tz += ('0' - zone[2]) * 60 * 60;
4223                                 tz += ('0' - zone[3]) * 60;
4224                                 tz += ('0' - zone[4]) * 60;
4225
4226                                 if (zone[0] == '-')
4227                                         tz = -tz;
4228
4229                                 time -= tz;
4230                         }
4231
4232                         gmtime_r(&time, &commit->time);
4233                 }
4234                 break;
4235         }
4236         default:
4237                 /* Fill in the commit title if it has not already been set. */
4238                 if (commit->title[0])
4239                         break;
4240
4241                 /* Require titles to start with a non-space character at the
4242                  * offset used by git log. */
4243                 if (strncmp(line, "    ", 4))
4244                         break;
4245                 line += 4;
4246                 /* Well, if the title starts with a whitespace character,
4247                  * try to be forgiving.  Otherwise we end up with no title. */
4248                 while (isspace(*line))
4249                         line++;
4250                 if (*line == '\0')
4251                         break;
4252                 /* FIXME: More graceful handling of titles; append "..." to
4253                  * shortened titles, etc. */
4254
4255                 string_ncopy(commit->title, line, strlen(line));
4256         }
4257
4258         return TRUE;
4259 }
4260
4261 static enum request
4262 main_request(struct view *view, enum request request, struct line *line)
4263 {
4264         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4265
4266         if (request == REQ_ENTER)
4267                 open_view(view, REQ_VIEW_DIFF, flags);
4268         else
4269                 return request;
4270
4271         return REQ_NONE;
4272 }
4273
4274 static bool
4275 main_grep(struct view *view, struct line *line)
4276 {
4277         struct commit *commit = line->data;
4278         enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
4279         char buf[DATE_COLS + 1];
4280         regmatch_t pmatch;
4281
4282         for (state = S_TITLE; state < S_END; state++) {
4283                 char *text;
4284
4285                 switch (state) {
4286                 case S_TITLE:   text = commit->title;   break;
4287                 case S_AUTHOR:  text = commit->author;  break;
4288                 case S_DATE:
4289                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
4290                                 continue;
4291                         text = buf;
4292                         break;
4293
4294                 default:
4295                         return FALSE;
4296                 }
4297
4298                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4299                         return TRUE;
4300         }
4301
4302         return FALSE;
4303 }
4304
4305 static void
4306 main_select(struct view *view, struct line *line)
4307 {
4308         struct commit *commit = line->data;
4309
4310         string_copy_rev(view->ref, commit->id);
4311         string_copy_rev(ref_commit, view->ref);
4312 }
4313
4314 static struct view_ops main_ops = {
4315         "commit",
4316         NULL,
4317         main_read,
4318         main_draw,
4319         main_request,
4320         main_grep,
4321         main_select,
4322 };
4323
4324
4325 /*
4326  * Unicode / UTF-8 handling
4327  *
4328  * NOTE: Much of the following code for dealing with unicode is derived from
4329  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
4330  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
4331  */
4332
4333 /* I've (over)annotated a lot of code snippets because I am not entirely
4334  * confident that the approach taken by this small UTF-8 interface is correct.
4335  * --jonas */
4336
4337 static inline int
4338 unicode_width(unsigned long c)
4339 {
4340         if (c >= 0x1100 &&
4341            (c <= 0x115f                         /* Hangul Jamo */
4342             || c == 0x2329
4343             || c == 0x232a
4344             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
4345                                                 /* CJK ... Yi */
4346             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
4347             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
4348             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
4349             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
4350             || (c >= 0xffe0  && c <= 0xffe6)
4351             || (c >= 0x20000 && c <= 0x2fffd)
4352             || (c >= 0x30000 && c <= 0x3fffd)))
4353                 return 2;
4354
4355         return 1;
4356 }
4357
4358 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
4359  * Illegal bytes are set one. */
4360 static const unsigned char utf8_bytes[256] = {
4361         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,
4362         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,
4363         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,
4364         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,
4365         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,
4366         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,
4367         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,
4368         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,
4369 };
4370
4371 /* Decode UTF-8 multi-byte representation into a unicode character. */
4372 static inline unsigned long
4373 utf8_to_unicode(const char *string, size_t length)
4374 {
4375         unsigned long unicode;
4376
4377         switch (length) {
4378         case 1:
4379                 unicode  =   string[0];
4380                 break;
4381         case 2:
4382                 unicode  =  (string[0] & 0x1f) << 6;
4383                 unicode +=  (string[1] & 0x3f);
4384                 break;
4385         case 3:
4386                 unicode  =  (string[0] & 0x0f) << 12;
4387                 unicode += ((string[1] & 0x3f) << 6);
4388                 unicode +=  (string[2] & 0x3f);
4389                 break;
4390         case 4:
4391                 unicode  =  (string[0] & 0x0f) << 18;
4392                 unicode += ((string[1] & 0x3f) << 12);
4393                 unicode += ((string[2] & 0x3f) << 6);
4394                 unicode +=  (string[3] & 0x3f);
4395                 break;
4396         case 5:
4397                 unicode  =  (string[0] & 0x0f) << 24;
4398                 unicode += ((string[1] & 0x3f) << 18);
4399                 unicode += ((string[2] & 0x3f) << 12);
4400                 unicode += ((string[3] & 0x3f) << 6);
4401                 unicode +=  (string[4] & 0x3f);
4402                 break;
4403         case 6:
4404                 unicode  =  (string[0] & 0x01) << 30;
4405                 unicode += ((string[1] & 0x3f) << 24);
4406                 unicode += ((string[2] & 0x3f) << 18);
4407                 unicode += ((string[3] & 0x3f) << 12);
4408                 unicode += ((string[4] & 0x3f) << 6);
4409                 unicode +=  (string[5] & 0x3f);
4410                 break;
4411         default:
4412                 die("Invalid unicode length");
4413         }
4414
4415         /* Invalid characters could return the special 0xfffd value but NUL
4416          * should be just as good. */
4417         return unicode > 0xffff ? 0 : unicode;
4418 }
4419
4420 /* Calculates how much of string can be shown within the given maximum width
4421  * and sets trimmed parameter to non-zero value if all of string could not be
4422  * shown.
4423  *
4424  * Additionally, adds to coloffset how many many columns to move to align with
4425  * the expected position. Takes into account how multi-byte and double-width
4426  * characters will effect the cursor position.
4427  *
4428  * Returns the number of bytes to output from string to satisfy max_width. */
4429 static size_t
4430 utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed)
4431 {
4432         const char *start = string;
4433         const char *end = strchr(string, '\0');
4434         size_t mbwidth = 0;
4435         size_t width = 0;
4436
4437         *trimmed = 0;
4438
4439         while (string < end) {
4440                 int c = *(unsigned char *) string;
4441                 unsigned char bytes = utf8_bytes[c];
4442                 size_t ucwidth;
4443                 unsigned long unicode;
4444
4445                 if (string + bytes > end)
4446                         break;
4447
4448                 /* Change representation to figure out whether
4449                  * it is a single- or double-width character. */
4450
4451                 unicode = utf8_to_unicode(string, bytes);
4452                 /* FIXME: Graceful handling of invalid unicode character. */
4453                 if (!unicode)
4454                         break;
4455
4456                 ucwidth = unicode_width(unicode);
4457                 width  += ucwidth;
4458                 if (width > max_width) {
4459                         *trimmed = 1;
4460                         break;
4461                 }
4462
4463                 /* The column offset collects the differences between the
4464                  * number of bytes encoding a character and the number of
4465                  * columns will be used for rendering said character.
4466                  *
4467                  * So if some character A is encoded in 2 bytes, but will be
4468                  * represented on the screen using only 1 byte this will and up
4469                  * adding 1 to the multi-byte column offset.
4470                  *
4471                  * Assumes that no double-width character can be encoding in
4472                  * less than two bytes. */
4473                 if (bytes > ucwidth)
4474                         mbwidth += bytes - ucwidth;
4475
4476                 string  += bytes;
4477         }
4478
4479         *coloffset += mbwidth;
4480
4481         return string - start;
4482 }
4483
4484
4485 /*
4486  * Status management
4487  */
4488
4489 /* Whether or not the curses interface has been initialized. */
4490 static bool cursed = FALSE;
4491
4492 /* The status window is used for polling keystrokes. */
4493 static WINDOW *status_win;
4494
4495 static bool status_empty = TRUE;
4496
4497 /* Update status and title window. */
4498 static void
4499 report(const char *msg, ...)
4500 {
4501         struct view *view = display[current_view];
4502
4503         if (input_mode)
4504                 return;
4505
4506         if (!view) {
4507                 char buf[SIZEOF_STR];
4508                 va_list args;
4509
4510                 va_start(args, msg);
4511                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
4512                         buf[sizeof(buf) - 1] = 0;
4513                         buf[sizeof(buf) - 2] = '.';
4514                         buf[sizeof(buf) - 3] = '.';
4515                         buf[sizeof(buf) - 4] = '.';
4516                 }
4517                 va_end(args);
4518                 die("%s", buf);
4519         }
4520
4521         if (!status_empty || *msg) {
4522                 va_list args;
4523
4524                 va_start(args, msg);
4525
4526                 wmove(status_win, 0, 0);
4527                 if (*msg) {
4528                         vwprintw(status_win, msg, args);
4529                         status_empty = FALSE;
4530                 } else {
4531                         status_empty = TRUE;
4532                 }
4533                 wclrtoeol(status_win);
4534                 wrefresh(status_win);
4535
4536                 va_end(args);
4537         }
4538
4539         update_view_title(view);
4540         update_display_cursor(view);
4541 }
4542
4543 /* Controls when nodelay should be in effect when polling user input. */
4544 static void
4545 set_nonblocking_input(bool loading)
4546 {
4547         static unsigned int loading_views;
4548
4549         if ((loading == FALSE && loading_views-- == 1) ||
4550             (loading == TRUE  && loading_views++ == 0))
4551                 nodelay(status_win, loading);
4552 }
4553
4554 static void
4555 init_display(void)
4556 {
4557         int x, y;
4558
4559         /* Initialize the curses library */
4560         if (isatty(STDIN_FILENO)) {
4561                 cursed = !!initscr();
4562         } else {
4563                 /* Leave stdin and stdout alone when acting as a pager. */
4564                 FILE *io = fopen("/dev/tty", "r+");
4565
4566                 if (!io)
4567                         die("Failed to open /dev/tty");
4568                 cursed = !!newterm(NULL, io, io);
4569         }
4570
4571         if (!cursed)
4572                 die("Failed to initialize curses");
4573
4574         nonl();         /* Tell curses not to do NL->CR/NL on output */
4575         cbreak();       /* Take input chars one at a time, no wait for \n */
4576         noecho();       /* Don't echo input */
4577         leaveok(stdscr, TRUE);
4578
4579         if (has_colors())
4580                 init_colors();
4581
4582         getmaxyx(stdscr, y, x);
4583         status_win = newwin(1, 0, y - 1, 0);
4584         if (!status_win)
4585                 die("Failed to create status window");
4586
4587         /* Enable keyboard mapping */
4588         keypad(status_win, TRUE);
4589         wbkgdset(status_win, get_line_attr(LINE_STATUS));
4590 }
4591
4592 static char *
4593 read_prompt(const char *prompt)
4594 {
4595         enum { READING, STOP, CANCEL } status = READING;
4596         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
4597         int pos = 0;
4598
4599         while (status == READING) {
4600                 struct view *view;
4601                 int i, key;
4602
4603                 input_mode = TRUE;
4604
4605                 foreach_view (view, i)
4606                         update_view(view);
4607
4608                 input_mode = FALSE;
4609
4610                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
4611                 wclrtoeol(status_win);
4612
4613                 /* Refresh, accept single keystroke of input */
4614                 key = wgetch(status_win);
4615                 switch (key) {
4616                 case KEY_RETURN:
4617                 case KEY_ENTER:
4618                 case '\n':
4619                         status = pos ? STOP : CANCEL;
4620                         break;
4621
4622                 case KEY_BACKSPACE:
4623                         if (pos > 0)
4624                                 pos--;
4625                         else
4626                                 status = CANCEL;
4627                         break;
4628
4629                 case KEY_ESC:
4630                         status = CANCEL;
4631                         break;
4632
4633                 case ERR:
4634                         break;
4635
4636                 default:
4637                         if (pos >= sizeof(buf)) {
4638                                 report("Input string too long");
4639                                 return NULL;
4640                         }
4641
4642                         if (isprint(key))
4643                                 buf[pos++] = (char) key;
4644                 }
4645         }
4646
4647         /* Clear the status window */
4648         status_empty = FALSE;
4649         report("");
4650
4651         if (status == CANCEL)
4652                 return NULL;
4653
4654         buf[pos++] = 0;
4655
4656         return buf;
4657 }
4658
4659 /*
4660  * Repository references
4661  */
4662
4663 static struct ref *refs;
4664 static size_t refs_size;
4665
4666 /* Id <-> ref store */
4667 static struct ref ***id_refs;
4668 static size_t id_refs_size;
4669
4670 static struct ref **
4671 get_refs(char *id)
4672 {
4673         struct ref ***tmp_id_refs;
4674         struct ref **ref_list = NULL;
4675         size_t ref_list_size = 0;
4676         size_t i;
4677
4678         for (i = 0; i < id_refs_size; i++)
4679                 if (!strcmp(id, id_refs[i][0]->id))
4680                         return id_refs[i];
4681
4682         tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
4683         if (!tmp_id_refs)
4684                 return NULL;
4685
4686         id_refs = tmp_id_refs;
4687
4688         for (i = 0; i < refs_size; i++) {
4689                 struct ref **tmp;
4690
4691                 if (strcmp(id, refs[i].id))
4692                         continue;
4693
4694                 tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
4695                 if (!tmp) {
4696                         if (ref_list)
4697                                 free(ref_list);
4698                         return NULL;
4699                 }
4700
4701                 ref_list = tmp;
4702                 if (ref_list_size > 0)
4703                         ref_list[ref_list_size - 1]->next = 1;
4704                 ref_list[ref_list_size] = &refs[i];
4705
4706                 /* XXX: The properties of the commit chains ensures that we can
4707                  * safely modify the shared ref. The repo references will
4708                  * always be similar for the same id. */
4709                 ref_list[ref_list_size]->next = 0;
4710                 ref_list_size++;
4711         }
4712
4713         if (ref_list)
4714                 id_refs[id_refs_size++] = ref_list;
4715
4716         return ref_list;
4717 }
4718
4719 static int
4720 read_ref(char *id, size_t idlen, char *name, size_t namelen)
4721 {
4722         struct ref *ref;
4723         bool tag = FALSE;
4724         bool remote = FALSE;
4725
4726         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
4727                 /* Commits referenced by tags has "^{}" appended. */
4728                 if (name[namelen - 1] != '}')
4729                         return OK;
4730
4731                 while (namelen > 0 && name[namelen] != '^')
4732                         namelen--;
4733
4734                 tag = TRUE;
4735                 namelen -= STRING_SIZE("refs/tags/");
4736                 name    += STRING_SIZE("refs/tags/");
4737
4738         } else if (!strncmp(name, "refs/remotes/", STRING_SIZE("refs/remotes/"))) {
4739                 remote = TRUE;
4740                 namelen -= STRING_SIZE("refs/remotes/");
4741                 name    += STRING_SIZE("refs/remotes/");
4742
4743         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
4744                 namelen -= STRING_SIZE("refs/heads/");
4745                 name    += STRING_SIZE("refs/heads/");
4746
4747         } else if (!strcmp(name, "HEAD")) {
4748                 return OK;
4749         }
4750
4751         refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
4752         if (!refs)
4753                 return ERR;
4754
4755         ref = &refs[refs_size++];
4756         ref->name = malloc(namelen + 1);
4757         if (!ref->name)
4758                 return ERR;
4759
4760         strncpy(ref->name, name, namelen);
4761         ref->name[namelen] = 0;
4762         ref->tag = tag;
4763         ref->remote = remote;
4764         string_copy_rev(ref->id, id);
4765
4766         return OK;
4767 }
4768
4769 static int
4770 load_refs(void)
4771 {
4772         const char *cmd_env = getenv("TIG_LS_REMOTE");
4773         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
4774
4775         return read_properties(popen(cmd, "r"), "\t", read_ref);
4776 }
4777
4778 static int
4779 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
4780 {
4781         if (!strcmp(name, "i18n.commitencoding"))
4782                 string_ncopy(opt_encoding, value, valuelen);
4783
4784         if (!strcmp(name, "core.editor"))
4785                 string_ncopy(opt_editor, value, valuelen);
4786
4787         return OK;
4788 }
4789
4790 static int
4791 load_repo_config(void)
4792 {
4793         return read_properties(popen(GIT_CONFIG " --list", "r"),
4794                                "=", read_repo_config_option);
4795 }
4796
4797 static int
4798 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
4799 {
4800         if (!opt_git_dir[0]) {
4801                 string_ncopy(opt_git_dir, name, namelen);
4802
4803         } else if (opt_is_inside_work_tree == -1) {
4804                 /* This can be 3 different values depending on the
4805                  * version of git being used. If git-rev-parse does not
4806                  * understand --is-inside-work-tree it will simply echo
4807                  * the option else either "true" or "false" is printed.
4808                  * Default to true for the unknown case. */
4809                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
4810
4811         } else {
4812                 string_ncopy(opt_cdup, name, namelen);
4813         }
4814
4815         return OK;
4816 }
4817
4818 /* XXX: The line outputted by "--show-cdup" can be empty so the option
4819  * must be the last one! */
4820 static int
4821 load_repo_info(void)
4822 {
4823         return read_properties(popen("git rev-parse --git-dir --is-inside-work-tree --show-cdup 2>/dev/null", "r"),
4824                                "=", read_repo_info);
4825 }
4826
4827 static int
4828 read_properties(FILE *pipe, const char *separators,
4829                 int (*read_property)(char *, size_t, char *, size_t))
4830 {
4831         char buffer[BUFSIZ];
4832         char *name;
4833         int state = OK;
4834
4835         if (!pipe)
4836                 return ERR;
4837
4838         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
4839                 char *value;
4840                 size_t namelen;
4841                 size_t valuelen;
4842
4843                 name = chomp_string(name);
4844                 namelen = strcspn(name, separators);
4845
4846                 if (name[namelen]) {
4847                         name[namelen] = 0;
4848                         value = chomp_string(name + namelen + 1);
4849                         valuelen = strlen(value);
4850
4851                 } else {
4852                         value = "";
4853                         valuelen = 0;
4854                 }
4855
4856                 state = read_property(name, namelen, value, valuelen);
4857         }
4858
4859         if (state != ERR && ferror(pipe))
4860                 state = ERR;
4861
4862         pclose(pipe);
4863
4864         return state;
4865 }
4866
4867
4868 /*
4869  * Main
4870  */
4871
4872 static void __NORETURN
4873 quit(int sig)
4874 {
4875         /* XXX: Restore tty modes and let the OS cleanup the rest! */
4876         if (cursed)
4877                 endwin();
4878         exit(0);
4879 }
4880
4881 static void __NORETURN
4882 die(const char *err, ...)
4883 {
4884         va_list args;
4885
4886         endwin();
4887
4888         va_start(args, err);
4889         fputs("tig: ", stderr);
4890         vfprintf(stderr, err, args);
4891         fputs("\n", stderr);
4892         va_end(args);
4893
4894         exit(1);
4895 }
4896
4897 int
4898 main(int argc, char *argv[])
4899 {
4900         struct view *view;
4901         enum request request;
4902         size_t i;
4903
4904         signal(SIGINT, quit);
4905
4906         if (setlocale(LC_ALL, "")) {
4907                 char *codeset = nl_langinfo(CODESET);
4908
4909                 string_ncopy(opt_codeset, codeset, strlen(codeset));
4910         }
4911
4912         if (load_repo_info() == ERR)
4913                 die("Failed to load repo info.");
4914
4915         if (load_options() == ERR)
4916                 die("Failed to load user config.");
4917
4918         /* Load the repo config file so options can be overwritten from
4919          * the command line. */
4920         if (load_repo_config() == ERR)
4921                 die("Failed to load repo config.");
4922
4923         if (!parse_options(argc, argv))
4924                 return 0;
4925
4926         /* Require a git repository unless when running in pager mode. */
4927         if (!opt_git_dir[0])
4928                 die("Not a git repository");
4929
4930         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
4931                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
4932                 if (opt_iconv == ICONV_NONE)
4933                         die("Failed to initialize character set conversion");
4934         }
4935
4936         if (load_refs() == ERR)
4937                 die("Failed to load refs.");
4938
4939         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
4940                 view->cmd_env = getenv(view->cmd_env);
4941
4942         request = opt_request;
4943
4944         init_display();
4945
4946         while (view_driver(display[current_view], request)) {
4947                 int key;
4948                 int i;
4949
4950                 foreach_view (view, i)
4951                         update_view(view);
4952
4953                 /* Refresh, accept single keystroke of input */
4954                 key = wgetch(status_win);
4955
4956                 /* wgetch() with nodelay() enabled returns ERR when there's no
4957                  * input. */
4958                 if (key == ERR) {
4959                         request = REQ_NONE;
4960                         continue;
4961                 }
4962
4963                 request = get_keybinding(display[current_view]->keymap, key);
4964
4965                 /* Some low-level request handling. This keeps access to
4966                  * status_win restricted. */
4967                 switch (request) {
4968                 case REQ_PROMPT:
4969                 {
4970                         char *cmd = read_prompt(":");
4971
4972                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
4973                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
4974                                         opt_request = REQ_VIEW_DIFF;
4975                                 } else {
4976                                         opt_request = REQ_VIEW_PAGER;
4977                                 }
4978                                 break;
4979                         }
4980
4981                         request = REQ_NONE;
4982                         break;
4983                 }
4984                 case REQ_SEARCH:
4985                 case REQ_SEARCH_BACK:
4986                 {
4987                         const char *prompt = request == REQ_SEARCH
4988                                            ? "/" : "?";
4989                         char *search = read_prompt(prompt);
4990
4991                         if (search)
4992                                 string_ncopy(opt_search, search, strlen(search));
4993                         else
4994                                 request = REQ_NONE;
4995                         break;
4996                 }
4997                 case REQ_SCREEN_RESIZE:
4998                 {
4999                         int height, width;
5000
5001                         getmaxyx(stdscr, height, width);
5002
5003                         /* Resize the status view and let the view driver take
5004                          * care of resizing the displayed views. */
5005                         wresize(status_win, 1, width);
5006                         mvwin(status_win, height - 1, 0);
5007                         wrefresh(status_win);
5008                         break;
5009                 }
5010                 default:
5011                         break;
5012                 }
5013         }
5014
5015         quit(0);
5016
5017         return 0;
5018 }