Collect remaining string in last entry when parsing config file lines
[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                 value += valuelen;
1223
1224                 /* Nothing more to tokenize or last available token. */
1225                 if (!*value || argc >= ARRAY_SIZE(argv))
1226                         break;
1227
1228                 *value++ = 0;
1229                 while (isspace(*value))
1230                         value++;
1231         }
1232
1233         if (!strcmp(opt, "color"))
1234                 return option_color_command(argc, argv);
1235
1236         if (!strcmp(opt, "set"))
1237                 return option_set_command(argc, argv);
1238
1239         if (!strcmp(opt, "bind"))
1240                 return option_bind_command(argc, argv);
1241
1242         config_msg = "Unknown option command";
1243         return ERR;
1244 }
1245
1246 static int
1247 read_option(char *opt, size_t optlen, char *value, size_t valuelen)
1248 {
1249         int status = OK;
1250
1251         config_lineno++;
1252         config_msg = "Internal error";
1253
1254         /* Check for comment markers, since read_properties() will
1255          * only ensure opt and value are split at first " \t". */
1256         optlen = strcspn(opt, "#");
1257         if (optlen == 0)
1258                 return OK;
1259
1260         if (opt[optlen] != 0) {
1261                 config_msg = "No option value";
1262                 status = ERR;
1263
1264         }  else {
1265                 /* Look for comment endings in the value. */
1266                 size_t len = strcspn(value, "#");
1267
1268                 if (len < valuelen) {
1269                         valuelen = len;
1270                         value[valuelen] = 0;
1271                 }
1272
1273                 status = set_option(opt, value);
1274         }
1275
1276         if (status == ERR) {
1277                 fprintf(stderr, "Error on line %d, near '%.*s': %s\n",
1278                         config_lineno, (int) optlen, opt, config_msg);
1279                 config_errors = TRUE;
1280         }
1281
1282         /* Always keep going if errors are encountered. */
1283         return OK;
1284 }
1285
1286 static int
1287 load_options(void)
1288 {
1289         char *home = getenv("HOME");
1290         char buf[SIZEOF_STR];
1291         FILE *file;
1292
1293         config_lineno = 0;
1294         config_errors = FALSE;
1295
1296         add_builtin_run_requests();
1297
1298         if (!home || !string_format(buf, "%s/.tigrc", home))
1299                 return ERR;
1300
1301         /* It's ok that the file doesn't exist. */
1302         file = fopen(buf, "r");
1303         if (!file)
1304                 return OK;
1305
1306         if (read_properties(file, " \t", read_option) == ERR ||
1307             config_errors == TRUE)
1308                 fprintf(stderr, "Errors while loading %s.\n", buf);
1309
1310         return OK;
1311 }
1312
1313
1314 /*
1315  * The viewer
1316  */
1317
1318 struct view;
1319 struct view_ops;
1320
1321 /* The display array of active views and the index of the current view. */
1322 static struct view *display[2];
1323 static unsigned int current_view;
1324
1325 /* Reading from the prompt? */
1326 static bool input_mode = FALSE;
1327
1328 #define foreach_displayed_view(view, i) \
1329         for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
1330
1331 #define displayed_views()       (display[1] != NULL ? 2 : 1)
1332
1333 /* Current head and commit ID */
1334 static char ref_blob[SIZEOF_REF]        = "";
1335 static char ref_commit[SIZEOF_REF]      = "HEAD";
1336 static char ref_head[SIZEOF_REF]        = "HEAD";
1337
1338 struct view {
1339         const char *name;       /* View name */
1340         const char *cmd_fmt;    /* Default command line format */
1341         const char *cmd_env;    /* Command line set via environment */
1342         const char *id;         /* Points to either of ref_{head,commit,blob} */
1343
1344         struct view_ops *ops;   /* View operations */
1345
1346         enum keymap keymap;     /* What keymap does this view have */
1347
1348         char cmd[SIZEOF_STR];   /* Command buffer */
1349         char ref[SIZEOF_REF];   /* Hovered commit reference */
1350         char vid[SIZEOF_REF];   /* View ID. Set to id member when updating. */
1351
1352         int height, width;      /* The width and height of the main window */
1353         WINDOW *win;            /* The main window */
1354         WINDOW *title;          /* The title window living below the main window */
1355
1356         /* Navigation */
1357         unsigned long offset;   /* Offset of the window top */
1358         unsigned long lineno;   /* Current line number */
1359
1360         /* Searching */
1361         char grep[SIZEOF_STR];  /* Search string */
1362         regex_t *regex;         /* Pre-compiled regex */
1363
1364         /* If non-NULL, points to the view that opened this view. If this view
1365          * is closed tig will switch back to the parent view. */
1366         struct view *parent;
1367
1368         /* Buffering */
1369         unsigned long lines;    /* Total number of lines */
1370         struct line *line;      /* Line index */
1371         unsigned long line_size;/* Total number of allocated lines */
1372         unsigned int digits;    /* Number of digits in the lines member. */
1373
1374         /* Loading */
1375         FILE *pipe;
1376         time_t start_time;
1377 };
1378
1379 struct view_ops {
1380         /* What type of content being displayed. Used in the title bar. */
1381         const char *type;
1382         /* Open and reads in all view content. */
1383         bool (*open)(struct view *view);
1384         /* Read one line; updates view->line. */
1385         bool (*read)(struct view *view, char *data);
1386         /* Draw one line; @lineno must be < view->height. */
1387         bool (*draw)(struct view *view, struct line *line, unsigned int lineno, bool selected);
1388         /* Depending on view handle a special requests. */
1389         enum request (*request)(struct view *view, enum request request, struct line *line);
1390         /* Search for regex in a line. */
1391         bool (*grep)(struct view *view, struct line *line);
1392         /* Select line */
1393         void (*select)(struct view *view, struct line *line);
1394 };
1395
1396 static struct view_ops pager_ops;
1397 static struct view_ops main_ops;
1398 static struct view_ops tree_ops;
1399 static struct view_ops blob_ops;
1400 static struct view_ops help_ops;
1401 static struct view_ops status_ops;
1402 static struct view_ops stage_ops;
1403
1404 #define VIEW_STR(name, cmd, env, ref, ops, map) \
1405         { name, cmd, #env, ref, ops, map}
1406
1407 #define VIEW_(id, name, ops, ref) \
1408         VIEW_STR(name, TIG_##id##_CMD,  TIG_##id##_CMD, ref, ops, KEYMAP_##id)
1409
1410
1411 static struct view views[] = {
1412         VIEW_(MAIN,   "main",   &main_ops,   ref_head),
1413         VIEW_(DIFF,   "diff",   &pager_ops,  ref_commit),
1414         VIEW_(LOG,    "log",    &pager_ops,  ref_head),
1415         VIEW_(TREE,   "tree",   &tree_ops,   ref_commit),
1416         VIEW_(BLOB,   "blob",   &blob_ops,   ref_blob),
1417         VIEW_(HELP,   "help",   &help_ops,   ""),
1418         VIEW_(PAGER,  "pager",  &pager_ops,  "stdin"),
1419         VIEW_(STATUS, "status", &status_ops, ""),
1420         VIEW_(STAGE,  "stage",  &stage_ops,  ""),
1421 };
1422
1423 #define VIEW(req) (&views[(req) - REQ_OFFSET - 1])
1424
1425 #define foreach_view(view, i) \
1426         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
1427
1428 #define view_is_displayed(view) \
1429         (view == display[0] || view == display[1])
1430
1431 static bool
1432 draw_view_line(struct view *view, unsigned int lineno)
1433 {
1434         struct line *line;
1435         bool selected = (view->offset + lineno == view->lineno);
1436         bool draw_ok;
1437
1438         assert(view_is_displayed(view));
1439
1440         if (view->offset + lineno >= view->lines)
1441                 return FALSE;
1442
1443         line = &view->line[view->offset + lineno];
1444
1445         if (selected) {
1446                 line->selected = TRUE;
1447                 view->ops->select(view, line);
1448         } else if (line->selected) {
1449                 line->selected = FALSE;
1450                 wmove(view->win, lineno, 0);
1451                 wclrtoeol(view->win);
1452         }
1453
1454         scrollok(view->win, FALSE);
1455         draw_ok = view->ops->draw(view, line, lineno, selected);
1456         scrollok(view->win, TRUE);
1457
1458         return draw_ok;
1459 }
1460
1461 static void
1462 redraw_view_from(struct view *view, int lineno)
1463 {
1464         assert(0 <= lineno && lineno < view->height);
1465
1466         for (; lineno < view->height; lineno++) {
1467                 if (!draw_view_line(view, lineno))
1468                         break;
1469         }
1470
1471         redrawwin(view->win);
1472         if (input_mode)
1473                 wnoutrefresh(view->win);
1474         else
1475                 wrefresh(view->win);
1476 }
1477
1478 static void
1479 redraw_view(struct view *view)
1480 {
1481         wclear(view->win);
1482         redraw_view_from(view, 0);
1483 }
1484
1485
1486 static void
1487 update_view_title(struct view *view)
1488 {
1489         char buf[SIZEOF_STR];
1490         char state[SIZEOF_STR];
1491         size_t bufpos = 0, statelen = 0;
1492
1493         assert(view_is_displayed(view));
1494
1495         if (view != VIEW(REQ_VIEW_STATUS) && (view->lines || view->pipe)) {
1496                 unsigned int view_lines = view->offset + view->height;
1497                 unsigned int lines = view->lines
1498                                    ? MIN(view_lines, view->lines) * 100 / view->lines
1499                                    : 0;
1500
1501                 string_format_from(state, &statelen, "- %s %d of %d (%d%%)",
1502                                    view->ops->type,
1503                                    view->lineno + 1,
1504                                    view->lines,
1505                                    lines);
1506
1507                 if (view->pipe) {
1508                         time_t secs = time(NULL) - view->start_time;
1509
1510                         /* Three git seconds are a long time ... */
1511                         if (secs > 2)
1512                                 string_format_from(state, &statelen, " %lds", secs);
1513                 }
1514         }
1515
1516         string_format_from(buf, &bufpos, "[%s]", view->name);
1517         if (*view->ref && bufpos < view->width) {
1518                 size_t refsize = strlen(view->ref);
1519                 size_t minsize = bufpos + 1 + /* abbrev= */ 7 + 1 + statelen;
1520
1521                 if (minsize < view->width)
1522                         refsize = view->width - minsize + 7;
1523                 string_format_from(buf, &bufpos, " %.*s", (int) refsize, view->ref);
1524         }
1525
1526         if (statelen && bufpos < view->width) {
1527                 string_format_from(buf, &bufpos, " %s", state);
1528         }
1529
1530         if (view == display[current_view])
1531                 wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
1532         else
1533                 wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
1534
1535         mvwaddnstr(view->title, 0, 0, buf, bufpos);
1536         wclrtoeol(view->title);
1537         wmove(view->title, 0, view->width - 1);
1538
1539         if (input_mode)
1540                 wnoutrefresh(view->title);
1541         else
1542                 wrefresh(view->title);
1543 }
1544
1545 static void
1546 resize_display(void)
1547 {
1548         int offset, i;
1549         struct view *base = display[0];
1550         struct view *view = display[1] ? display[1] : display[0];
1551
1552         /* Setup window dimensions */
1553
1554         getmaxyx(stdscr, base->height, base->width);
1555
1556         /* Make room for the status window. */
1557         base->height -= 1;
1558
1559         if (view != base) {
1560                 /* Horizontal split. */
1561                 view->width   = base->width;
1562                 view->height  = SCALE_SPLIT_VIEW(base->height);
1563                 base->height -= view->height;
1564
1565                 /* Make room for the title bar. */
1566                 view->height -= 1;
1567         }
1568
1569         /* Make room for the title bar. */
1570         base->height -= 1;
1571
1572         offset = 0;
1573
1574         foreach_displayed_view (view, i) {
1575                 if (!view->win) {
1576                         view->win = newwin(view->height, 0, offset, 0);
1577                         if (!view->win)
1578                                 die("Failed to create %s view", view->name);
1579
1580                         scrollok(view->win, TRUE);
1581
1582                         view->title = newwin(1, 0, offset + view->height, 0);
1583                         if (!view->title)
1584                                 die("Failed to create title window");
1585
1586                 } else {
1587                         wresize(view->win, view->height, view->width);
1588                         mvwin(view->win,   offset, 0);
1589                         mvwin(view->title, offset + view->height, 0);
1590                 }
1591
1592                 offset += view->height + 1;
1593         }
1594 }
1595
1596 static void
1597 redraw_display(void)
1598 {
1599         struct view *view;
1600         int i;
1601
1602         foreach_displayed_view (view, i) {
1603                 redraw_view(view);
1604                 update_view_title(view);
1605         }
1606 }
1607
1608 static void
1609 update_display_cursor(struct view *view)
1610 {
1611         /* Move the cursor to the right-most column of the cursor line.
1612          *
1613          * XXX: This could turn out to be a bit expensive, but it ensures that
1614          * the cursor does not jump around. */
1615         if (view->lines) {
1616                 wmove(view->win, view->lineno - view->offset, view->width - 1);
1617                 wrefresh(view->win);
1618         }
1619 }
1620
1621 /*
1622  * Navigation
1623  */
1624
1625 /* Scrolling backend */
1626 static void
1627 do_scroll_view(struct view *view, int lines)
1628 {
1629         bool redraw_current_line = FALSE;
1630
1631         /* The rendering expects the new offset. */
1632         view->offset += lines;
1633
1634         assert(0 <= view->offset && view->offset < view->lines);
1635         assert(lines);
1636
1637         /* Move current line into the view. */
1638         if (view->lineno < view->offset) {
1639                 view->lineno = view->offset;
1640                 redraw_current_line = TRUE;
1641         } else if (view->lineno >= view->offset + view->height) {
1642                 view->lineno = view->offset + view->height - 1;
1643                 redraw_current_line = TRUE;
1644         }
1645
1646         assert(view->offset <= view->lineno && view->lineno < view->lines);
1647
1648         /* Redraw the whole screen if scrolling is pointless. */
1649         if (view->height < ABS(lines)) {
1650                 redraw_view(view);
1651
1652         } else {
1653                 int line = lines > 0 ? view->height - lines : 0;
1654                 int end = line + ABS(lines);
1655
1656                 wscrl(view->win, lines);
1657
1658                 for (; line < end; line++) {
1659                         if (!draw_view_line(view, line))
1660                                 break;
1661                 }
1662
1663                 if (redraw_current_line)
1664                         draw_view_line(view, view->lineno - view->offset);
1665         }
1666
1667         redrawwin(view->win);
1668         wrefresh(view->win);
1669         report("");
1670 }
1671
1672 /* Scroll frontend */
1673 static void
1674 scroll_view(struct view *view, enum request request)
1675 {
1676         int lines = 1;
1677
1678         assert(view_is_displayed(view));
1679
1680         switch (request) {
1681         case REQ_SCROLL_PAGE_DOWN:
1682                 lines = view->height;
1683         case REQ_SCROLL_LINE_DOWN:
1684                 if (view->offset + lines > view->lines)
1685                         lines = view->lines - view->offset;
1686
1687                 if (lines == 0 || view->offset + view->height >= view->lines) {
1688                         report("Cannot scroll beyond the last line");
1689                         return;
1690                 }
1691                 break;
1692
1693         case REQ_SCROLL_PAGE_UP:
1694                 lines = view->height;
1695         case REQ_SCROLL_LINE_UP:
1696                 if (lines > view->offset)
1697                         lines = view->offset;
1698
1699                 if (lines == 0) {
1700                         report("Cannot scroll beyond the first line");
1701                         return;
1702                 }
1703
1704                 lines = -lines;
1705                 break;
1706
1707         default:
1708                 die("request %d not handled in switch", request);
1709         }
1710
1711         do_scroll_view(view, lines);
1712 }
1713
1714 /* Cursor moving */
1715 static void
1716 move_view(struct view *view, enum request request)
1717 {
1718         int scroll_steps = 0;
1719         int steps;
1720
1721         switch (request) {
1722         case REQ_MOVE_FIRST_LINE:
1723                 steps = -view->lineno;
1724                 break;
1725
1726         case REQ_MOVE_LAST_LINE:
1727                 steps = view->lines - view->lineno - 1;
1728                 break;
1729
1730         case REQ_MOVE_PAGE_UP:
1731                 steps = view->height > view->lineno
1732                       ? -view->lineno : -view->height;
1733                 break;
1734
1735         case REQ_MOVE_PAGE_DOWN:
1736                 steps = view->lineno + view->height >= view->lines
1737                       ? view->lines - view->lineno - 1 : view->height;
1738                 break;
1739
1740         case REQ_MOVE_UP:
1741                 steps = -1;
1742                 break;
1743
1744         case REQ_MOVE_DOWN:
1745                 steps = 1;
1746                 break;
1747
1748         default:
1749                 die("request %d not handled in switch", request);
1750         }
1751
1752         if (steps <= 0 && view->lineno == 0) {
1753                 report("Cannot move beyond the first line");
1754                 return;
1755
1756         } else if (steps >= 0 && view->lineno + 1 >= view->lines) {
1757                 report("Cannot move beyond the last line");
1758                 return;
1759         }
1760
1761         /* Move the current line */
1762         view->lineno += steps;
1763         assert(0 <= view->lineno && view->lineno < view->lines);
1764
1765         /* Check whether the view needs to be scrolled */
1766         if (view->lineno < view->offset ||
1767             view->lineno >= view->offset + view->height) {
1768                 scroll_steps = steps;
1769                 if (steps < 0 && -steps > view->offset) {
1770                         scroll_steps = -view->offset;
1771
1772                 } else if (steps > 0) {
1773                         if (view->lineno == view->lines - 1 &&
1774                             view->lines > view->height) {
1775                                 scroll_steps = view->lines - view->offset - 1;
1776                                 if (scroll_steps >= view->height)
1777                                         scroll_steps -= view->height - 1;
1778                         }
1779                 }
1780         }
1781
1782         if (!view_is_displayed(view)) {
1783                 view->offset += scroll_steps;
1784                 assert(0 <= view->offset && view->offset < view->lines);
1785                 view->ops->select(view, &view->line[view->lineno]);
1786                 return;
1787         }
1788
1789         /* Repaint the old "current" line if we be scrolling */
1790         if (ABS(steps) < view->height)
1791                 draw_view_line(view, view->lineno - steps - view->offset);
1792
1793         if (scroll_steps) {
1794                 do_scroll_view(view, scroll_steps);
1795                 return;
1796         }
1797
1798         /* Draw the current line */
1799         draw_view_line(view, view->lineno - view->offset);
1800
1801         redrawwin(view->win);
1802         wrefresh(view->win);
1803         report("");
1804 }
1805
1806
1807 /*
1808  * Searching
1809  */
1810
1811 static void search_view(struct view *view, enum request request);
1812
1813 static bool
1814 find_next_line(struct view *view, unsigned long lineno, struct line *line)
1815 {
1816         assert(view_is_displayed(view));
1817
1818         if (!view->ops->grep(view, line))
1819                 return FALSE;
1820
1821         if (lineno - view->offset >= view->height) {
1822                 view->offset = lineno;
1823                 view->lineno = lineno;
1824                 redraw_view(view);
1825
1826         } else {
1827                 unsigned long old_lineno = view->lineno - view->offset;
1828
1829                 view->lineno = lineno;
1830                 draw_view_line(view, old_lineno);
1831
1832                 draw_view_line(view, view->lineno - view->offset);
1833                 redrawwin(view->win);
1834                 wrefresh(view->win);
1835         }
1836
1837         report("Line %ld matches '%s'", lineno + 1, view->grep);
1838         return TRUE;
1839 }
1840
1841 static void
1842 find_next(struct view *view, enum request request)
1843 {
1844         unsigned long lineno = view->lineno;
1845         int direction;
1846
1847         if (!*view->grep) {
1848                 if (!*opt_search)
1849                         report("No previous search");
1850                 else
1851                         search_view(view, request);
1852                 return;
1853         }
1854
1855         switch (request) {
1856         case REQ_SEARCH:
1857         case REQ_FIND_NEXT:
1858                 direction = 1;
1859                 break;
1860
1861         case REQ_SEARCH_BACK:
1862         case REQ_FIND_PREV:
1863                 direction = -1;
1864                 break;
1865
1866         default:
1867                 return;
1868         }
1869
1870         if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
1871                 lineno += direction;
1872
1873         /* Note, lineno is unsigned long so will wrap around in which case it
1874          * will become bigger than view->lines. */
1875         for (; lineno < view->lines; lineno += direction) {
1876                 struct line *line = &view->line[lineno];
1877
1878                 if (find_next_line(view, lineno, line))
1879                         return;
1880         }
1881
1882         report("No match found for '%s'", view->grep);
1883 }
1884
1885 static void
1886 search_view(struct view *view, enum request request)
1887 {
1888         int regex_err;
1889
1890         if (view->regex) {
1891                 regfree(view->regex);
1892                 *view->grep = 0;
1893         } else {
1894                 view->regex = calloc(1, sizeof(*view->regex));
1895                 if (!view->regex)
1896                         return;
1897         }
1898
1899         regex_err = regcomp(view->regex, opt_search, REG_EXTENDED);
1900         if (regex_err != 0) {
1901                 char buf[SIZEOF_STR] = "unknown error";
1902
1903                 regerror(regex_err, view->regex, buf, sizeof(buf));
1904                 report("Search failed: %s", buf);
1905                 return;
1906         }
1907
1908         string_copy(view->grep, opt_search);
1909
1910         find_next(view, request);
1911 }
1912
1913 /*
1914  * Incremental updating
1915  */
1916
1917 static void
1918 end_update(struct view *view)
1919 {
1920         if (!view->pipe)
1921                 return;
1922         set_nonblocking_input(FALSE);
1923         if (view->pipe == stdin)
1924                 fclose(view->pipe);
1925         else
1926                 pclose(view->pipe);
1927         view->pipe = NULL;
1928 }
1929
1930 static bool
1931 begin_update(struct view *view)
1932 {
1933         if (view->pipe)
1934                 end_update(view);
1935
1936         if (opt_cmd[0]) {
1937                 string_copy(view->cmd, opt_cmd);
1938                 opt_cmd[0] = 0;
1939                 /* When running random commands, initially show the
1940                  * command in the title. However, it maybe later be
1941                  * overwritten if a commit line is selected. */
1942                 if (view == VIEW(REQ_VIEW_PAGER))
1943                         string_copy(view->ref, view->cmd);
1944                 else
1945                         view->ref[0] = 0;
1946
1947         } else if (view == VIEW(REQ_VIEW_TREE)) {
1948                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1949                 char path[SIZEOF_STR];
1950
1951                 if (strcmp(view->vid, view->id))
1952                         opt_path[0] = path[0] = 0;
1953                 else if (sq_quote(path, 0, opt_path) >= sizeof(path))
1954                         return FALSE;
1955
1956                 if (!string_format(view->cmd, format, view->id, path))
1957                         return FALSE;
1958
1959         } else {
1960                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1961                 const char *id = view->id;
1962
1963                 if (!string_format(view->cmd, format, id, id, id, id, id))
1964                         return FALSE;
1965
1966                 /* Put the current ref_* value to the view title ref
1967                  * member. This is needed by the blob view. Most other
1968                  * views sets it automatically after loading because the
1969                  * first line is a commit line. */
1970                 string_copy_rev(view->ref, view->id);
1971         }
1972
1973         /* Special case for the pager view. */
1974         if (opt_pipe) {
1975                 view->pipe = opt_pipe;
1976                 opt_pipe = NULL;
1977         } else {
1978                 view->pipe = popen(view->cmd, "r");
1979         }
1980
1981         if (!view->pipe)
1982                 return FALSE;
1983
1984         set_nonblocking_input(TRUE);
1985
1986         view->offset = 0;
1987         view->lines  = 0;
1988         view->lineno = 0;
1989         string_copy_rev(view->vid, view->id);
1990
1991         if (view->line) {
1992                 int i;
1993
1994                 for (i = 0; i < view->lines; i++)
1995                         if (view->line[i].data)
1996                                 free(view->line[i].data);
1997
1998                 free(view->line);
1999                 view->line = NULL;
2000         }
2001
2002         view->start_time = time(NULL);
2003
2004         return TRUE;
2005 }
2006
2007 static struct line *
2008 realloc_lines(struct view *view, size_t line_size)
2009 {
2010         struct line *tmp = realloc(view->line, sizeof(*view->line) * line_size);
2011
2012         if (!tmp)
2013                 return NULL;
2014
2015         view->line = tmp;
2016         view->line_size = line_size;
2017         return view->line;
2018 }
2019
2020 static bool
2021 update_view(struct view *view)
2022 {
2023         char in_buffer[BUFSIZ];
2024         char out_buffer[BUFSIZ * 2];
2025         char *line;
2026         /* The number of lines to read. If too low it will cause too much
2027          * redrawing (and possible flickering), if too high responsiveness
2028          * will suffer. */
2029         unsigned long lines = view->height;
2030         int redraw_from = -1;
2031
2032         if (!view->pipe)
2033                 return TRUE;
2034
2035         /* Only redraw if lines are visible. */
2036         if (view->offset + view->height >= view->lines)
2037                 redraw_from = view->lines - view->offset;
2038
2039         /* FIXME: This is probably not perfect for backgrounded views. */
2040         if (!realloc_lines(view, view->lines + lines))
2041                 goto alloc_error;
2042
2043         while ((line = fgets(in_buffer, sizeof(in_buffer), view->pipe))) {
2044                 size_t linelen = strlen(line);
2045
2046                 if (linelen)
2047                         line[linelen - 1] = 0;
2048
2049                 if (opt_iconv != ICONV_NONE) {
2050                         ICONV_CONST char *inbuf = line;
2051                         size_t inlen = linelen;
2052
2053                         char *outbuf = out_buffer;
2054                         size_t outlen = sizeof(out_buffer);
2055
2056                         size_t ret;
2057
2058                         ret = iconv(opt_iconv, &inbuf, &inlen, &outbuf, &outlen);
2059                         if (ret != (size_t) -1) {
2060                                 line = out_buffer;
2061                                 linelen = strlen(out_buffer);
2062                         }
2063                 }
2064
2065                 if (!view->ops->read(view, line))
2066                         goto alloc_error;
2067
2068                 if (lines-- == 1)
2069                         break;
2070         }
2071
2072         {
2073                 int digits;
2074
2075                 lines = view->lines;
2076                 for (digits = 0; lines; digits++)
2077                         lines /= 10;
2078
2079                 /* Keep the displayed view in sync with line number scaling. */
2080                 if (digits != view->digits) {
2081                         view->digits = digits;
2082                         redraw_from = 0;
2083                 }
2084         }
2085
2086         if (!view_is_displayed(view))
2087                 goto check_pipe;
2088
2089         if (view == VIEW(REQ_VIEW_TREE)) {
2090                 /* Clear the view and redraw everything since the tree sorting
2091                  * might have rearranged things. */
2092                 redraw_view(view);
2093
2094         } else if (redraw_from >= 0) {
2095                 /* If this is an incremental update, redraw the previous line
2096                  * since for commits some members could have changed when
2097                  * loading the main view. */
2098                 if (redraw_from > 0)
2099                         redraw_from--;
2100
2101                 /* Since revision graph visualization requires knowledge
2102                  * about the parent commit, it causes a further one-off
2103                  * needed to be redrawn for incremental updates. */
2104                 if (redraw_from > 0 && opt_rev_graph)
2105                         redraw_from--;
2106
2107                 /* Incrementally draw avoids flickering. */
2108                 redraw_view_from(view, redraw_from);
2109         }
2110
2111         /* Update the title _after_ the redraw so that if the redraw picks up a
2112          * commit reference in view->ref it'll be available here. */
2113         update_view_title(view);
2114
2115 check_pipe:
2116         if (ferror(view->pipe)) {
2117                 report("Failed to read: %s", strerror(errno));
2118                 goto end;
2119
2120         } else if (feof(view->pipe)) {
2121                 report("");
2122                 goto end;
2123         }
2124
2125         return TRUE;
2126
2127 alloc_error:
2128         report("Allocation failure");
2129
2130 end:
2131         view->ops->read(view, NULL);
2132         end_update(view);
2133         return FALSE;
2134 }
2135
2136 static struct line *
2137 add_line_data(struct view *view, void *data, enum line_type type)
2138 {
2139         struct line *line = &view->line[view->lines++];
2140
2141         memset(line, 0, sizeof(*line));
2142         line->type = type;
2143         line->data = data;
2144
2145         return line;
2146 }
2147
2148 static struct line *
2149 add_line_text(struct view *view, char *data, enum line_type type)
2150 {
2151         if (data)
2152                 data = strdup(data);
2153
2154         return data ? add_line_data(view, data, type) : NULL;
2155 }
2156
2157
2158 /*
2159  * View opening
2160  */
2161
2162 enum open_flags {
2163         OPEN_DEFAULT = 0,       /* Use default view switching. */
2164         OPEN_SPLIT = 1,         /* Split current view. */
2165         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
2166         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
2167 };
2168
2169 static void
2170 open_view(struct view *prev, enum request request, enum open_flags flags)
2171 {
2172         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
2173         bool split = !!(flags & OPEN_SPLIT);
2174         bool reload = !!(flags & OPEN_RELOAD);
2175         struct view *view = VIEW(request);
2176         int nviews = displayed_views();
2177         struct view *base_view = display[0];
2178
2179         if (view == prev && nviews == 1 && !reload) {
2180                 report("Already in %s view", view->name);
2181                 return;
2182         }
2183
2184         if (view->ops->open) {
2185                 if (!view->ops->open(view)) {
2186                         report("Failed to load %s view", view->name);
2187                         return;
2188                 }
2189
2190         } else if ((reload || strcmp(view->vid, view->id)) &&
2191                    !begin_update(view)) {
2192                 report("Failed to load %s view", view->name);
2193                 return;
2194         }
2195
2196         if (split) {
2197                 display[1] = view;
2198                 if (!backgrounded)
2199                         current_view = 1;
2200         } else {
2201                 /* Maximize the current view. */
2202                 memset(display, 0, sizeof(display));
2203                 current_view = 0;
2204                 display[current_view] = view;
2205         }
2206
2207         /* Resize the view when switching between split- and full-screen,
2208          * or when switching between two different full-screen views. */
2209         if (nviews != displayed_views() ||
2210             (nviews == 1 && base_view != display[0]))
2211                 resize_display();
2212
2213         if (split && prev->lineno - prev->offset >= prev->height) {
2214                 /* Take the title line into account. */
2215                 int lines = prev->lineno - prev->offset - prev->height + 1;
2216
2217                 /* Scroll the view that was split if the current line is
2218                  * outside the new limited view. */
2219                 do_scroll_view(prev, lines);
2220         }
2221
2222         if (prev && view != prev) {
2223                 if (split && !backgrounded) {
2224                         /* "Blur" the previous view. */
2225                         update_view_title(prev);
2226                 }
2227
2228                 view->parent = prev;
2229         }
2230
2231         if (view->pipe && view->lines == 0) {
2232                 /* Clear the old view and let the incremental updating refill
2233                  * the screen. */
2234                 wclear(view->win);
2235                 report("");
2236         } else {
2237                 redraw_view(view);
2238                 report("");
2239         }
2240
2241         /* If the view is backgrounded the above calls to report()
2242          * won't redraw the view title. */
2243         if (backgrounded)
2244                 update_view_title(view);
2245 }
2246
2247 static void
2248 open_external_viewer(const char *cmd)
2249 {
2250         def_prog_mode();           /* save current tty modes */
2251         endwin();                  /* restore original tty modes */
2252         system(cmd);
2253         fprintf(stderr, "Press Enter to continue");
2254         getc(stdin);
2255         reset_prog_mode();
2256         redraw_display();
2257 }
2258
2259 static void
2260 open_mergetool(const char *file)
2261 {
2262         char cmd[SIZEOF_STR];
2263         char file_sq[SIZEOF_STR];
2264
2265         if (sq_quote(file_sq, 0, file) < sizeof(file_sq) &&
2266             string_format(cmd, "git mergetool %s", file_sq)) {
2267                 open_external_viewer(cmd);
2268         }
2269 }
2270
2271 static void
2272 open_editor(bool from_root, const char *file)
2273 {
2274         char cmd[SIZEOF_STR];
2275         char file_sq[SIZEOF_STR];
2276         char *editor;
2277         char *prefix = from_root ? opt_cdup : "";
2278
2279         editor = getenv("GIT_EDITOR");
2280         if (!editor && *opt_editor)
2281                 editor = opt_editor;
2282         if (!editor)
2283                 editor = getenv("VISUAL");
2284         if (!editor)
2285                 editor = getenv("EDITOR");
2286         if (!editor)
2287                 editor = "vi";
2288
2289         if (sq_quote(file_sq, 0, file) < sizeof(file_sq) &&
2290             string_format(cmd, "%s %s%s", editor, prefix, file_sq)) {
2291                 open_external_viewer(cmd);
2292         }
2293 }
2294
2295 static void
2296 open_run_request(enum request request)
2297 {
2298         struct run_request *req = get_run_request(request);
2299         char buf[SIZEOF_STR * 2];
2300         size_t bufpos;
2301         char *cmd;
2302
2303         if (!req) {
2304                 report("Unknown run request");
2305                 return;
2306         }
2307
2308         bufpos = 0;
2309         cmd = req->cmd;
2310
2311         while (cmd) {
2312                 char *next = strstr(cmd, "%(");
2313                 int len = next - cmd;
2314                 char *value;
2315
2316                 if (!next) {
2317                         len = strlen(cmd);
2318                         value = "";
2319
2320                 } else if (!strncmp(next, "%(head)", 7)) {
2321                         value = ref_head;
2322
2323                 } else if (!strncmp(next, "%(commit)", 9)) {
2324                         value = ref_commit;
2325
2326                 } else if (!strncmp(next, "%(blob)", 7)) {
2327                         value = ref_blob;
2328
2329                 } else {
2330                         report("Unknown replacement in run request: `%s`", req->cmd);
2331                         return;
2332                 }
2333
2334                 if (!string_format_from(buf, &bufpos, "%.*s%s", len, cmd, value))
2335                         return;
2336
2337                 if (next)
2338                         next = strchr(next, ')') + 1;
2339                 cmd = next;
2340         }
2341
2342         open_external_viewer(buf);
2343 }
2344
2345 /*
2346  * User request switch noodle
2347  */
2348
2349 static int
2350 view_driver(struct view *view, enum request request)
2351 {
2352         int i;
2353
2354         if (request == REQ_NONE) {
2355                 doupdate();
2356                 return TRUE;
2357         }
2358
2359         if (request > REQ_NONE) {
2360                 open_run_request(request);
2361                 return TRUE;
2362         }
2363
2364         if (view && view->lines) {
2365                 request = view->ops->request(view, request, &view->line[view->lineno]);
2366                 if (request == REQ_NONE)
2367                         return TRUE;
2368         }
2369
2370         switch (request) {
2371         case REQ_MOVE_UP:
2372         case REQ_MOVE_DOWN:
2373         case REQ_MOVE_PAGE_UP:
2374         case REQ_MOVE_PAGE_DOWN:
2375         case REQ_MOVE_FIRST_LINE:
2376         case REQ_MOVE_LAST_LINE:
2377                 move_view(view, request);
2378                 break;
2379
2380         case REQ_SCROLL_LINE_DOWN:
2381         case REQ_SCROLL_LINE_UP:
2382         case REQ_SCROLL_PAGE_DOWN:
2383         case REQ_SCROLL_PAGE_UP:
2384                 scroll_view(view, request);
2385                 break;
2386
2387         case REQ_VIEW_BLOB:
2388                 if (!ref_blob[0]) {
2389                         report("No file chosen, press %s to open tree view",
2390                                get_key(REQ_VIEW_TREE));
2391                         break;
2392                 }
2393                 open_view(view, request, OPEN_DEFAULT);
2394                 break;
2395
2396         case REQ_VIEW_PAGER:
2397                 if (!opt_pipe && !VIEW(REQ_VIEW_PAGER)->lines) {
2398                         report("No pager content, press %s to run command from prompt",
2399                                get_key(REQ_PROMPT));
2400                         break;
2401                 }
2402                 open_view(view, request, OPEN_DEFAULT);
2403                 break;
2404
2405         case REQ_VIEW_STAGE:
2406                 if (!VIEW(REQ_VIEW_STAGE)->lines) {
2407                         report("No stage content, press %s to open the status view and choose file",
2408                                get_key(REQ_VIEW_STATUS));
2409                         break;
2410                 }
2411                 open_view(view, request, OPEN_DEFAULT);
2412                 break;
2413
2414         case REQ_VIEW_STATUS:
2415                 if (opt_is_inside_work_tree == FALSE) {
2416                         report("The status view requires a working tree");
2417                         break;
2418                 }
2419                 open_view(view, request, OPEN_DEFAULT);
2420                 break;
2421
2422         case REQ_VIEW_MAIN:
2423         case REQ_VIEW_DIFF:
2424         case REQ_VIEW_LOG:
2425         case REQ_VIEW_TREE:
2426         case REQ_VIEW_HELP:
2427                 open_view(view, request, OPEN_DEFAULT);
2428                 break;
2429
2430         case REQ_NEXT:
2431         case REQ_PREVIOUS:
2432                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
2433
2434                 if ((view == VIEW(REQ_VIEW_DIFF) &&
2435                      view->parent == VIEW(REQ_VIEW_MAIN)) ||
2436                    (view == VIEW(REQ_VIEW_STAGE) &&
2437                      view->parent == VIEW(REQ_VIEW_STATUS)) ||
2438                    (view == VIEW(REQ_VIEW_BLOB) &&
2439                      view->parent == VIEW(REQ_VIEW_TREE))) {
2440                         int line;
2441
2442                         view = view->parent;
2443                         line = view->lineno;
2444                         move_view(view, request);
2445                         if (view_is_displayed(view))
2446                                 update_view_title(view);
2447                         if (line != view->lineno)
2448                                 view->ops->request(view, REQ_ENTER,
2449                                                    &view->line[view->lineno]);
2450
2451                 } else {
2452                         move_view(view, request);
2453                 }
2454                 break;
2455
2456         case REQ_VIEW_NEXT:
2457         {
2458                 int nviews = displayed_views();
2459                 int next_view = (current_view + 1) % nviews;
2460
2461                 if (next_view == current_view) {
2462                         report("Only one view is displayed");
2463                         break;
2464                 }
2465
2466                 current_view = next_view;
2467                 /* Blur out the title of the previous view. */
2468                 update_view_title(view);
2469                 report("");
2470                 break;
2471         }
2472         case REQ_REFRESH:
2473                 report("Refreshing is not yet supported for the %s view", view->name);
2474                 break;
2475
2476         case REQ_TOGGLE_LINENO:
2477                 opt_line_number = !opt_line_number;
2478                 redraw_display();
2479                 break;
2480
2481         case REQ_TOGGLE_REV_GRAPH:
2482                 opt_rev_graph = !opt_rev_graph;
2483                 redraw_display();
2484                 break;
2485
2486         case REQ_PROMPT:
2487                 /* Always reload^Wrerun commands from the prompt. */
2488                 open_view(view, opt_request, OPEN_RELOAD);
2489                 break;
2490
2491         case REQ_SEARCH:
2492         case REQ_SEARCH_BACK:
2493                 search_view(view, request);
2494                 break;
2495
2496         case REQ_FIND_NEXT:
2497         case REQ_FIND_PREV:
2498                 find_next(view, request);
2499                 break;
2500
2501         case REQ_STOP_LOADING:
2502                 for (i = 0; i < ARRAY_SIZE(views); i++) {
2503                         view = &views[i];
2504                         if (view->pipe)
2505                                 report("Stopped loading the %s view", view->name),
2506                         end_update(view);
2507                 }
2508                 break;
2509
2510         case REQ_SHOW_VERSION:
2511                 report("tig-%s (built %s)", TIG_VERSION, __DATE__);
2512                 return TRUE;
2513
2514         case REQ_SCREEN_RESIZE:
2515                 resize_display();
2516                 /* Fall-through */
2517         case REQ_SCREEN_REDRAW:
2518                 redraw_display();
2519                 break;
2520
2521         case REQ_EDIT:
2522                 report("Nothing to edit");
2523                 break;
2524
2525
2526         case REQ_ENTER:
2527                 report("Nothing to enter");
2528                 break;
2529
2530
2531         case REQ_VIEW_CLOSE:
2532                 /* XXX: Mark closed views by letting view->parent point to the
2533                  * view itself. Parents to closed view should never be
2534                  * followed. */
2535                 if (view->parent &&
2536                     view->parent->parent != view->parent) {
2537                         memset(display, 0, sizeof(display));
2538                         current_view = 0;
2539                         display[current_view] = view->parent;
2540                         view->parent = view;
2541                         resize_display();
2542                         redraw_display();
2543                         break;
2544                 }
2545                 /* Fall-through */
2546         case REQ_QUIT:
2547                 return FALSE;
2548
2549         default:
2550                 /* An unknown key will show most commonly used commands. */
2551                 report("Unknown key, press 'h' for help");
2552                 return TRUE;
2553         }
2554
2555         return TRUE;
2556 }
2557
2558
2559 /*
2560  * Pager backend
2561  */
2562
2563 static bool
2564 pager_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
2565 {
2566         char *text = line->data;
2567         enum line_type type = line->type;
2568         int textlen = strlen(text);
2569         int attr;
2570
2571         wmove(view->win, lineno, 0);
2572
2573         if (selected) {
2574                 type = LINE_CURSOR;
2575                 wchgat(view->win, -1, 0, type, NULL);
2576         }
2577
2578         attr = get_line_attr(type);
2579         wattrset(view->win, attr);
2580
2581         if (opt_line_number || opt_tab_size < TABSIZE) {
2582                 static char spaces[] = "                    ";
2583                 int col_offset = 0, col = 0;
2584
2585                 if (opt_line_number) {
2586                         unsigned long real_lineno = view->offset + lineno + 1;
2587
2588                         if (real_lineno == 1 ||
2589                             (real_lineno % opt_num_interval) == 0) {
2590                                 wprintw(view->win, "%.*d", view->digits, real_lineno);
2591
2592                         } else {
2593                                 waddnstr(view->win, spaces,
2594                                          MIN(view->digits, STRING_SIZE(spaces)));
2595                         }
2596                         waddstr(view->win, ": ");
2597                         col_offset = view->digits + 2;
2598                 }
2599
2600                 while (text && col_offset + col < view->width) {
2601                         int cols_max = view->width - col_offset - col;
2602                         char *pos = text;
2603                         int cols;
2604
2605                         if (*text == '\t') {
2606                                 text++;
2607                                 assert(sizeof(spaces) > TABSIZE);
2608                                 pos = spaces;
2609                                 cols = opt_tab_size - (col % opt_tab_size);
2610
2611                         } else {
2612                                 text = strchr(text, '\t');
2613                                 cols = line ? text - pos : strlen(pos);
2614                         }
2615
2616                         waddnstr(view->win, pos, MIN(cols, cols_max));
2617                         col += cols;
2618                 }
2619
2620         } else {
2621                 int col = 0, pos = 0;
2622
2623                 for (; pos < textlen && col < view->width; pos++, col++)
2624                         if (text[pos] == '\t')
2625                                 col += TABSIZE - (col % TABSIZE) - 1;
2626
2627                 waddnstr(view->win, text, pos);
2628         }
2629
2630         return TRUE;
2631 }
2632
2633 static bool
2634 add_describe_ref(char *buf, size_t *bufpos, char *commit_id, const char *sep)
2635 {
2636         char refbuf[SIZEOF_STR];
2637         char *ref = NULL;
2638         FILE *pipe;
2639
2640         if (!string_format(refbuf, "git describe %s 2>/dev/null", commit_id))
2641                 return TRUE;
2642
2643         pipe = popen(refbuf, "r");
2644         if (!pipe)
2645                 return TRUE;
2646
2647         if ((ref = fgets(refbuf, sizeof(refbuf), pipe)))
2648                 ref = chomp_string(ref);
2649         pclose(pipe);
2650
2651         if (!ref || !*ref)
2652                 return TRUE;
2653
2654         /* This is the only fatal call, since it can "corrupt" the buffer. */
2655         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
2656                 return FALSE;
2657
2658         return TRUE;
2659 }
2660
2661 static void
2662 add_pager_refs(struct view *view, struct line *line)
2663 {
2664         char buf[SIZEOF_STR];
2665         char *commit_id = line->data + STRING_SIZE("commit ");
2666         struct ref **refs;
2667         size_t bufpos = 0, refpos = 0;
2668         const char *sep = "Refs: ";
2669         bool is_tag = FALSE;
2670
2671         assert(line->type == LINE_COMMIT);
2672
2673         refs = get_refs(commit_id);
2674         if (!refs) {
2675                 if (view == VIEW(REQ_VIEW_DIFF))
2676                         goto try_add_describe_ref;
2677                 return;
2678         }
2679
2680         do {
2681                 struct ref *ref = refs[refpos];
2682                 char *fmt = ref->tag    ? "%s[%s]" :
2683                             ref->remote ? "%s<%s>" : "%s%s";
2684
2685                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
2686                         return;
2687                 sep = ", ";
2688                 if (ref->tag)
2689                         is_tag = TRUE;
2690         } while (refs[refpos++]->next);
2691
2692         if (!is_tag && view == VIEW(REQ_VIEW_DIFF)) {
2693 try_add_describe_ref:
2694                 /* Add <tag>-g<commit_id> "fake" reference. */
2695                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
2696                         return;
2697         }
2698
2699         if (bufpos == 0)
2700                 return;
2701
2702         if (!realloc_lines(view, view->line_size + 1))
2703                 return;
2704
2705         add_line_text(view, buf, LINE_PP_REFS);
2706 }
2707
2708 static bool
2709 pager_read(struct view *view, char *data)
2710 {
2711         struct line *line;
2712
2713         if (!data)
2714                 return TRUE;
2715
2716         line = add_line_text(view, data, get_line_type(data));
2717         if (!line)
2718                 return FALSE;
2719
2720         if (line->type == LINE_COMMIT &&
2721             (view == VIEW(REQ_VIEW_DIFF) ||
2722              view == VIEW(REQ_VIEW_LOG)))
2723                 add_pager_refs(view, line);
2724
2725         return TRUE;
2726 }
2727
2728 static enum request
2729 pager_request(struct view *view, enum request request, struct line *line)
2730 {
2731         int split = 0;
2732
2733         if (request != REQ_ENTER)
2734                 return request;
2735
2736         if (line->type == LINE_COMMIT &&
2737            (view == VIEW(REQ_VIEW_LOG) ||
2738             view == VIEW(REQ_VIEW_PAGER))) {
2739                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
2740                 split = 1;
2741         }
2742
2743         /* Always scroll the view even if it was split. That way
2744          * you can use Enter to scroll through the log view and
2745          * split open each commit diff. */
2746         scroll_view(view, REQ_SCROLL_LINE_DOWN);
2747
2748         /* FIXME: A minor workaround. Scrolling the view will call report("")
2749          * but if we are scrolling a non-current view this won't properly
2750          * update the view title. */
2751         if (split)
2752                 update_view_title(view);
2753
2754         return REQ_NONE;
2755 }
2756
2757 static bool
2758 pager_grep(struct view *view, struct line *line)
2759 {
2760         regmatch_t pmatch;
2761         char *text = line->data;
2762
2763         if (!*text)
2764                 return FALSE;
2765
2766         if (regexec(view->regex, text, 1, &pmatch, 0) == REG_NOMATCH)
2767                 return FALSE;
2768
2769         return TRUE;
2770 }
2771
2772 static void
2773 pager_select(struct view *view, struct line *line)
2774 {
2775         if (line->type == LINE_COMMIT) {
2776                 char *text = line->data + STRING_SIZE("commit ");
2777
2778                 if (view != VIEW(REQ_VIEW_PAGER))
2779                         string_copy_rev(view->ref, text);
2780                 string_copy_rev(ref_commit, text);
2781         }
2782 }
2783
2784 static struct view_ops pager_ops = {
2785         "line",
2786         NULL,
2787         pager_read,
2788         pager_draw,
2789         pager_request,
2790         pager_grep,
2791         pager_select,
2792 };
2793
2794
2795 /*
2796  * Help backend
2797  */
2798
2799 static bool
2800 help_open(struct view *view)
2801 {
2802         char buf[BUFSIZ];
2803         int lines = ARRAY_SIZE(req_info) + 2;
2804         int i;
2805
2806         if (view->lines > 0)
2807                 return TRUE;
2808
2809         for (i = 0; i < ARRAY_SIZE(req_info); i++)
2810                 if (!req_info[i].request)
2811                         lines++;
2812
2813         lines += run_requests + 1;
2814
2815         view->line = calloc(lines, sizeof(*view->line));
2816         if (!view->line)
2817                 return FALSE;
2818
2819         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
2820
2821         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
2822                 char *key;
2823
2824                 if (req_info[i].request == REQ_NONE)
2825                         continue;
2826
2827                 if (!req_info[i].request) {
2828                         add_line_text(view, "", LINE_DEFAULT);
2829                         add_line_text(view, req_info[i].help, LINE_DEFAULT);
2830                         continue;
2831                 }
2832
2833                 key = get_key(req_info[i].request);
2834                 if (!*key)
2835                         key = "(no key defined)";
2836
2837                 if (!string_format(buf, "    %-25s %s", key, req_info[i].help))
2838                         continue;
2839
2840                 add_line_text(view, buf, LINE_DEFAULT);
2841         }
2842
2843         if (run_requests) {
2844                 add_line_text(view, "", LINE_DEFAULT);
2845                 add_line_text(view, "External commands:", LINE_DEFAULT);
2846         }
2847
2848         for (i = 0; i < run_requests; i++) {
2849                 struct run_request *req = get_run_request(REQ_NONE + i + 1);
2850                 char *key;
2851
2852                 if (!req)
2853                         continue;
2854
2855                 key = get_key_name(req->key);
2856                 if (!*key)
2857                         key = "(no key defined)";
2858
2859                 if (!string_format(buf, "    %-10s %-14s `%s`",
2860                                    keymap_table[req->keymap].name,
2861                                    key, req->cmd))
2862                         continue;
2863
2864                 add_line_text(view, buf, LINE_DEFAULT);
2865         }
2866
2867         return TRUE;
2868 }
2869
2870 static struct view_ops help_ops = {
2871         "line",
2872         help_open,
2873         NULL,
2874         pager_draw,
2875         pager_request,
2876         pager_grep,
2877         pager_select,
2878 };
2879
2880
2881 /*
2882  * Tree backend
2883  */
2884
2885 struct tree_stack_entry {
2886         struct tree_stack_entry *prev;  /* Entry below this in the stack */
2887         unsigned long lineno;           /* Line number to restore */
2888         char *name;                     /* Position of name in opt_path */
2889 };
2890
2891 /* The top of the path stack. */
2892 static struct tree_stack_entry *tree_stack = NULL;
2893 unsigned long tree_lineno = 0;
2894
2895 static void
2896 pop_tree_stack_entry(void)
2897 {
2898         struct tree_stack_entry *entry = tree_stack;
2899
2900         tree_lineno = entry->lineno;
2901         entry->name[0] = 0;
2902         tree_stack = entry->prev;
2903         free(entry);
2904 }
2905
2906 static void
2907 push_tree_stack_entry(char *name, unsigned long lineno)
2908 {
2909         struct tree_stack_entry *entry = calloc(1, sizeof(*entry));
2910         size_t pathlen = strlen(opt_path);
2911
2912         if (!entry)
2913                 return;
2914
2915         entry->prev = tree_stack;
2916         entry->name = opt_path + pathlen;
2917         tree_stack = entry;
2918
2919         if (!string_format_from(opt_path, &pathlen, "%s/", name)) {
2920                 pop_tree_stack_entry();
2921                 return;
2922         }
2923
2924         /* Move the current line to the first tree entry. */
2925         tree_lineno = 1;
2926         entry->lineno = lineno;
2927 }
2928
2929 /* Parse output from git-ls-tree(1):
2930  *
2931  * 100644 blob fb0e31ea6cc679b7379631188190e975f5789c26 Makefile
2932  * 100644 blob 5304ca4260aaddaee6498f9630e7d471b8591ea6 README
2933  * 100644 blob f931e1d229c3e185caad4449bf5b66ed72462657 tig.c
2934  * 100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38 web.conf
2935  */
2936
2937 #define SIZEOF_TREE_ATTR \
2938         STRING_SIZE("100644 blob ed09fe897f3c7c9af90bcf80cae92558ea88ae38\t")
2939
2940 #define TREE_UP_FORMAT "040000 tree %s\t.."
2941
2942 static int
2943 tree_compare_entry(enum line_type type1, char *name1,
2944                    enum line_type type2, char *name2)
2945 {
2946         if (type1 != type2) {
2947                 if (type1 == LINE_TREE_DIR)
2948                         return -1;
2949                 return 1;
2950         }
2951
2952         return strcmp(name1, name2);
2953 }
2954
2955 static bool
2956 tree_read(struct view *view, char *text)
2957 {
2958         size_t textlen = text ? strlen(text) : 0;
2959         char buf[SIZEOF_STR];
2960         unsigned long pos;
2961         enum line_type type;
2962         bool first_read = view->lines == 0;
2963
2964         if (textlen <= SIZEOF_TREE_ATTR)
2965                 return FALSE;
2966
2967         type = text[STRING_SIZE("100644 ")] == 't'
2968              ? LINE_TREE_DIR : LINE_TREE_FILE;
2969
2970         if (first_read) {
2971                 /* Add path info line */
2972                 if (!string_format(buf, "Directory path /%s", opt_path) ||
2973                     !realloc_lines(view, view->line_size + 1) ||
2974                     !add_line_text(view, buf, LINE_DEFAULT))
2975                         return FALSE;
2976
2977                 /* Insert "link" to parent directory. */
2978                 if (*opt_path) {
2979                         if (!string_format(buf, TREE_UP_FORMAT, view->ref) ||
2980                             !realloc_lines(view, view->line_size + 1) ||
2981                             !add_line_text(view, buf, LINE_TREE_DIR))
2982                                 return FALSE;
2983                 }
2984         }
2985
2986         /* Strip the path part ... */
2987         if (*opt_path) {
2988                 size_t pathlen = textlen - SIZEOF_TREE_ATTR;
2989                 size_t striplen = strlen(opt_path);
2990                 char *path = text + SIZEOF_TREE_ATTR;
2991
2992                 if (pathlen > striplen)
2993                         memmove(path, path + striplen,
2994                                 pathlen - striplen + 1);
2995         }
2996
2997         /* Skip "Directory ..." and ".." line. */
2998         for (pos = 1 + !!*opt_path; pos < view->lines; pos++) {
2999                 struct line *line = &view->line[pos];
3000                 char *path1 = ((char *) line->data) + SIZEOF_TREE_ATTR;
3001                 char *path2 = text + SIZEOF_TREE_ATTR;
3002                 int cmp = tree_compare_entry(line->type, path1, type, path2);
3003
3004                 if (cmp <= 0)
3005                         continue;
3006
3007                 text = strdup(text);
3008                 if (!text)
3009                         return FALSE;
3010
3011                 if (view->lines > pos)
3012                         memmove(&view->line[pos + 1], &view->line[pos],
3013                                 (view->lines - pos) * sizeof(*line));
3014
3015                 line = &view->line[pos];
3016                 line->data = text;
3017                 line->type = type;
3018                 view->lines++;
3019                 return TRUE;
3020         }
3021
3022         if (!add_line_text(view, text, type))
3023                 return FALSE;
3024
3025         if (tree_lineno > view->lineno) {
3026                 view->lineno = tree_lineno;
3027                 tree_lineno = 0;
3028         }
3029
3030         return TRUE;
3031 }
3032
3033 static enum request
3034 tree_request(struct view *view, enum request request, struct line *line)
3035 {
3036         enum open_flags flags;
3037
3038         if (request != REQ_ENTER)
3039                 return request;
3040
3041         /* Cleanup the stack if the tree view is at a different tree. */
3042         while (!*opt_path && tree_stack)
3043                 pop_tree_stack_entry();
3044
3045         switch (line->type) {
3046         case LINE_TREE_DIR:
3047                 /* Depending on whether it is a subdir or parent (updir?) link
3048                  * mangle the path buffer. */
3049                 if (line == &view->line[1] && *opt_path) {
3050                         pop_tree_stack_entry();
3051
3052                 } else {
3053                         char *data = line->data;
3054                         char *basename = data + SIZEOF_TREE_ATTR;
3055
3056                         push_tree_stack_entry(basename, view->lineno);
3057                 }
3058
3059                 /* Trees and subtrees share the same ID, so they are not not
3060                  * unique like blobs. */
3061                 flags = OPEN_RELOAD;
3062                 request = REQ_VIEW_TREE;
3063                 break;
3064
3065         case LINE_TREE_FILE:
3066                 flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
3067                 request = REQ_VIEW_BLOB;
3068                 break;
3069
3070         default:
3071                 return TRUE;
3072         }
3073
3074         open_view(view, request, flags);
3075         if (request == REQ_VIEW_TREE) {
3076                 view->lineno = tree_lineno;
3077         }
3078
3079         return REQ_NONE;
3080 }
3081
3082 static void
3083 tree_select(struct view *view, struct line *line)
3084 {
3085         char *text = line->data + STRING_SIZE("100644 blob ");
3086
3087         if (line->type == LINE_TREE_FILE) {
3088                 string_copy_rev(ref_blob, text);
3089
3090         } else if (line->type != LINE_TREE_DIR) {
3091                 return;
3092         }
3093
3094         string_copy_rev(view->ref, text);
3095 }
3096
3097 static struct view_ops tree_ops = {
3098         "file",
3099         NULL,
3100         tree_read,
3101         pager_draw,
3102         tree_request,
3103         pager_grep,
3104         tree_select,
3105 };
3106
3107 static bool
3108 blob_read(struct view *view, char *line)
3109 {
3110         return add_line_text(view, line, LINE_DEFAULT) != NULL;
3111 }
3112
3113 static struct view_ops blob_ops = {
3114         "line",
3115         NULL,
3116         blob_read,
3117         pager_draw,
3118         pager_request,
3119         pager_grep,
3120         pager_select,
3121 };
3122
3123
3124 /*
3125  * Status backend
3126  */
3127
3128 struct status {
3129         char status;
3130         struct {
3131                 mode_t mode;
3132                 char rev[SIZEOF_REV];
3133         } old;
3134         struct {
3135                 mode_t mode;
3136                 char rev[SIZEOF_REV];
3137         } new;
3138         char name[SIZEOF_STR];
3139 };
3140
3141 static struct status stage_status;
3142 static enum line_type stage_line_type;
3143
3144 /* Get fields from the diff line:
3145  * :100644 100644 06a5d6ae9eca55be2e0e585a152e6b1336f2b20e 0000000000000000000000000000000000000000 M
3146  */
3147 static inline bool
3148 status_get_diff(struct status *file, char *buf, size_t bufsize)
3149 {
3150         char *old_mode = buf +  1;
3151         char *new_mode = buf +  8;
3152         char *old_rev  = buf + 15;
3153         char *new_rev  = buf + 56;
3154         char *status   = buf + 97;
3155
3156         if (bufsize != 99 ||
3157             old_mode[-1] != ':' ||
3158             new_mode[-1] != ' ' ||
3159             old_rev[-1]  != ' ' ||
3160             new_rev[-1]  != ' ' ||
3161             status[-1]   != ' ')
3162                 return FALSE;
3163
3164         file->status = *status;
3165
3166         string_copy_rev(file->old.rev, old_rev);
3167         string_copy_rev(file->new.rev, new_rev);
3168
3169         file->old.mode = strtoul(old_mode, NULL, 8);
3170         file->new.mode = strtoul(new_mode, NULL, 8);
3171
3172         file->name[0] = 0;
3173
3174         return TRUE;
3175 }
3176
3177 static bool
3178 status_run(struct view *view, const char cmd[], bool diff, enum line_type type)
3179 {
3180         struct status *file = NULL;
3181         struct status *unmerged = NULL;
3182         char buf[SIZEOF_STR * 4];
3183         size_t bufsize = 0;
3184         FILE *pipe;
3185
3186         pipe = popen(cmd, "r");
3187         if (!pipe)
3188                 return FALSE;
3189
3190         add_line_data(view, NULL, type);
3191
3192         while (!feof(pipe) && !ferror(pipe)) {
3193                 char *sep;
3194                 size_t readsize;
3195
3196                 readsize = fread(buf + bufsize, 1, sizeof(buf) - bufsize, pipe);
3197                 if (!readsize)
3198                         break;
3199                 bufsize += readsize;
3200
3201                 /* Process while we have NUL chars. */
3202                 while ((sep = memchr(buf, 0, bufsize))) {
3203                         size_t sepsize = sep - buf + 1;
3204
3205                         if (!file) {
3206                                 if (!realloc_lines(view, view->line_size + 1))
3207                                         goto error_out;
3208
3209                                 file = calloc(1, sizeof(*file));
3210                                 if (!file)
3211                                         goto error_out;
3212
3213                                 add_line_data(view, file, type);
3214                         }
3215
3216                         /* Parse diff info part. */
3217                         if (!diff) {
3218                                 file->status = '?';
3219
3220                         } else if (!file->status) {
3221                                 if (!status_get_diff(file, buf, sepsize))
3222                                         goto error_out;
3223
3224                                 bufsize -= sepsize;
3225                                 memmove(buf, sep + 1, bufsize);
3226
3227                                 sep = memchr(buf, 0, bufsize);
3228                                 if (!sep)
3229                                         break;
3230                                 sepsize = sep - buf + 1;
3231
3232                                 /* Collapse all 'M'odified entries that
3233                                  * follow a associated 'U'nmerged entry.
3234                                  */
3235                                 if (file->status == 'U') {
3236                                         unmerged = file;
3237
3238                                 } else if (unmerged) {
3239                                         int collapse = !strcmp(buf, unmerged->name);
3240
3241                                         unmerged = NULL;
3242                                         if (collapse) {
3243                                                 free(file);
3244                                                 view->lines--;
3245                                                 continue;
3246                                         }
3247                                 }
3248                         }
3249
3250                         /* git-ls-files just delivers a NUL separated
3251                          * list of file names similar to the second half
3252                          * of the git-diff-* output. */
3253                         string_ncopy(file->name, buf, sepsize);
3254                         bufsize -= sepsize;
3255                         memmove(buf, sep + 1, bufsize);
3256                         file = NULL;
3257                 }
3258         }
3259
3260         if (ferror(pipe)) {
3261 error_out:
3262                 pclose(pipe);
3263                 return FALSE;
3264         }
3265
3266         if (!view->line[view->lines - 1].data)
3267                 add_line_data(view, NULL, LINE_STAT_NONE);
3268
3269         pclose(pipe);
3270         return TRUE;
3271 }
3272
3273 /* Don't show unmerged entries in the staged section. */
3274 #define STATUS_DIFF_INDEX_CMD "git diff-index -z --diff-filter=ACDMRTXB --cached HEAD"
3275 #define STATUS_DIFF_FILES_CMD "git diff-files -z"
3276 #define STATUS_LIST_OTHER_CMD \
3277         "git ls-files -z --others --exclude-per-directory=.gitignore"
3278
3279 #define STATUS_DIFF_SHOW_CMD \
3280         "git diff --root --patch-with-stat --find-copies-harder -B -C %s -- %s 2>/dev/null"
3281
3282 /* First parse staged info using git-diff-index(1), then parse unstaged
3283  * info using git-diff-files(1), and finally untracked files using
3284  * git-ls-files(1). */
3285 static bool
3286 status_open(struct view *view)
3287 {
3288         struct stat statbuf;
3289         char exclude[SIZEOF_STR];
3290         char cmd[SIZEOF_STR];
3291         unsigned long prev_lineno = view->lineno;
3292         size_t i;
3293
3294         for (i = 0; i < view->lines; i++)
3295                 free(view->line[i].data);
3296         free(view->line);
3297         view->lines = view->line_size = view->lineno = 0;
3298         view->line = NULL;
3299
3300         if (!realloc_lines(view, view->line_size + 6))
3301                 return FALSE;
3302
3303         if (!string_format(exclude, "%s/info/exclude", opt_git_dir))
3304                 return FALSE;
3305
3306         string_copy(cmd, STATUS_LIST_OTHER_CMD);
3307
3308         if (stat(exclude, &statbuf) >= 0) {
3309                 size_t cmdsize = strlen(cmd);
3310
3311                 if (!string_format_from(cmd, &cmdsize, " %s", "--exclude-from=") ||
3312                     sq_quote(cmd, cmdsize, exclude) >= sizeof(cmd))
3313                         return FALSE;
3314         }
3315
3316         if (!status_run(view, STATUS_DIFF_INDEX_CMD, TRUE, LINE_STAT_STAGED) ||
3317             !status_run(view, STATUS_DIFF_FILES_CMD, TRUE, LINE_STAT_UNSTAGED) ||
3318             !status_run(view, cmd, FALSE, LINE_STAT_UNTRACKED))
3319                 return FALSE;
3320
3321         /* If all went well restore the previous line number to stay in
3322          * the context. */
3323         if (prev_lineno < view->lines)
3324                 view->lineno = prev_lineno;
3325         else
3326                 view->lineno = view->lines - 1;
3327
3328         return TRUE;
3329 }
3330
3331 static bool
3332 status_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
3333 {
3334         struct status *status = line->data;
3335
3336         wmove(view->win, lineno, 0);
3337
3338         if (selected) {
3339                 wattrset(view->win, get_line_attr(LINE_CURSOR));
3340                 wchgat(view->win, -1, 0, LINE_CURSOR, NULL);
3341
3342         } else if (!status && line->type != LINE_STAT_NONE) {
3343                 wattrset(view->win, get_line_attr(LINE_STAT_SECTION));
3344                 wchgat(view->win, -1, 0, LINE_STAT_SECTION, NULL);
3345
3346         } else {
3347                 wattrset(view->win, get_line_attr(line->type));
3348         }
3349
3350         if (!status) {
3351                 char *text;
3352
3353                 switch (line->type) {
3354                 case LINE_STAT_STAGED:
3355                         text = "Changes to be committed:";
3356                         break;
3357
3358                 case LINE_STAT_UNSTAGED:
3359                         text = "Changed but not updated:";
3360                         break;
3361
3362                 case LINE_STAT_UNTRACKED:
3363                         text = "Untracked files:";
3364                         break;
3365
3366                 case LINE_STAT_NONE:
3367                         text = "    (no files)";
3368                         break;
3369
3370                 default:
3371                         return FALSE;
3372                 }
3373
3374                 waddstr(view->win, text);
3375                 return TRUE;
3376         }
3377
3378         waddch(view->win, status->status);
3379         if (!selected)
3380                 wattrset(view->win, A_NORMAL);
3381         wmove(view->win, lineno, 4);
3382         waddstr(view->win, status->name);
3383
3384         return TRUE;
3385 }
3386
3387 static enum request
3388 status_enter(struct view *view, struct line *line)
3389 {
3390         struct status *status = line->data;
3391         char path[SIZEOF_STR] = "";
3392         char *info;
3393         size_t cmdsize = 0;
3394
3395         if (line->type == LINE_STAT_NONE ||
3396             (!status && line[1].type == LINE_STAT_NONE)) {
3397                 report("No file to diff");
3398                 return REQ_NONE;
3399         }
3400
3401         if (status && sq_quote(path, 0, status->name) >= sizeof(path))
3402                 return REQ_QUIT;
3403
3404         if (opt_cdup[0] &&
3405             line->type != LINE_STAT_UNTRACKED &&
3406             !string_format_from(opt_cmd, &cmdsize, "cd %s;", opt_cdup))
3407                 return REQ_QUIT;
3408
3409         switch (line->type) {
3410         case LINE_STAT_STAGED:
3411                 if (!string_format_from(opt_cmd, &cmdsize, STATUS_DIFF_SHOW_CMD,
3412                                         "--cached", path))
3413                         return REQ_QUIT;
3414                 if (status)
3415                         info = "Staged changes to %s";
3416                 else
3417                         info = "Staged changes";
3418                 break;
3419
3420         case LINE_STAT_UNSTAGED:
3421                 if (!string_format_from(opt_cmd, &cmdsize, STATUS_DIFF_SHOW_CMD,
3422                                         "", path))
3423                         return REQ_QUIT;
3424                 if (status)
3425                         info = "Unstaged changes to %s";
3426                 else
3427                         info = "Unstaged changes";
3428                 break;
3429
3430         case LINE_STAT_UNTRACKED:
3431                 if (opt_pipe)
3432                         return REQ_QUIT;
3433
3434
3435                 if (!status) {
3436                         report("No file to show");
3437                         return REQ_NONE;
3438                 }
3439
3440                 opt_pipe = fopen(status->name, "r");
3441                 info = "Untracked file %s";
3442                 break;
3443
3444         default:
3445                 die("line type %d not handled in switch", line->type);
3446         }
3447
3448         open_view(view, REQ_VIEW_STAGE, OPEN_RELOAD | OPEN_SPLIT);
3449         if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) {
3450                 if (status) {
3451                         stage_status = *status;
3452                 } else {
3453                         memset(&stage_status, 0, sizeof(stage_status));
3454                 }
3455
3456                 stage_line_type = line->type;
3457                 string_format(VIEW(REQ_VIEW_STAGE)->ref, info, stage_status.name);
3458         }
3459
3460         return REQ_NONE;
3461 }
3462
3463
3464 static bool
3465 status_update_file(struct view *view, struct status *status, enum line_type type)
3466 {
3467         char cmd[SIZEOF_STR];
3468         char buf[SIZEOF_STR];
3469         size_t cmdsize = 0;
3470         size_t bufsize = 0;
3471         size_t written = 0;
3472         FILE *pipe;
3473
3474         if (opt_cdup[0] &&
3475             type != LINE_STAT_UNTRACKED &&
3476             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
3477                 return FALSE;
3478
3479         switch (type) {
3480         case LINE_STAT_STAGED:
3481                 if (!string_format_from(buf, &bufsize, "%06o %s\t%s%c",
3482                                         status->old.mode,
3483                                         status->old.rev,
3484                                         status->name, 0))
3485                         return FALSE;
3486
3487                 string_add(cmd, cmdsize, "git update-index -z --index-info");
3488                 break;
3489
3490         case LINE_STAT_UNSTAGED:
3491         case LINE_STAT_UNTRACKED:
3492                 if (!string_format_from(buf, &bufsize, "%s%c", status->name, 0))
3493                         return FALSE;
3494
3495                 string_add(cmd, cmdsize, "git update-index -z --add --remove --stdin");
3496                 break;
3497
3498         default:
3499                 die("line type %d not handled in switch", type);
3500         }
3501
3502         pipe = popen(cmd, "w");
3503         if (!pipe)
3504                 return FALSE;
3505
3506         while (!ferror(pipe) && written < bufsize) {
3507                 written += fwrite(buf + written, 1, bufsize - written, pipe);
3508         }
3509
3510         pclose(pipe);
3511
3512         if (written != bufsize)
3513                 return FALSE;
3514
3515         return TRUE;
3516 }
3517
3518 static void
3519 status_update(struct view *view)
3520 {
3521         struct line *line = &view->line[view->lineno];
3522
3523         assert(view->lines);
3524
3525         if (!line->data) {
3526                 while (++line < view->line + view->lines && line->data) {
3527                         if (!status_update_file(view, line->data, line->type))
3528                                 report("Failed to update file status");
3529                 }
3530
3531                 if (!line[-1].data) {
3532                         report("Nothing to update");
3533                         return;
3534                 }
3535
3536         } else if (!status_update_file(view, line->data, line->type)) {
3537                 report("Failed to update file status");
3538         }
3539 }
3540
3541 static enum request
3542 status_request(struct view *view, enum request request, struct line *line)
3543 {
3544         struct status *status = line->data;
3545
3546         switch (request) {
3547         case REQ_STATUS_UPDATE:
3548                 status_update(view);
3549                 break;
3550
3551         case REQ_STATUS_MERGE:
3552                 open_mergetool(status->name);
3553                 break;
3554
3555         case REQ_EDIT:
3556                 if (!status)
3557                         return request;
3558
3559                 open_editor(status->status != '?', status->name);
3560                 break;
3561
3562         case REQ_ENTER:
3563                 /* After returning the status view has been split to
3564                  * show the stage view. No further reloading is
3565                  * necessary. */
3566                 status_enter(view, line);
3567                 return REQ_NONE;
3568
3569         case REQ_REFRESH:
3570                 /* Simply reload the view. */
3571                 break;
3572
3573         default:
3574                 return request;
3575         }
3576
3577         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
3578
3579         return REQ_NONE;
3580 }
3581
3582 static void
3583 status_select(struct view *view, struct line *line)
3584 {
3585         struct status *status = line->data;
3586         char file[SIZEOF_STR] = "all files";
3587         char *text;
3588         char *key;
3589
3590         if (status && !string_format(file, "'%s'", status->name))
3591                 return;
3592
3593         if (!status && line[1].type == LINE_STAT_NONE)
3594                 line++;
3595
3596         switch (line->type) {
3597         case LINE_STAT_STAGED:
3598                 text = "Press %s to unstage %s for commit";
3599                 break;
3600
3601         case LINE_STAT_UNSTAGED:
3602                 text = "Press %s to stage %s for commit";
3603                 break;
3604
3605         case LINE_STAT_UNTRACKED:
3606                 text = "Press %s to stage %s for addition";
3607                 break;
3608
3609         case LINE_STAT_NONE:
3610                 text = "Nothing to update";
3611                 break;
3612
3613         default:
3614                 die("line type %d not handled in switch", line->type);
3615         }
3616
3617         if (status && status->status == 'U') {
3618                 text = "Press %s to resolve conflict in %s";
3619                 key = get_key(REQ_STATUS_MERGE);
3620
3621         } else {
3622                 key = get_key(REQ_STATUS_UPDATE);
3623         }
3624
3625         string_format(view->ref, text, key, file);
3626 }
3627
3628 static bool
3629 status_grep(struct view *view, struct line *line)
3630 {
3631         struct status *status = line->data;
3632         enum { S_STATUS, S_NAME, S_END } state;
3633         char buf[2] = "?";
3634         regmatch_t pmatch;
3635
3636         if (!status)
3637                 return FALSE;
3638
3639         for (state = S_STATUS; state < S_END; state++) {
3640                 char *text;
3641
3642                 switch (state) {
3643                 case S_NAME:    text = status->name;    break;
3644                 case S_STATUS:
3645                         buf[0] = status->status;
3646                         text = buf;
3647                         break;
3648
3649                 default:
3650                         return FALSE;
3651                 }
3652
3653                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
3654                         return TRUE;
3655         }
3656
3657         return FALSE;
3658 }
3659
3660 static struct view_ops status_ops = {
3661         "file",
3662         status_open,
3663         NULL,
3664         status_draw,
3665         status_request,
3666         status_grep,
3667         status_select,
3668 };
3669
3670
3671 static bool
3672 stage_diff_line(FILE *pipe, struct line *line)
3673 {
3674         char *buf = line->data;
3675         size_t bufsize = strlen(buf);
3676         size_t written = 0;
3677
3678         while (!ferror(pipe) && written < bufsize) {
3679                 written += fwrite(buf + written, 1, bufsize - written, pipe);
3680         }
3681
3682         fputc('\n', pipe);
3683
3684         return written == bufsize;
3685 }
3686
3687 static struct line *
3688 stage_diff_hdr(struct view *view, struct line *line)
3689 {
3690         int diff_hdr_dir = line->type == LINE_DIFF_CHUNK ? -1 : 1;
3691         struct line *diff_hdr;
3692
3693         if (line->type == LINE_DIFF_CHUNK)
3694                 diff_hdr = line - 1;
3695         else
3696                 diff_hdr = view->line + 1;
3697
3698         while (diff_hdr > view->line && diff_hdr < view->line + view->lines) {
3699                 if (diff_hdr->type == LINE_DIFF_HEADER)
3700                         return diff_hdr;
3701
3702                 diff_hdr += diff_hdr_dir;
3703         }
3704
3705         return NULL;
3706 }
3707
3708 static bool
3709 stage_update_chunk(struct view *view, struct line *line)
3710 {
3711         char cmd[SIZEOF_STR];
3712         size_t cmdsize = 0;
3713         struct line *diff_hdr, *diff_chunk, *diff_end;
3714         FILE *pipe;
3715
3716         diff_hdr = stage_diff_hdr(view, line);
3717         if (!diff_hdr)
3718                 return FALSE;
3719
3720         if (opt_cdup[0] &&
3721             !string_format_from(cmd, &cmdsize, "cd %s;", opt_cdup))
3722                 return FALSE;
3723
3724         if (!string_format_from(cmd, &cmdsize,
3725                                 "git apply --cached %s - && "
3726                                 "git update-index -q --unmerged --refresh 2>/dev/null",
3727                                 stage_line_type == LINE_STAT_STAGED ? "-R" : ""))
3728                 return FALSE;
3729
3730         pipe = popen(cmd, "w");
3731         if (!pipe)
3732                 return FALSE;
3733
3734         diff_end = view->line + view->lines;
3735         if (line->type != LINE_DIFF_CHUNK) {
3736                 diff_chunk = diff_hdr;
3737
3738         } else {
3739                 for (diff_chunk = line + 1; diff_chunk < diff_end; diff_chunk++)
3740                         if (diff_chunk->type == LINE_DIFF_CHUNK ||
3741                             diff_chunk->type == LINE_DIFF_HEADER)
3742                                 diff_end = diff_chunk;
3743
3744                 diff_chunk = line;
3745
3746                 while (diff_hdr->type != LINE_DIFF_CHUNK) {
3747                         switch (diff_hdr->type) {
3748                         case LINE_DIFF_HEADER:
3749                         case LINE_DIFF_INDEX:
3750                         case LINE_DIFF_ADD:
3751                         case LINE_DIFF_DEL:
3752                                 break;
3753
3754                         default:
3755                                 diff_hdr++;
3756                                 continue;
3757                         }
3758
3759                         if (!stage_diff_line(pipe, diff_hdr++)) {
3760                                 pclose(pipe);
3761                                 return FALSE;
3762                         }
3763                 }
3764         }
3765
3766         while (diff_chunk < diff_end && stage_diff_line(pipe, diff_chunk))
3767                 diff_chunk++;
3768
3769         pclose(pipe);
3770
3771         if (diff_chunk != diff_end)
3772                 return FALSE;
3773
3774         return TRUE;
3775 }
3776
3777 static void
3778 stage_update(struct view *view, struct line *line)
3779 {
3780         if (stage_line_type != LINE_STAT_UNTRACKED &&
3781             (line->type == LINE_DIFF_CHUNK || !stage_status.status)) {
3782                 if (!stage_update_chunk(view, line)) {
3783                         report("Failed to apply chunk");
3784                         return;
3785                 }
3786
3787         } else if (!status_update_file(view, &stage_status, stage_line_type)) {
3788                 report("Failed to update file");
3789                 return;
3790         }
3791
3792         open_view(view, REQ_VIEW_STATUS, OPEN_RELOAD);
3793
3794         view = VIEW(REQ_VIEW_STATUS);
3795         if (view_is_displayed(view))
3796                 status_enter(view, &view->line[view->lineno]);
3797 }
3798
3799 static enum request
3800 stage_request(struct view *view, enum request request, struct line *line)
3801 {
3802         switch (request) {
3803         case REQ_STATUS_UPDATE:
3804                 stage_update(view, line);
3805                 break;
3806
3807         case REQ_EDIT:
3808                 if (!stage_status.name[0])
3809                         return request;
3810
3811                 open_editor(stage_status.status != '?', stage_status.name);
3812                 break;
3813
3814         case REQ_ENTER:
3815                 pager_request(view, request, line);
3816                 break;
3817
3818         default:
3819                 return request;
3820         }
3821
3822         return REQ_NONE;
3823 }
3824
3825 static struct view_ops stage_ops = {
3826         "line",
3827         NULL,
3828         pager_read,
3829         pager_draw,
3830         stage_request,
3831         pager_grep,
3832         pager_select,
3833 };
3834
3835
3836 /*
3837  * Revision graph
3838  */
3839
3840 struct commit {
3841         char id[SIZEOF_REV];            /* SHA1 ID. */
3842         char title[128];                /* First line of the commit message. */
3843         char author[75];                /* Author of the commit. */
3844         struct tm time;                 /* Date from the author ident. */
3845         struct ref **refs;              /* Repository references. */
3846         chtype graph[SIZEOF_REVGRAPH];  /* Ancestry chain graphics. */
3847         size_t graph_size;              /* The width of the graph array. */
3848 };
3849
3850 /* Size of rev graph with no  "padding" columns */
3851 #define SIZEOF_REVITEMS (SIZEOF_REVGRAPH - (SIZEOF_REVGRAPH / 2))
3852
3853 struct rev_graph {
3854         struct rev_graph *prev, *next, *parents;
3855         char rev[SIZEOF_REVITEMS][SIZEOF_REV];
3856         size_t size;
3857         struct commit *commit;
3858         size_t pos;
3859 };
3860
3861 /* Parents of the commit being visualized. */
3862 static struct rev_graph graph_parents[4];
3863
3864 /* The current stack of revisions on the graph. */
3865 static struct rev_graph graph_stacks[4] = {
3866         { &graph_stacks[3], &graph_stacks[1], &graph_parents[0] },
3867         { &graph_stacks[0], &graph_stacks[2], &graph_parents[1] },
3868         { &graph_stacks[1], &graph_stacks[3], &graph_parents[2] },
3869         { &graph_stacks[2], &graph_stacks[0], &graph_parents[3] },
3870 };
3871
3872 static inline bool
3873 graph_parent_is_merge(struct rev_graph *graph)
3874 {
3875         return graph->parents->size > 1;
3876 }
3877
3878 static inline void
3879 append_to_rev_graph(struct rev_graph *graph, chtype symbol)
3880 {
3881         struct commit *commit = graph->commit;
3882
3883         if (commit->graph_size < ARRAY_SIZE(commit->graph) - 1)
3884                 commit->graph[commit->graph_size++] = symbol;
3885 }
3886
3887 static void
3888 done_rev_graph(struct rev_graph *graph)
3889 {
3890         if (graph_parent_is_merge(graph) &&
3891             graph->pos < graph->size - 1 &&
3892             graph->next->size == graph->size + graph->parents->size - 1) {
3893                 size_t i = graph->pos + graph->parents->size - 1;
3894
3895                 graph->commit->graph_size = i * 2;
3896                 while (i < graph->next->size - 1) {
3897                         append_to_rev_graph(graph, ' ');
3898                         append_to_rev_graph(graph, '\\');
3899                         i++;
3900                 }
3901         }
3902
3903         graph->size = graph->pos = 0;
3904         graph->commit = NULL;
3905         memset(graph->parents, 0, sizeof(*graph->parents));
3906 }
3907
3908 static void
3909 push_rev_graph(struct rev_graph *graph, char *parent)
3910 {
3911         int i;
3912
3913         /* "Collapse" duplicate parents lines.
3914          *
3915          * FIXME: This needs to also update update the drawn graph but
3916          * for now it just serves as a method for pruning graph lines. */
3917         for (i = 0; i < graph->size; i++)
3918                 if (!strncmp(graph->rev[i], parent, SIZEOF_REV))
3919                         return;
3920
3921         if (graph->size < SIZEOF_REVITEMS) {
3922                 string_copy_rev(graph->rev[graph->size++], parent);
3923         }
3924 }
3925
3926 static chtype
3927 get_rev_graph_symbol(struct rev_graph *graph)
3928 {
3929         chtype symbol;
3930
3931         if (graph->parents->size == 0)
3932                 symbol = REVGRAPH_INIT;
3933         else if (graph_parent_is_merge(graph))
3934                 symbol = REVGRAPH_MERGE;
3935         else if (graph->pos >= graph->size)
3936                 symbol = REVGRAPH_BRANCH;
3937         else
3938                 symbol = REVGRAPH_COMMIT;
3939
3940         return symbol;
3941 }
3942
3943 static void
3944 draw_rev_graph(struct rev_graph *graph)
3945 {
3946         struct rev_filler {
3947                 chtype separator, line;
3948         };
3949         enum { DEFAULT, RSHARP, RDIAG, LDIAG };
3950         static struct rev_filler fillers[] = {
3951                 { ' ',  REVGRAPH_LINE },
3952                 { '`',  '.' },
3953                 { '\'', ' ' },
3954                 { '/',  ' ' },
3955         };
3956         chtype symbol = get_rev_graph_symbol(graph);
3957         struct rev_filler *filler;
3958         size_t i;
3959
3960         filler = &fillers[DEFAULT];
3961
3962         for (i = 0; i < graph->pos; i++) {
3963                 append_to_rev_graph(graph, filler->line);
3964                 if (graph_parent_is_merge(graph->prev) &&
3965                     graph->prev->pos == i)
3966                         filler = &fillers[RSHARP];
3967
3968                 append_to_rev_graph(graph, filler->separator);
3969         }
3970
3971         /* Place the symbol for this revision. */
3972         append_to_rev_graph(graph, symbol);
3973
3974         if (graph->prev->size > graph->size)
3975                 filler = &fillers[RDIAG];
3976         else
3977                 filler = &fillers[DEFAULT];
3978
3979         i++;
3980
3981         for (; i < graph->size; i++) {
3982                 append_to_rev_graph(graph, filler->separator);
3983                 append_to_rev_graph(graph, filler->line);
3984                 if (graph_parent_is_merge(graph->prev) &&
3985                     i < graph->prev->pos + graph->parents->size)
3986                         filler = &fillers[RSHARP];
3987                 if (graph->prev->size > graph->size)
3988                         filler = &fillers[LDIAG];
3989         }
3990
3991         if (graph->prev->size > graph->size) {
3992                 append_to_rev_graph(graph, filler->separator);
3993                 if (filler->line != ' ')
3994                         append_to_rev_graph(graph, filler->line);
3995         }
3996 }
3997
3998 /* Prepare the next rev graph */
3999 static void
4000 prepare_rev_graph(struct rev_graph *graph)
4001 {
4002         size_t i;
4003
4004         /* First, traverse all lines of revisions up to the active one. */
4005         for (graph->pos = 0; graph->pos < graph->size; graph->pos++) {
4006                 if (!strcmp(graph->rev[graph->pos], graph->commit->id))
4007                         break;
4008
4009                 push_rev_graph(graph->next, graph->rev[graph->pos]);
4010         }
4011
4012         /* Interleave the new revision parent(s). */
4013         for (i = 0; i < graph->parents->size; i++)
4014                 push_rev_graph(graph->next, graph->parents->rev[i]);
4015
4016         /* Lastly, put any remaining revisions. */
4017         for (i = graph->pos + 1; i < graph->size; i++)
4018                 push_rev_graph(graph->next, graph->rev[i]);
4019 }
4020
4021 static void
4022 update_rev_graph(struct rev_graph *graph)
4023 {
4024         /* If this is the finalizing update ... */
4025         if (graph->commit)
4026                 prepare_rev_graph(graph);
4027
4028         /* Graph visualization needs a one rev look-ahead,
4029          * so the first update doesn't visualize anything. */
4030         if (!graph->prev->commit)
4031                 return;
4032
4033         draw_rev_graph(graph->prev);
4034         done_rev_graph(graph->prev->prev);
4035 }
4036
4037
4038 /*
4039  * Main view backend
4040  */
4041
4042 static bool
4043 main_draw(struct view *view, struct line *line, unsigned int lineno, bool selected)
4044 {
4045         char buf[DATE_COLS + 1];
4046         struct commit *commit = line->data;
4047         enum line_type type;
4048         int col = 0;
4049         size_t timelen;
4050         size_t authorlen;
4051         int trimmed = 1;
4052
4053         if (!*commit->author)
4054                 return FALSE;
4055
4056         wmove(view->win, lineno, col);
4057
4058         if (selected) {
4059                 type = LINE_CURSOR;
4060                 wattrset(view->win, get_line_attr(type));
4061                 wchgat(view->win, -1, 0, type, NULL);
4062
4063         } else {
4064                 type = LINE_MAIN_COMMIT;
4065                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
4066         }
4067
4068         timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
4069         waddnstr(view->win, buf, timelen);
4070         waddstr(view->win, " ");
4071
4072         col += DATE_COLS;
4073         wmove(view->win, lineno, col);
4074         if (type != LINE_CURSOR)
4075                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
4076
4077         if (opt_utf8) {
4078                 authorlen = utf8_length(commit->author, AUTHOR_COLS - 2, &col, &trimmed);
4079         } else {
4080                 authorlen = strlen(commit->author);
4081                 if (authorlen > AUTHOR_COLS - 2) {
4082                         authorlen = AUTHOR_COLS - 2;
4083                         trimmed = 1;
4084                 }
4085         }
4086
4087         if (trimmed) {
4088                 waddnstr(view->win, commit->author, authorlen);
4089                 if (type != LINE_CURSOR)
4090                         wattrset(view->win, get_line_attr(LINE_MAIN_DELIM));
4091                 waddch(view->win, '~');
4092         } else {
4093                 waddstr(view->win, commit->author);
4094         }
4095
4096         col += AUTHOR_COLS;
4097         if (type != LINE_CURSOR)
4098                 wattrset(view->win, A_NORMAL);
4099
4100         if (opt_rev_graph && commit->graph_size) {
4101                 size_t i;
4102
4103                 wmove(view->win, lineno, col);
4104                 /* Using waddch() instead of waddnstr() ensures that
4105                  * they'll be rendered correctly for the cursor line. */
4106                 for (i = 0; i < commit->graph_size; i++)
4107                         waddch(view->win, commit->graph[i]);
4108
4109                 waddch(view->win, ' ');
4110                 col += commit->graph_size + 1;
4111         }
4112
4113         wmove(view->win, lineno, col);
4114
4115         if (commit->refs) {
4116                 size_t i = 0;
4117
4118                 do {
4119                         if (type == LINE_CURSOR)
4120                                 ;
4121                         else if (commit->refs[i]->tag)
4122                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
4123                         else if (commit->refs[i]->remote)
4124                                 wattrset(view->win, get_line_attr(LINE_MAIN_REMOTE));
4125                         else
4126                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
4127                         waddstr(view->win, "[");
4128                         waddstr(view->win, commit->refs[i]->name);
4129                         waddstr(view->win, "]");
4130                         if (type != LINE_CURSOR)
4131                                 wattrset(view->win, A_NORMAL);
4132                         waddstr(view->win, " ");
4133                         col += strlen(commit->refs[i]->name) + STRING_SIZE("[] ");
4134                 } while (commit->refs[i++]->next);
4135         }
4136
4137         if (type != LINE_CURSOR)
4138                 wattrset(view->win, get_line_attr(type));
4139
4140         {
4141                 int titlelen = strlen(commit->title);
4142
4143                 if (col + titlelen > view->width)
4144                         titlelen = view->width - col;
4145
4146                 waddnstr(view->win, commit->title, titlelen);
4147         }
4148
4149         return TRUE;
4150 }
4151
4152 /* Reads git log --pretty=raw output and parses it into the commit struct. */
4153 static bool
4154 main_read(struct view *view, char *line)
4155 {
4156         static struct rev_graph *graph = graph_stacks;
4157         enum line_type type;
4158         struct commit *commit;
4159
4160         if (!line) {
4161                 update_rev_graph(graph);
4162                 return TRUE;
4163         }
4164
4165         type = get_line_type(line);
4166         if (type == LINE_COMMIT) {
4167                 commit = calloc(1, sizeof(struct commit));
4168                 if (!commit)
4169                         return FALSE;
4170
4171                 string_copy_rev(commit->id, line + STRING_SIZE("commit "));
4172                 commit->refs = get_refs(commit->id);
4173                 graph->commit = commit;
4174                 add_line_data(view, commit, LINE_MAIN_COMMIT);
4175                 return TRUE;
4176         }
4177
4178         if (!view->lines)
4179                 return TRUE;
4180         commit = view->line[view->lines - 1].data;
4181
4182         switch (type) {
4183         case LINE_PARENT:
4184                 push_rev_graph(graph->parents, line + STRING_SIZE("parent "));
4185                 break;
4186
4187         case LINE_AUTHOR:
4188         {
4189                 /* Parse author lines where the name may be empty:
4190                  *      author  <email@address.tld> 1138474660 +0100
4191                  */
4192                 char *ident = line + STRING_SIZE("author ");
4193                 char *nameend = strchr(ident, '<');
4194                 char *emailend = strchr(ident, '>');
4195
4196                 if (!nameend || !emailend)
4197                         break;
4198
4199                 update_rev_graph(graph);
4200                 graph = graph->next;
4201
4202                 *nameend = *emailend = 0;
4203                 ident = chomp_string(ident);
4204                 if (!*ident) {
4205                         ident = chomp_string(nameend + 1);
4206                         if (!*ident)
4207                                 ident = "Unknown";
4208                 }
4209
4210                 string_ncopy(commit->author, ident, strlen(ident));
4211
4212                 /* Parse epoch and timezone */
4213                 if (emailend[1] == ' ') {
4214                         char *secs = emailend + 2;
4215                         char *zone = strchr(secs, ' ');
4216                         time_t time = (time_t) atol(secs);
4217
4218                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
4219                                 long tz;
4220
4221                                 zone++;
4222                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
4223                                 tz += ('0' - zone[2]) * 60 * 60;
4224                                 tz += ('0' - zone[3]) * 60;
4225                                 tz += ('0' - zone[4]) * 60;
4226
4227                                 if (zone[0] == '-')
4228                                         tz = -tz;
4229
4230                                 time -= tz;
4231                         }
4232
4233                         gmtime_r(&time, &commit->time);
4234                 }
4235                 break;
4236         }
4237         default:
4238                 /* Fill in the commit title if it has not already been set. */
4239                 if (commit->title[0])
4240                         break;
4241
4242                 /* Require titles to start with a non-space character at the
4243                  * offset used by git log. */
4244                 if (strncmp(line, "    ", 4))
4245                         break;
4246                 line += 4;
4247                 /* Well, if the title starts with a whitespace character,
4248                  * try to be forgiving.  Otherwise we end up with no title. */
4249                 while (isspace(*line))
4250                         line++;
4251                 if (*line == '\0')
4252                         break;
4253                 /* FIXME: More graceful handling of titles; append "..." to
4254                  * shortened titles, etc. */
4255
4256                 string_ncopy(commit->title, line, strlen(line));
4257         }
4258
4259         return TRUE;
4260 }
4261
4262 static enum request
4263 main_request(struct view *view, enum request request, struct line *line)
4264 {
4265         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
4266
4267         if (request == REQ_ENTER)
4268                 open_view(view, REQ_VIEW_DIFF, flags);
4269         else
4270                 return request;
4271
4272         return REQ_NONE;
4273 }
4274
4275 static bool
4276 main_grep(struct view *view, struct line *line)
4277 {
4278         struct commit *commit = line->data;
4279         enum { S_TITLE, S_AUTHOR, S_DATE, S_END } state;
4280         char buf[DATE_COLS + 1];
4281         regmatch_t pmatch;
4282
4283         for (state = S_TITLE; state < S_END; state++) {
4284                 char *text;
4285
4286                 switch (state) {
4287                 case S_TITLE:   text = commit->title;   break;
4288                 case S_AUTHOR:  text = commit->author;  break;
4289                 case S_DATE:
4290                         if (!strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time))
4291                                 continue;
4292                         text = buf;
4293                         break;
4294
4295                 default:
4296                         return FALSE;
4297                 }
4298
4299                 if (regexec(view->regex, text, 1, &pmatch, 0) != REG_NOMATCH)
4300                         return TRUE;
4301         }
4302
4303         return FALSE;
4304 }
4305
4306 static void
4307 main_select(struct view *view, struct line *line)
4308 {
4309         struct commit *commit = line->data;
4310
4311         string_copy_rev(view->ref, commit->id);
4312         string_copy_rev(ref_commit, view->ref);
4313 }
4314
4315 static struct view_ops main_ops = {
4316         "commit",
4317         NULL,
4318         main_read,
4319         main_draw,
4320         main_request,
4321         main_grep,
4322         main_select,
4323 };
4324
4325
4326 /*
4327  * Unicode / UTF-8 handling
4328  *
4329  * NOTE: Much of the following code for dealing with unicode is derived from
4330  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
4331  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
4332  */
4333
4334 /* I've (over)annotated a lot of code snippets because I am not entirely
4335  * confident that the approach taken by this small UTF-8 interface is correct.
4336  * --jonas */
4337
4338 static inline int
4339 unicode_width(unsigned long c)
4340 {
4341         if (c >= 0x1100 &&
4342            (c <= 0x115f                         /* Hangul Jamo */
4343             || c == 0x2329
4344             || c == 0x232a
4345             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
4346                                                 /* CJK ... Yi */
4347             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
4348             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
4349             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
4350             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
4351             || (c >= 0xffe0  && c <= 0xffe6)
4352             || (c >= 0x20000 && c <= 0x2fffd)
4353             || (c >= 0x30000 && c <= 0x3fffd)))
4354                 return 2;
4355
4356         return 1;
4357 }
4358
4359 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
4360  * Illegal bytes are set one. */
4361 static const unsigned char utf8_bytes[256] = {
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         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,
4368         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,
4369         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,
4370 };
4371
4372 /* Decode UTF-8 multi-byte representation into a unicode character. */
4373 static inline unsigned long
4374 utf8_to_unicode(const char *string, size_t length)
4375 {
4376         unsigned long unicode;
4377
4378         switch (length) {
4379         case 1:
4380                 unicode  =   string[0];
4381                 break;
4382         case 2:
4383                 unicode  =  (string[0] & 0x1f) << 6;
4384                 unicode +=  (string[1] & 0x3f);
4385                 break;
4386         case 3:
4387                 unicode  =  (string[0] & 0x0f) << 12;
4388                 unicode += ((string[1] & 0x3f) << 6);
4389                 unicode +=  (string[2] & 0x3f);
4390                 break;
4391         case 4:
4392                 unicode  =  (string[0] & 0x0f) << 18;
4393                 unicode += ((string[1] & 0x3f) << 12);
4394                 unicode += ((string[2] & 0x3f) << 6);
4395                 unicode +=  (string[3] & 0x3f);
4396                 break;
4397         case 5:
4398                 unicode  =  (string[0] & 0x0f) << 24;
4399                 unicode += ((string[1] & 0x3f) << 18);
4400                 unicode += ((string[2] & 0x3f) << 12);
4401                 unicode += ((string[3] & 0x3f) << 6);
4402                 unicode +=  (string[4] & 0x3f);
4403                 break;
4404         case 6:
4405                 unicode  =  (string[0] & 0x01) << 30;
4406                 unicode += ((string[1] & 0x3f) << 24);
4407                 unicode += ((string[2] & 0x3f) << 18);
4408                 unicode += ((string[3] & 0x3f) << 12);
4409                 unicode += ((string[4] & 0x3f) << 6);
4410                 unicode +=  (string[5] & 0x3f);
4411                 break;
4412         default:
4413                 die("Invalid unicode length");
4414         }
4415
4416         /* Invalid characters could return the special 0xfffd value but NUL
4417          * should be just as good. */
4418         return unicode > 0xffff ? 0 : unicode;
4419 }
4420
4421 /* Calculates how much of string can be shown within the given maximum width
4422  * and sets trimmed parameter to non-zero value if all of string could not be
4423  * shown.
4424  *
4425  * Additionally, adds to coloffset how many many columns to move to align with
4426  * the expected position. Takes into account how multi-byte and double-width
4427  * characters will effect the cursor position.
4428  *
4429  * Returns the number of bytes to output from string to satisfy max_width. */
4430 static size_t
4431 utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed)
4432 {
4433         const char *start = string;
4434         const char *end = strchr(string, '\0');
4435         size_t mbwidth = 0;
4436         size_t width = 0;
4437
4438         *trimmed = 0;
4439
4440         while (string < end) {
4441                 int c = *(unsigned char *) string;
4442                 unsigned char bytes = utf8_bytes[c];
4443                 size_t ucwidth;
4444                 unsigned long unicode;
4445
4446                 if (string + bytes > end)
4447                         break;
4448
4449                 /* Change representation to figure out whether
4450                  * it is a single- or double-width character. */
4451
4452                 unicode = utf8_to_unicode(string, bytes);
4453                 /* FIXME: Graceful handling of invalid unicode character. */
4454                 if (!unicode)
4455                         break;
4456
4457                 ucwidth = unicode_width(unicode);
4458                 width  += ucwidth;
4459                 if (width > max_width) {
4460                         *trimmed = 1;
4461                         break;
4462                 }
4463
4464                 /* The column offset collects the differences between the
4465                  * number of bytes encoding a character and the number of
4466                  * columns will be used for rendering said character.
4467                  *
4468                  * So if some character A is encoded in 2 bytes, but will be
4469                  * represented on the screen using only 1 byte this will and up
4470                  * adding 1 to the multi-byte column offset.
4471                  *
4472                  * Assumes that no double-width character can be encoding in
4473                  * less than two bytes. */
4474                 if (bytes > ucwidth)
4475                         mbwidth += bytes - ucwidth;
4476
4477                 string  += bytes;
4478         }
4479
4480         *coloffset += mbwidth;
4481
4482         return string - start;
4483 }
4484
4485
4486 /*
4487  * Status management
4488  */
4489
4490 /* Whether or not the curses interface has been initialized. */
4491 static bool cursed = FALSE;
4492
4493 /* The status window is used for polling keystrokes. */
4494 static WINDOW *status_win;
4495
4496 static bool status_empty = TRUE;
4497
4498 /* Update status and title window. */
4499 static void
4500 report(const char *msg, ...)
4501 {
4502         struct view *view = display[current_view];
4503
4504         if (input_mode)
4505                 return;
4506
4507         if (!view) {
4508                 char buf[SIZEOF_STR];
4509                 va_list args;
4510
4511                 va_start(args, msg);
4512                 if (vsnprintf(buf, sizeof(buf), msg, args) >= sizeof(buf)) {
4513                         buf[sizeof(buf) - 1] = 0;
4514                         buf[sizeof(buf) - 2] = '.';
4515                         buf[sizeof(buf) - 3] = '.';
4516                         buf[sizeof(buf) - 4] = '.';
4517                 }
4518                 va_end(args);
4519                 die("%s", buf);
4520         }
4521
4522         if (!status_empty || *msg) {
4523                 va_list args;
4524
4525                 va_start(args, msg);
4526
4527                 wmove(status_win, 0, 0);
4528                 if (*msg) {
4529                         vwprintw(status_win, msg, args);
4530                         status_empty = FALSE;
4531                 } else {
4532                         status_empty = TRUE;
4533                 }
4534                 wclrtoeol(status_win);
4535                 wrefresh(status_win);
4536
4537                 va_end(args);
4538         }
4539
4540         update_view_title(view);
4541         update_display_cursor(view);
4542 }
4543
4544 /* Controls when nodelay should be in effect when polling user input. */
4545 static void
4546 set_nonblocking_input(bool loading)
4547 {
4548         static unsigned int loading_views;
4549
4550         if ((loading == FALSE && loading_views-- == 1) ||
4551             (loading == TRUE  && loading_views++ == 0))
4552                 nodelay(status_win, loading);
4553 }
4554
4555 static void
4556 init_display(void)
4557 {
4558         int x, y;
4559
4560         /* Initialize the curses library */
4561         if (isatty(STDIN_FILENO)) {
4562                 cursed = !!initscr();
4563         } else {
4564                 /* Leave stdin and stdout alone when acting as a pager. */
4565                 FILE *io = fopen("/dev/tty", "r+");
4566
4567                 if (!io)
4568                         die("Failed to open /dev/tty");
4569                 cursed = !!newterm(NULL, io, io);
4570         }
4571
4572         if (!cursed)
4573                 die("Failed to initialize curses");
4574
4575         nonl();         /* Tell curses not to do NL->CR/NL on output */
4576         cbreak();       /* Take input chars one at a time, no wait for \n */
4577         noecho();       /* Don't echo input */
4578         leaveok(stdscr, TRUE);
4579
4580         if (has_colors())
4581                 init_colors();
4582
4583         getmaxyx(stdscr, y, x);
4584         status_win = newwin(1, 0, y - 1, 0);
4585         if (!status_win)
4586                 die("Failed to create status window");
4587
4588         /* Enable keyboard mapping */
4589         keypad(status_win, TRUE);
4590         wbkgdset(status_win, get_line_attr(LINE_STATUS));
4591 }
4592
4593 static char *
4594 read_prompt(const char *prompt)
4595 {
4596         enum { READING, STOP, CANCEL } status = READING;
4597         static char buf[sizeof(opt_cmd) - STRING_SIZE("git \0")];
4598         int pos = 0;
4599
4600         while (status == READING) {
4601                 struct view *view;
4602                 int i, key;
4603
4604                 input_mode = TRUE;
4605
4606                 foreach_view (view, i)
4607                         update_view(view);
4608
4609                 input_mode = FALSE;
4610
4611                 mvwprintw(status_win, 0, 0, "%s%.*s", prompt, pos, buf);
4612                 wclrtoeol(status_win);
4613
4614                 /* Refresh, accept single keystroke of input */
4615                 key = wgetch(status_win);
4616                 switch (key) {
4617                 case KEY_RETURN:
4618                 case KEY_ENTER:
4619                 case '\n':
4620                         status = pos ? STOP : CANCEL;
4621                         break;
4622
4623                 case KEY_BACKSPACE:
4624                         if (pos > 0)
4625                                 pos--;
4626                         else
4627                                 status = CANCEL;
4628                         break;
4629
4630                 case KEY_ESC:
4631                         status = CANCEL;
4632                         break;
4633
4634                 case ERR:
4635                         break;
4636
4637                 default:
4638                         if (pos >= sizeof(buf)) {
4639                                 report("Input string too long");
4640                                 return NULL;
4641                         }
4642
4643                         if (isprint(key))
4644                                 buf[pos++] = (char) key;
4645                 }
4646         }
4647
4648         /* Clear the status window */
4649         status_empty = FALSE;
4650         report("");
4651
4652         if (status == CANCEL)
4653                 return NULL;
4654
4655         buf[pos++] = 0;
4656
4657         return buf;
4658 }
4659
4660 /*
4661  * Repository references
4662  */
4663
4664 static struct ref *refs;
4665 static size_t refs_size;
4666
4667 /* Id <-> ref store */
4668 static struct ref ***id_refs;
4669 static size_t id_refs_size;
4670
4671 static struct ref **
4672 get_refs(char *id)
4673 {
4674         struct ref ***tmp_id_refs;
4675         struct ref **ref_list = NULL;
4676         size_t ref_list_size = 0;
4677         size_t i;
4678
4679         for (i = 0; i < id_refs_size; i++)
4680                 if (!strcmp(id, id_refs[i][0]->id))
4681                         return id_refs[i];
4682
4683         tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
4684         if (!tmp_id_refs)
4685                 return NULL;
4686
4687         id_refs = tmp_id_refs;
4688
4689         for (i = 0; i < refs_size; i++) {
4690                 struct ref **tmp;
4691
4692                 if (strcmp(id, refs[i].id))
4693                         continue;
4694
4695                 tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
4696                 if (!tmp) {
4697                         if (ref_list)
4698                                 free(ref_list);
4699                         return NULL;
4700                 }
4701
4702                 ref_list = tmp;
4703                 if (ref_list_size > 0)
4704                         ref_list[ref_list_size - 1]->next = 1;
4705                 ref_list[ref_list_size] = &refs[i];
4706
4707                 /* XXX: The properties of the commit chains ensures that we can
4708                  * safely modify the shared ref. The repo references will
4709                  * always be similar for the same id. */
4710                 ref_list[ref_list_size]->next = 0;
4711                 ref_list_size++;
4712         }
4713
4714         if (ref_list)
4715                 id_refs[id_refs_size++] = ref_list;
4716
4717         return ref_list;
4718 }
4719
4720 static int
4721 read_ref(char *id, size_t idlen, char *name, size_t namelen)
4722 {
4723         struct ref *ref;
4724         bool tag = FALSE;
4725         bool remote = FALSE;
4726
4727         if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
4728                 /* Commits referenced by tags has "^{}" appended. */
4729                 if (name[namelen - 1] != '}')
4730                         return OK;
4731
4732                 while (namelen > 0 && name[namelen] != '^')
4733                         namelen--;
4734
4735                 tag = TRUE;
4736                 namelen -= STRING_SIZE("refs/tags/");
4737                 name    += STRING_SIZE("refs/tags/");
4738
4739         } else if (!strncmp(name, "refs/remotes/", STRING_SIZE("refs/remotes/"))) {
4740                 remote = TRUE;
4741                 namelen -= STRING_SIZE("refs/remotes/");
4742                 name    += STRING_SIZE("refs/remotes/");
4743
4744         } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
4745                 namelen -= STRING_SIZE("refs/heads/");
4746                 name    += STRING_SIZE("refs/heads/");
4747
4748         } else if (!strcmp(name, "HEAD")) {
4749                 return OK;
4750         }
4751
4752         refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
4753         if (!refs)
4754                 return ERR;
4755
4756         ref = &refs[refs_size++];
4757         ref->name = malloc(namelen + 1);
4758         if (!ref->name)
4759                 return ERR;
4760
4761         strncpy(ref->name, name, namelen);
4762         ref->name[namelen] = 0;
4763         ref->tag = tag;
4764         ref->remote = remote;
4765         string_copy_rev(ref->id, id);
4766
4767         return OK;
4768 }
4769
4770 static int
4771 load_refs(void)
4772 {
4773         const char *cmd_env = getenv("TIG_LS_REMOTE");
4774         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
4775
4776         return read_properties(popen(cmd, "r"), "\t", read_ref);
4777 }
4778
4779 static int
4780 read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen)
4781 {
4782         if (!strcmp(name, "i18n.commitencoding"))
4783                 string_ncopy(opt_encoding, value, valuelen);
4784
4785         if (!strcmp(name, "core.editor"))
4786                 string_ncopy(opt_editor, value, valuelen);
4787
4788         return OK;
4789 }
4790
4791 static int
4792 load_repo_config(void)
4793 {
4794         return read_properties(popen(GIT_CONFIG " --list", "r"),
4795                                "=", read_repo_config_option);
4796 }
4797
4798 static int
4799 read_repo_info(char *name, size_t namelen, char *value, size_t valuelen)
4800 {
4801         if (!opt_git_dir[0]) {
4802                 string_ncopy(opt_git_dir, name, namelen);
4803
4804         } else if (opt_is_inside_work_tree == -1) {
4805                 /* This can be 3 different values depending on the
4806                  * version of git being used. If git-rev-parse does not
4807                  * understand --is-inside-work-tree it will simply echo
4808                  * the option else either "true" or "false" is printed.
4809                  * Default to true for the unknown case. */
4810                 opt_is_inside_work_tree = strcmp(name, "false") ? TRUE : FALSE;
4811
4812         } else {
4813                 string_ncopy(opt_cdup, name, namelen);
4814         }
4815
4816         return OK;
4817 }
4818
4819 /* XXX: The line outputted by "--show-cdup" can be empty so the option
4820  * must be the last one! */
4821 static int
4822 load_repo_info(void)
4823 {
4824         return read_properties(popen("git rev-parse --git-dir --is-inside-work-tree --show-cdup 2>/dev/null", "r"),
4825                                "=", read_repo_info);
4826 }
4827
4828 static int
4829 read_properties(FILE *pipe, const char *separators,
4830                 int (*read_property)(char *, size_t, char *, size_t))
4831 {
4832         char buffer[BUFSIZ];
4833         char *name;
4834         int state = OK;
4835
4836         if (!pipe)
4837                 return ERR;
4838
4839         while (state == OK && (name = fgets(buffer, sizeof(buffer), pipe))) {
4840                 char *value;
4841                 size_t namelen;
4842                 size_t valuelen;
4843
4844                 name = chomp_string(name);
4845                 namelen = strcspn(name, separators);
4846
4847                 if (name[namelen]) {
4848                         name[namelen] = 0;
4849                         value = chomp_string(name + namelen + 1);
4850                         valuelen = strlen(value);
4851
4852                 } else {
4853                         value = "";
4854                         valuelen = 0;
4855                 }
4856
4857                 state = read_property(name, namelen, value, valuelen);
4858         }
4859
4860         if (state != ERR && ferror(pipe))
4861                 state = ERR;
4862
4863         pclose(pipe);
4864
4865         return state;
4866 }
4867
4868
4869 /*
4870  * Main
4871  */
4872
4873 static void __NORETURN
4874 quit(int sig)
4875 {
4876         /* XXX: Restore tty modes and let the OS cleanup the rest! */
4877         if (cursed)
4878                 endwin();
4879         exit(0);
4880 }
4881
4882 static void __NORETURN
4883 die(const char *err, ...)
4884 {
4885         va_list args;
4886
4887         endwin();
4888
4889         va_start(args, err);
4890         fputs("tig: ", stderr);
4891         vfprintf(stderr, err, args);
4892         fputs("\n", stderr);
4893         va_end(args);
4894
4895         exit(1);
4896 }
4897
4898 int
4899 main(int argc, char *argv[])
4900 {
4901         struct view *view;
4902         enum request request;
4903         size_t i;
4904
4905         signal(SIGINT, quit);
4906
4907         if (setlocale(LC_ALL, "")) {
4908                 char *codeset = nl_langinfo(CODESET);
4909
4910                 string_ncopy(opt_codeset, codeset, strlen(codeset));
4911         }
4912
4913         if (load_repo_info() == ERR)
4914                 die("Failed to load repo info.");
4915
4916         if (load_options() == ERR)
4917                 die("Failed to load user config.");
4918
4919         /* Load the repo config file so options can be overwritten from
4920          * the command line. */
4921         if (load_repo_config() == ERR)
4922                 die("Failed to load repo config.");
4923
4924         if (!parse_options(argc, argv))
4925                 return 0;
4926
4927         /* Require a git repository unless when running in pager mode. */
4928         if (!opt_git_dir[0])
4929                 die("Not a git repository");
4930
4931         if (*opt_codeset && strcmp(opt_codeset, opt_encoding)) {
4932                 opt_iconv = iconv_open(opt_codeset, opt_encoding);
4933                 if (opt_iconv == ICONV_NONE)
4934                         die("Failed to initialize character set conversion");
4935         }
4936
4937         if (load_refs() == ERR)
4938                 die("Failed to load refs.");
4939
4940         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
4941                 view->cmd_env = getenv(view->cmd_env);
4942
4943         request = opt_request;
4944
4945         init_display();
4946
4947         while (view_driver(display[current_view], request)) {
4948                 int key;
4949                 int i;
4950
4951                 foreach_view (view, i)
4952                         update_view(view);
4953
4954                 /* Refresh, accept single keystroke of input */
4955                 key = wgetch(status_win);
4956
4957                 /* wgetch() with nodelay() enabled returns ERR when there's no
4958                  * input. */
4959                 if (key == ERR) {
4960                         request = REQ_NONE;
4961                         continue;
4962                 }
4963
4964                 request = get_keybinding(display[current_view]->keymap, key);
4965
4966                 /* Some low-level request handling. This keeps access to
4967                  * status_win restricted. */
4968                 switch (request) {
4969                 case REQ_PROMPT:
4970                 {
4971                         char *cmd = read_prompt(":");
4972
4973                         if (cmd && string_format(opt_cmd, "git %s", cmd)) {
4974                                 if (strncmp(cmd, "show", 4) && isspace(cmd[4])) {
4975                                         opt_request = REQ_VIEW_DIFF;
4976                                 } else {
4977                                         opt_request = REQ_VIEW_PAGER;
4978                                 }
4979                                 break;
4980                         }
4981
4982                         request = REQ_NONE;
4983                         break;
4984                 }
4985                 case REQ_SEARCH:
4986                 case REQ_SEARCH_BACK:
4987                 {
4988                         const char *prompt = request == REQ_SEARCH
4989                                            ? "/" : "?";
4990                         char *search = read_prompt(prompt);
4991
4992                         if (search)
4993                                 string_ncopy(opt_search, search, strlen(search));
4994                         else
4995                                 request = REQ_NONE;
4996                         break;
4997                 }
4998                 case REQ_SCREEN_RESIZE:
4999                 {
5000                         int height, width;
5001
5002                         getmaxyx(stdscr, height, width);
5003
5004                         /* Resize the status view and let the view driver take
5005                          * care of resizing the displayed views. */
5006                         wresize(status_win, 1, width);
5007                         mvwin(status_win, height - 1, 0);
5008                         wrefresh(status_win);
5009                         break;
5010                 }
5011                 default:
5012                         break;
5013                 }
5014         }
5015
5016         quit(0);
5017
5018         return 0;
5019 }