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