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