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