Reenable the log view from the command line
[tig.git] / tig.c
1 /* Copyright (c) 2006-2013 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 #include "tig.h"
15 #include "io.h"
16 #include "refs.h"
17 #include "graph.h"
18 #include "git.h"
19
20 static void TIG_NORETURN die(const char *err, ...) PRINTF_LIKE(1, 2);
21 static void warn(const char *msg, ...) PRINTF_LIKE(1, 2);
22 static void report(const char *msg, ...) PRINTF_LIKE(1, 2);
23 #define report_clear() report("%s", "")
24
25 static bool set_environment_variable(const char *name, const char *value);
26 static bool set_int_environment_variable(const char *name, int value);
27
28
29 enum input_status {
30         INPUT_OK,
31         INPUT_SKIP,
32         INPUT_STOP,
33         INPUT_CANCEL
34 };
35
36 typedef enum input_status (*input_handler)(void *data, char *buf, int c);
37
38 static char *prompt_input(const char *prompt, input_handler handler, void *data);
39 static bool prompt_yesno(const char *prompt);
40 static char *read_prompt(const char *prompt);
41
42 struct menu_item {
43         int hotkey;
44         const char *text;
45         void *data;
46 };
47
48 static bool prompt_menu(const char *prompt, const struct menu_item *items, int *selected);
49
50 #define GRAPHIC_ENUM(_) \
51         _(GRAPHIC, ASCII), \
52         _(GRAPHIC, DEFAULT), \
53         _(GRAPHIC, UTF_8)
54
55 DEFINE_ENUM(graphic, GRAPHIC_ENUM);
56
57 #define DATE_ENUM(_) \
58         _(DATE, NO), \
59         _(DATE, DEFAULT), \
60         _(DATE, LOCAL), \
61         _(DATE, RELATIVE), \
62         _(DATE, SHORT)
63
64 DEFINE_ENUM(date, DATE_ENUM);
65
66 struct time {
67         time_t sec;
68         int tz;
69 };
70
71 static inline int timecmp(const struct time *t1, const struct time *t2)
72 {
73         return t1->sec - t2->sec;
74 }
75
76 static const char *
77 mkdate(const struct time *time, enum date date)
78 {
79         static char buf[DATE_WIDTH + 1];
80         static const struct enum_map reldate[] = {
81                 { "second", 1,                  60 * 2 },
82                 { "minute", 60,                 60 * 60 * 2 },
83                 { "hour",   60 * 60,            60 * 60 * 24 * 2 },
84                 { "day",    60 * 60 * 24,       60 * 60 * 24 * 7 * 2 },
85                 { "week",   60 * 60 * 24 * 7,   60 * 60 * 24 * 7 * 5 },
86                 { "month",  60 * 60 * 24 * 30,  60 * 60 * 24 * 365 },
87                 { "year",   60 * 60 * 24 * 365, 0 },
88         };
89         struct tm tm;
90
91         if (!date || !time || !time->sec)
92                 return "";
93
94         if (date == DATE_RELATIVE) {
95                 struct timeval now;
96                 time_t date = time->sec + time->tz;
97                 time_t seconds;
98                 int i;
99
100                 gettimeofday(&now, NULL);
101                 seconds = now.tv_sec < date ? date - now.tv_sec : now.tv_sec - date;
102                 for (i = 0; i < ARRAY_SIZE(reldate); i++) {
103                         if (seconds >= reldate[i].value && reldate[i].value)
104                                 continue;
105
106                         seconds /= reldate[i].namelen;
107                         if (!string_format(buf, "%ld %s%s %s",
108                                            seconds, reldate[i].name,
109                                            seconds > 1 ? "s" : "",
110                                            now.tv_sec >= date ? "ago" : "ahead"))
111                                 break;
112                         return buf;
113                 }
114         }
115
116         if (date == DATE_LOCAL) {
117                 time_t date = time->sec + time->tz;
118                 localtime_r(&date, &tm);
119         }
120         else {
121                 gmtime_r(&time->sec, &tm);
122         }
123         return strftime(buf, sizeof(buf), DATE_FORMAT, &tm) ? buf : NULL;
124 }
125
126
127 #define AUTHOR_ENUM(_) \
128         _(AUTHOR, NO), \
129         _(AUTHOR, FULL), \
130         _(AUTHOR, ABBREVIATED), \
131         _(AUTHOR, EMAIL), \
132         _(AUTHOR, EMAIL_USER)
133
134 DEFINE_ENUM(author, AUTHOR_ENUM);
135
136 struct ident {
137         const char *name;
138         const char *email;
139 };
140
141 static const struct ident unknown_ident = { "Unknown", "unknown@localhost" };
142
143 static inline int
144 ident_compare(const struct ident *i1, const struct ident *i2)
145 {
146         if (!i1 || !i2)
147                 return (!!i1) - (!!i2);
148         if (!i1->name || !i2->name)
149                 return (!!i1->name) - (!!i2->name);
150         return strcmp(i1->name, i2->name);
151 }
152
153 static const char *
154 get_author_initials(const char *author)
155 {
156         static char initials[AUTHOR_WIDTH * 6 + 1];
157         size_t pos = 0;
158         const char *end = strchr(author, '\0');
159
160 #define is_initial_sep(c) (isspace(c) || ispunct(c) || (c) == '@' || (c) == '-')
161
162         memset(initials, 0, sizeof(initials));
163         while (author < end) {
164                 unsigned char bytes;
165                 size_t i;
166
167                 while (author < end && is_initial_sep(*author))
168                         author++;
169
170                 bytes = utf8_char_length(author, end);
171                 if (bytes >= sizeof(initials) - 1 - pos)
172                         break;
173                 while (bytes--) {
174                         initials[pos++] = *author++;
175                 }
176
177                 i = pos;
178                 while (author < end && !is_initial_sep(*author)) {
179                         bytes = utf8_char_length(author, end);
180                         if (bytes >= sizeof(initials) - 1 - i) {
181                                 while (author < end && !is_initial_sep(*author))
182                                         author++;
183                                 break;
184                         }
185                         while (bytes--) {
186                                 initials[i++] = *author++;
187                         }
188                 }
189
190                 initials[i++] = 0;
191         }
192
193         return initials;
194 }
195
196 static const char *
197 get_email_user(const char *email)
198 {
199         static char user[AUTHOR_WIDTH * 6 + 1];
200         const char *end = strchr(email, '@');
201         int length = end ? end - email : strlen(email);
202
203         string_format(user, "%.*s%c", length, email, 0);
204         return user;
205 }
206
207 #define author_trim(cols) (cols == 0 || cols > 10)
208
209 static const char *
210 mkauthor(const struct ident *ident, int cols, enum author author)
211 {
212         bool trim = author_trim(cols);
213         bool abbreviate = author == AUTHOR_ABBREVIATED || !trim;
214
215         if (author == AUTHOR_NO || !ident)
216                 return "";
217         if (author == AUTHOR_EMAIL && ident->email)
218                 return ident->email;
219         if (author == AUTHOR_EMAIL_USER && ident->email)
220                 return get_email_user(ident->email);
221         if (abbreviate && ident->name)
222                 return get_author_initials(ident->name);
223         return ident->name;
224 }
225
226 static const char *
227 mkmode(mode_t mode)
228 {
229         if (S_ISDIR(mode))
230                 return "drwxr-xr-x";
231         else if (S_ISLNK(mode))
232                 return "lrwxrwxrwx";
233         else if (S_ISGITLINK(mode))
234                 return "m---------";
235         else if (S_ISREG(mode) && mode & S_IXUSR)
236                 return "-rwxr-xr-x";
237         else if (S_ISREG(mode))
238                 return "-rw-r--r--";
239         else
240                 return "----------";
241 }
242
243 static char *
244 get_temp_dir(void)
245 {
246         static char *tmp;
247
248         if (tmp)
249                 return tmp;
250
251         if (!tmp)
252                 tmp = getenv("TMPDIR");
253         if (!tmp)
254                 tmp = getenv("TEMP");
255         if (!tmp)
256                 tmp = getenv("TMP");
257
258         if (tmp)
259                 tmp = strdup(tmp);
260         if (!tmp)
261                 tmp = "/tmp";
262
263         return tmp;
264 }
265
266 #define FILENAME_ENUM(_) \
267         _(FILENAME, NO), \
268         _(FILENAME, ALWAYS), \
269         _(FILENAME, AUTO)
270
271 DEFINE_ENUM(filename, FILENAME_ENUM);
272
273 #define IGNORE_SPACE_ENUM(_) \
274         _(IGNORE_SPACE, NO), \
275         _(IGNORE_SPACE, ALL), \
276         _(IGNORE_SPACE, SOME), \
277         _(IGNORE_SPACE, AT_EOL)
278
279 DEFINE_ENUM(ignore_space, IGNORE_SPACE_ENUM);
280
281 #define COMMIT_ORDER_ENUM(_) \
282         _(COMMIT_ORDER, DEFAULT), \
283         _(COMMIT_ORDER, TOPO), \
284         _(COMMIT_ORDER, DATE), \
285         _(COMMIT_ORDER, REVERSE)
286
287 DEFINE_ENUM(commit_order, COMMIT_ORDER_ENUM);
288
289 #define VIEW_INFO(_) \
290         _(MAIN,   main,   ref_head), \
291         _(DIFF,   diff,   ref_commit), \
292         _(LOG,    log,    ref_head), \
293         _(TREE,   tree,   ref_commit), \
294         _(BLOB,   blob,   ref_blob), \
295         _(BLAME,  blame,  ref_commit), \
296         _(BRANCH, branch, ref_head), \
297         _(HELP,   help,   ""), \
298         _(PAGER,  pager,  ""), \
299         _(STATUS, status, "status"), \
300         _(STAGE,  stage,  ref_status)
301
302 static struct encoding *
303 get_path_encoding(const char *path, struct encoding *default_encoding)
304 {
305         const char *check_attr_argv[] = {
306                 "git", "check-attr", "encoding", "--", path, NULL
307         };
308         char buf[SIZEOF_STR];
309         char *encoding;
310
311         /* <path>: encoding: <encoding> */
312
313         if (!*path || !io_run_buf(check_attr_argv, buf, sizeof(buf))
314             || !(encoding = strstr(buf, ENCODING_SEP)))
315                 return default_encoding;
316
317         encoding += STRING_SIZE(ENCODING_SEP);
318         if (!strcmp(encoding, ENCODING_UTF8)
319             || !strcmp(encoding, "unspecified")
320             || !strcmp(encoding, "set"))
321                 return default_encoding;
322
323         return encoding_open(encoding);
324 }
325
326 /*
327  * User requests
328  */
329
330 #define VIEW_REQ(id, name, ref) REQ_(VIEW_##id, "Show " #name " view")
331
332 #define REQ_INFO \
333         REQ_GROUP("View switching") \
334         VIEW_INFO(VIEW_REQ), \
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_(PARENT,            "Move to parent"), \
341         REQ_(VIEW_NEXT,         "Move focus to next view"), \
342         REQ_(REFRESH,           "Reload and refresh"), \
343         REQ_(MAXIMIZE,          "Maximize the current view"), \
344         REQ_(VIEW_CLOSE,        "Close the current view"), \
345         REQ_(QUIT,              "Close all views and quit"), \
346         \
347         REQ_GROUP("View specific requests") \
348         REQ_(STATUS_UPDATE,     "Update file status"), \
349         REQ_(STATUS_REVERT,     "Revert file changes"), \
350         REQ_(STATUS_MERGE,      "Merge file using external tool"), \
351         REQ_(STAGE_UPDATE_LINE, "Update single line"), \
352         REQ_(STAGE_NEXT,        "Find next chunk to stage"), \
353         REQ_(DIFF_CONTEXT_DOWN, "Decrease the diff context"), \
354         REQ_(DIFF_CONTEXT_UP,   "Increase the diff context"), \
355         \
356         REQ_GROUP("Cursor navigation") \
357         REQ_(MOVE_UP,           "Move cursor one line up"), \
358         REQ_(MOVE_DOWN,         "Move cursor one line down"), \
359         REQ_(MOVE_PAGE_DOWN,    "Move cursor one page down"), \
360         REQ_(MOVE_PAGE_UP,      "Move cursor one page up"), \
361         REQ_(MOVE_FIRST_LINE,   "Move cursor to first line"), \
362         REQ_(MOVE_LAST_LINE,    "Move cursor to last line"), \
363         \
364         REQ_GROUP("Scrolling") \
365         REQ_(SCROLL_FIRST_COL,  "Scroll to the first line columns"), \
366         REQ_(SCROLL_LEFT,       "Scroll two columns left"), \
367         REQ_(SCROLL_RIGHT,      "Scroll two columns right"), \
368         REQ_(SCROLL_LINE_UP,    "Scroll one line up"), \
369         REQ_(SCROLL_LINE_DOWN,  "Scroll one line down"), \
370         REQ_(SCROLL_PAGE_UP,    "Scroll one page up"), \
371         REQ_(SCROLL_PAGE_DOWN,  "Scroll one page down"), \
372         \
373         REQ_GROUP("Searching") \
374         REQ_(SEARCH,            "Search the view"), \
375         REQ_(SEARCH_BACK,       "Search backwards in the view"), \
376         REQ_(FIND_NEXT,         "Find next search match"), \
377         REQ_(FIND_PREV,         "Find previous search match"), \
378         \
379         REQ_GROUP("Option manipulation") \
380         REQ_(OPTIONS,           "Open option menu"), \
381         REQ_(TOGGLE_LINENO,     "Toggle line numbers"), \
382         REQ_(TOGGLE_DATE,       "Toggle date display"), \
383         REQ_(TOGGLE_AUTHOR,     "Toggle author display"), \
384         REQ_(TOGGLE_REV_GRAPH,  "Toggle revision graph visualization"), \
385         REQ_(TOGGLE_GRAPHIC,    "Toggle (line) graphics mode"), \
386         REQ_(TOGGLE_FILENAME,   "Toggle file name display"), \
387         REQ_(TOGGLE_REFS,       "Toggle reference display (tags/branches)"), \
388         REQ_(TOGGLE_CHANGES,    "Toggle local changes display in the main view"), \
389         REQ_(TOGGLE_SORT_ORDER, "Toggle ascending/descending sort order"), \
390         REQ_(TOGGLE_SORT_FIELD, "Toggle field to sort by"), \
391         REQ_(TOGGLE_IGNORE_SPACE,       "Toggle ignoring whitespace in diffs"), \
392         REQ_(TOGGLE_COMMIT_ORDER,       "Toggle commit ordering"), \
393         REQ_(TOGGLE_ID,         "Toggle commit ID display"), \
394         REQ_(TOGGLE_FILES,      "Toggle file filtering"), \
395         REQ_(TOGGLE_TITLE_OVERFLOW,     "Toggle highlighting of commit title overflow"), \
396         \
397         REQ_GROUP("Misc") \
398         REQ_(PROMPT,            "Bring up the prompt"), \
399         REQ_(SCREEN_REDRAW,     "Redraw the screen"), \
400         REQ_(SHOW_VERSION,      "Show version information"), \
401         REQ_(STOP_LOADING,      "Stop all loading views"), \
402         REQ_(EDIT,              "Open in editor"), \
403         REQ_(NONE,              "Do nothing")
404
405
406 /* User action requests. */
407 enum request {
408 #define REQ_GROUP(help)
409 #define REQ_(req, help) REQ_##req
410
411         /* Offset all requests to avoid conflicts with ncurses getch values. */
412         REQ_UNKNOWN = KEY_MAX + 1,
413         REQ_OFFSET,
414         REQ_INFO,
415
416         /* Internal requests. */
417         REQ_JUMP_COMMIT,
418
419 #undef  REQ_GROUP
420 #undef  REQ_
421 };
422
423 struct request_info {
424         enum request request;
425         const char *name;
426         int namelen;
427         const char *help;
428 };
429
430 static const struct request_info req_info[] = {
431 #define REQ_GROUP(help) { 0, NULL, 0, (help) },
432 #define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) }
433         REQ_INFO
434 #undef  REQ_GROUP
435 #undef  REQ_
436 };
437
438 static enum request
439 get_request(const char *name)
440 {
441         int namelen = strlen(name);
442         int i;
443
444         for (i = 0; i < ARRAY_SIZE(req_info); i++)
445                 if (enum_equals(req_info[i], name, namelen))
446                         return req_info[i].request;
447
448         return REQ_UNKNOWN;
449 }
450
451
452 /*
453  * Options
454  */
455
456 /* Option and state variables. */
457 static enum graphic opt_line_graphics   = GRAPHIC_DEFAULT;
458 static enum date opt_date               = DATE_DEFAULT;
459 static enum author opt_author           = AUTHOR_FULL;
460 static enum filename opt_filename       = FILENAME_AUTO;
461 static bool opt_rev_graph               = TRUE;
462 static bool opt_line_number             = FALSE;
463 static bool opt_show_refs               = TRUE;
464 static bool opt_show_changes            = TRUE;
465 static bool opt_untracked_dirs_content  = TRUE;
466 static bool opt_read_git_colors         = TRUE;
467 static bool opt_wrap_lines              = FALSE;
468 static bool opt_ignore_case             = FALSE;
469 static bool opt_stdin                   = FALSE;
470 static bool opt_focus_child             = TRUE;
471 static int opt_diff_context             = 3;
472 static char opt_diff_context_arg[9]     = "";
473 static enum ignore_space opt_ignore_space       = IGNORE_SPACE_NO;
474 static char opt_ignore_space_arg[22]    = "";
475 static enum commit_order opt_commit_order       = COMMIT_ORDER_DEFAULT;
476 static char opt_commit_order_arg[22]    = "";
477 static bool opt_notes                   = TRUE;
478 static char opt_notes_arg[SIZEOF_STR]   = "--show-notes";
479 static int opt_num_interval             = 5;
480 static double opt_hscroll               = 0.50;
481 static double opt_scale_split_view      = 2.0 / 3.0;
482 static double opt_scale_vsplit_view     = 0.5;
483 static bool opt_vsplit                  = FALSE;
484 static int opt_tab_size                 = 8;
485 static int opt_author_width             = AUTHOR_WIDTH;
486 static int opt_filename_width           = FILENAME_WIDTH;
487 static char opt_path[SIZEOF_STR]        = "";
488 static char opt_file[SIZEOF_STR]        = "";
489 static char opt_ref[SIZEOF_REF]         = "";
490 static unsigned long opt_goto_line      = 0;
491 static char opt_head[SIZEOF_REF]        = "";
492 static char opt_remote[SIZEOF_REF]      = "";
493 static struct encoding *opt_encoding    = NULL;
494 static char opt_encoding_arg[SIZEOF_STR]        = ENCODING_ARG;
495 static iconv_t opt_iconv_out            = ICONV_NONE;
496 static char opt_search[SIZEOF_STR]      = "";
497 static char opt_cdup[SIZEOF_STR]        = "";
498 static char opt_prefix[SIZEOF_STR]      = "";
499 static char opt_git_dir[SIZEOF_STR]     = "";
500 static signed char opt_is_inside_work_tree      = -1; /* set to TRUE or FALSE */
501 static char opt_editor[SIZEOF_STR]      = "";
502 static bool opt_editor_lineno           = TRUE;
503 static FILE *opt_tty                    = NULL;
504 static const char **opt_diff_argv       = NULL;
505 static const char **opt_rev_argv        = NULL;
506 static const char **opt_file_argv       = NULL;
507 static const char **opt_blame_argv      = NULL;
508 static int opt_lineno                   = 0;
509 static bool opt_show_id                 = FALSE;
510 static int opt_id_cols                  = ID_WIDTH;
511 static bool opt_file_filter             = TRUE;
512 static bool opt_show_title_overflow     = FALSE;
513 static int opt_title_overflow           = 50;
514
515 #define is_initial_commit()     (!get_ref_head())
516 #define is_head_commit(rev)     (!strcmp((rev), "HEAD") || (get_ref_head() && !strncmp(rev, get_ref_head()->id, SIZEOF_REV - 1)))
517 #define load_refs()             reload_refs(opt_git_dir, opt_remote, opt_head, sizeof(opt_head))
518
519 static inline void
520 update_diff_context_arg(int diff_context)
521 {
522         if (!string_format(opt_diff_context_arg, "-U%u", diff_context))
523                 string_ncopy(opt_diff_context_arg, "-U3", 3);
524 }
525
526 static inline void
527 update_ignore_space_arg()
528 {
529         if (opt_ignore_space == IGNORE_SPACE_ALL) {
530                 string_copy(opt_ignore_space_arg, "--ignore-all-space");
531         } else if (opt_ignore_space == IGNORE_SPACE_SOME) {
532                 string_copy(opt_ignore_space_arg, "--ignore-space-change");
533         } else if (opt_ignore_space == IGNORE_SPACE_AT_EOL) {
534                 string_copy(opt_ignore_space_arg, "--ignore-space-at-eol");
535         } else {
536                 string_copy(opt_ignore_space_arg, "");
537         }
538 }
539
540 static inline void
541 update_commit_order_arg()
542 {
543         if (opt_commit_order == COMMIT_ORDER_TOPO) {
544                 string_copy(opt_commit_order_arg, "--topo-order");
545         } else if (opt_commit_order == COMMIT_ORDER_DATE) {
546                 string_copy(opt_commit_order_arg, "--date-order");
547         } else if (opt_commit_order == COMMIT_ORDER_REVERSE) {
548                 string_copy(opt_commit_order_arg, "--reverse");
549         } else {
550                 string_copy(opt_commit_order_arg, "");
551         }
552 }
553
554 static inline void
555 update_notes_arg()
556 {
557         if (opt_notes) {
558                 string_copy(opt_notes_arg, "--show-notes");
559         } else {
560                 /* Notes are disabled by default when passing --pretty args. */
561                 string_copy(opt_notes_arg, "");
562         }
563 }
564
565 /*
566  * Line-oriented content detection.
567  */
568
569 #define LINE_INFO \
570 LINE(DIFF_HEADER,  "diff --",   COLOR_YELLOW,   COLOR_DEFAULT,  0), \
571 LINE(DIFF_CHUNK,   "@@",                COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
572 LINE(DIFF_ADD,     "+",                 COLOR_GREEN,    COLOR_DEFAULT,  0), \
573 LINE(DIFF_ADD2,    " +",                COLOR_GREEN,    COLOR_DEFAULT,  0), \
574 LINE(DIFF_DEL,     "-",                 COLOR_RED,      COLOR_DEFAULT,  0), \
575 LINE(DIFF_DEL2,    " -",                COLOR_RED,      COLOR_DEFAULT,  0), \
576 LINE(DIFF_INDEX,        "index ",         COLOR_BLUE,   COLOR_DEFAULT,  0), \
577 LINE(DIFF_OLDMODE,      "old file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
578 LINE(DIFF_NEWMODE,      "new file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
579 LINE(DIFF_DELETED_FILE_MODE, \
580                     "deleted file mode ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
581 LINE(DIFF_COPY_FROM,    "copy from ",     COLOR_YELLOW, COLOR_DEFAULT,  0), \
582 LINE(DIFF_COPY_TO,      "copy to ",       COLOR_YELLOW, COLOR_DEFAULT,  0), \
583 LINE(DIFF_RENAME_FROM,  "rename from ",   COLOR_YELLOW, COLOR_DEFAULT,  0), \
584 LINE(DIFF_RENAME_TO,    "rename to ",     COLOR_YELLOW, COLOR_DEFAULT,  0), \
585 LINE(DIFF_SIMILARITY,   "similarity ",    COLOR_YELLOW, COLOR_DEFAULT,  0), \
586 LINE(DIFF_DISSIMILARITY,"dissimilarity ", COLOR_YELLOW, COLOR_DEFAULT,  0), \
587 LINE(DIFF_TREE,         "diff-tree ",     COLOR_BLUE,   COLOR_DEFAULT,  0), \
588 LINE(PP_AUTHOR,    "Author: ",          COLOR_CYAN,     COLOR_DEFAULT,  0), \
589 LINE(PP_COMMIT,    "Commit: ",          COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
590 LINE(PP_MERGE,     "Merge: ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
591 LINE(PP_DATE,      "Date:   ",          COLOR_YELLOW,   COLOR_DEFAULT,  0), \
592 LINE(PP_ADATE,     "AuthorDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
593 LINE(PP_CDATE,     "CommitDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
594 LINE(PP_REFS,      "Refs: ",            COLOR_RED,      COLOR_DEFAULT,  0), \
595 LINE(COMMIT,       "commit ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
596 LINE(PARENT,       "parent ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
597 LINE(TREE,         "tree ",             COLOR_BLUE,     COLOR_DEFAULT,  0), \
598 LINE(AUTHOR,       "author ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
599 LINE(COMMITTER,    "committer ",        COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
600 LINE(SIGNOFF,      "    Signed-off-by", COLOR_YELLOW,   COLOR_DEFAULT,  0), \
601 LINE(ACKED,        "    Acked-by",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
602 LINE(TESTED,       "    Tested-by",     COLOR_YELLOW,   COLOR_DEFAULT,  0), \
603 LINE(REVIEWED,     "    Reviewed-by",   COLOR_YELLOW,   COLOR_DEFAULT,  0), \
604 LINE(DEFAULT,      "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
605 LINE(CURSOR,       "",                  COLOR_WHITE,    COLOR_GREEN,    A_BOLD), \
606 LINE(STATUS,       "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
607 LINE(DELIMITER,    "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
608 LINE(DATE,         "",                  COLOR_BLUE,     COLOR_DEFAULT,  0), \
609 LINE(MODE,         "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
610 LINE(ID,           "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
611 LINE(OVERFLOW,     "",                  COLOR_RED,      COLOR_DEFAULT,  0), \
612 LINE(FILENAME,     "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
613 LINE(LINE_NUMBER,  "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
614 LINE(TITLE_BLUR,   "",                  COLOR_WHITE,    COLOR_BLUE,     0), \
615 LINE(TITLE_FOCUS,  "",                  COLOR_WHITE,    COLOR_BLUE,     A_BOLD), \
616 LINE(MAIN_COMMIT,  "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
617 LINE(MAIN_TAG,     "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
618 LINE(MAIN_LOCAL_TAG,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
619 LINE(MAIN_REMOTE,  "",                  COLOR_YELLOW,   COLOR_DEFAULT,  0), \
620 LINE(MAIN_REPLACE, "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
621 LINE(MAIN_TRACKED, "",                  COLOR_YELLOW,   COLOR_DEFAULT,  A_BOLD), \
622 LINE(MAIN_REF,     "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
623 LINE(MAIN_HEAD,    "",                  COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD), \
624 LINE(MAIN_REVGRAPH,"",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
625 LINE(TREE_HEAD,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_BOLD), \
626 LINE(TREE_DIR,     "",                  COLOR_YELLOW,   COLOR_DEFAULT,  A_NORMAL), \
627 LINE(TREE_FILE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
628 LINE(STAT_HEAD,    "",                  COLOR_YELLOW,   COLOR_DEFAULT,  0), \
629 LINE(STAT_SECTION, "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
630 LINE(STAT_NONE,    "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
631 LINE(STAT_STAGED,  "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
632 LINE(STAT_UNSTAGED,"",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
633 LINE(STAT_UNTRACKED,"",                 COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
634 LINE(HELP_KEYMAP,  "",                  COLOR_CYAN,     COLOR_DEFAULT,  0), \
635 LINE(HELP_GROUP,   "",                  COLOR_BLUE,     COLOR_DEFAULT,  0), \
636 LINE(DIFF_STAT,         "",             COLOR_BLUE,     COLOR_DEFAULT,  0), \
637 LINE(PALETTE_0, "",                     COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
638 LINE(PALETTE_1, "",                     COLOR_YELLOW,   COLOR_DEFAULT,  0), \
639 LINE(PALETTE_2, "",                     COLOR_CYAN,     COLOR_DEFAULT,  0), \
640 LINE(PALETTE_3, "",                     COLOR_GREEN,    COLOR_DEFAULT,  0), \
641 LINE(PALETTE_4, "",                     COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
642 LINE(PALETTE_5, "",                     COLOR_WHITE,    COLOR_DEFAULT,  0), \
643 LINE(PALETTE_6, "",                     COLOR_RED,      COLOR_DEFAULT,  0), \
644 LINE(GRAPH_COMMIT, "",                  COLOR_BLUE,     COLOR_DEFAULT,  0)
645
646 enum line_type {
647 #define LINE(type, line, fg, bg, attr) \
648         LINE_##type
649         LINE_INFO,
650         LINE_NONE
651 #undef  LINE
652 };
653
654 struct line_info {
655         const char *name;       /* Option name. */
656         int namelen;            /* Size of option name. */
657         const char *line;       /* The start of line to match. */
658         int linelen;            /* Size of string to match. */
659         int fg, bg, attr;       /* Color and text attributes for the lines. */
660         int color_pair;
661 };
662
663 static struct line_info line_info[] = {
664 #define LINE(type, line, fg, bg, attr) \
665         { #type, STRING_SIZE(#type), (line), STRING_SIZE(line), (fg), (bg), (attr) }
666         LINE_INFO
667 #undef  LINE
668 };
669
670 static struct line_info **color_pair;
671 static size_t color_pairs;
672
673 static struct line_info *custom_color;
674 static size_t custom_colors;
675
676 DEFINE_ALLOCATOR(realloc_custom_color, struct line_info, 8)
677 DEFINE_ALLOCATOR(realloc_color_pair, struct line_info *, 8)
678
679 #define TO_CUSTOM_COLOR_TYPE(type)      (LINE_NONE + 1 + (type))
680 #define TO_CUSTOM_COLOR_OFFSET(type)    ((type) - LINE_NONE - 1)
681
682 /* Color IDs must be 1 or higher. [GH #15] */
683 #define COLOR_ID(line_type)             ((line_type) + 1)
684
685 static enum line_type
686 get_line_type(const char *line)
687 {
688         int linelen = strlen(line);
689         enum line_type type;
690
691         for (type = 0; type < custom_colors; type++)
692                 /* Case insensitive search matches Signed-off-by lines better. */
693                 if (linelen >= custom_color[type].linelen &&
694                     !strncasecmp(custom_color[type].line, line, custom_color[type].linelen))
695                         return TO_CUSTOM_COLOR_TYPE(type);
696
697         for (type = 0; type < ARRAY_SIZE(line_info); type++)
698                 /* Case insensitive search matches Signed-off-by lines better. */
699                 if (linelen >= line_info[type].linelen &&
700                     !strncasecmp(line_info[type].line, line, line_info[type].linelen))
701                         return type;
702
703         return LINE_DEFAULT;
704 }
705
706 static enum line_type
707 get_line_type_from_ref(const struct ref *ref)
708 {
709         if (ref->head)
710                 return LINE_MAIN_HEAD;
711         else if (ref->ltag)
712                 return LINE_MAIN_LOCAL_TAG;
713         else if (ref->tag)
714                 return LINE_MAIN_TAG;
715         else if (ref->tracked)
716                 return LINE_MAIN_TRACKED;
717         else if (ref->remote)
718                 return LINE_MAIN_REMOTE;
719         else if (ref->replace)
720                 return LINE_MAIN_REPLACE;
721
722         return LINE_MAIN_REF;
723 }
724
725 static inline struct line_info *
726 get_line(enum line_type type)
727 {
728         if (type > LINE_NONE) {
729                 assert(TO_CUSTOM_COLOR_OFFSET(type) < custom_colors);
730                 return &custom_color[TO_CUSTOM_COLOR_OFFSET(type)];
731         } else {
732                 assert(type < ARRAY_SIZE(line_info));
733                 return &line_info[type];
734         }
735 }
736
737 static inline int
738 get_line_color(enum line_type type)
739 {
740         return COLOR_ID(get_line(type)->color_pair);
741 }
742
743 static inline int
744 get_line_attr(enum line_type type)
745 {
746         struct line_info *info = get_line(type);
747
748         return COLOR_PAIR(COLOR_ID(info->color_pair)) | info->attr;
749 }
750
751 static struct line_info *
752 get_line_info(const char *name)
753 {
754         size_t namelen = strlen(name);
755         enum line_type type;
756
757         for (type = 0; type < ARRAY_SIZE(line_info); type++)
758                 if (enum_equals(line_info[type], name, namelen))
759                         return &line_info[type];
760
761         return NULL;
762 }
763
764 static struct line_info *
765 add_custom_color(const char *quoted_line)
766 {
767         struct line_info *info;
768         char *line;
769         size_t linelen;
770
771         if (!realloc_custom_color(&custom_color, custom_colors, 1))
772                 die("Failed to alloc custom line info");
773
774         linelen = strlen(quoted_line) - 1;
775         line = malloc(linelen);
776         if (!line)
777                 return NULL;
778
779         strncpy(line, quoted_line + 1, linelen);
780         line[linelen - 1] = 0;
781
782         info = &custom_color[custom_colors++];
783         info->name = info->line = line;
784         info->namelen = info->linelen = strlen(line);
785
786         return info;
787 }
788
789 static void
790 init_line_info_color_pair(struct line_info *info, enum line_type type,
791         int default_bg, int default_fg)
792 {
793         int bg = info->bg == COLOR_DEFAULT ? default_bg : info->bg;
794         int fg = info->fg == COLOR_DEFAULT ? default_fg : info->fg;
795         int i;
796
797         for (i = 0; i < color_pairs; i++) {
798                 if (color_pair[i]->fg == info->fg && color_pair[i]->bg == info->bg) {
799                         info->color_pair = i;
800                         return;
801                 }
802         }
803
804         if (!realloc_color_pair(&color_pair, color_pairs, 1))
805                 die("Failed to alloc color pair");
806
807         color_pair[color_pairs] = info;
808         info->color_pair = color_pairs++;
809         init_pair(COLOR_ID(info->color_pair), fg, bg);
810 }
811
812 static void
813 init_colors(void)
814 {
815         int default_bg = line_info[LINE_DEFAULT].bg;
816         int default_fg = line_info[LINE_DEFAULT].fg;
817         enum line_type type;
818
819         start_color();
820
821         if (assume_default_colors(default_fg, default_bg) == ERR) {
822                 default_bg = COLOR_BLACK;
823                 default_fg = COLOR_WHITE;
824         }
825
826         for (type = 0; type < ARRAY_SIZE(line_info); type++) {
827                 struct line_info *info = &line_info[type];
828
829                 init_line_info_color_pair(info, type, default_bg, default_fg);
830         }
831
832         for (type = 0; type < custom_colors; type++) {
833                 struct line_info *info = &custom_color[type];
834
835                 init_line_info_color_pair(info, TO_CUSTOM_COLOR_TYPE(type),
836                                           default_bg, default_fg);
837         }
838 }
839
840 struct line {
841         enum line_type type;
842         unsigned int lineno:24;
843
844         /* State flags */
845         unsigned int selected:1;
846         unsigned int dirty:1;
847         unsigned int cleareol:1;
848         unsigned int dont_free:1;
849         unsigned int wrapped:1;
850
851         unsigned int user_flags:6;
852         void *data;             /* User data */
853 };
854
855
856 /*
857  * Keys
858  */
859
860 struct keybinding {
861         int alias;
862         enum request request;
863 };
864
865 static struct keybinding default_keybindings[] = {
866         /* View switching */
867         { 'm',          REQ_VIEW_MAIN },
868         { 'd',          REQ_VIEW_DIFF },
869         { 'l',          REQ_VIEW_LOG },
870         { 't',          REQ_VIEW_TREE },
871         { 'f',          REQ_VIEW_BLOB },
872         { 'B',          REQ_VIEW_BLAME },
873         { 'H',          REQ_VIEW_BRANCH },
874         { 'p',          REQ_VIEW_PAGER },
875         { 'h',          REQ_VIEW_HELP },
876         { 'S',          REQ_VIEW_STATUS },
877         { 'c',          REQ_VIEW_STAGE },
878
879         /* View manipulation */
880         { 'q',          REQ_VIEW_CLOSE },
881         { KEY_TAB,      REQ_VIEW_NEXT },
882         { KEY_RETURN,   REQ_ENTER },
883         { KEY_UP,       REQ_PREVIOUS },
884         { KEY_CTL('P'), REQ_PREVIOUS },
885         { KEY_DOWN,     REQ_NEXT },
886         { KEY_CTL('N'), REQ_NEXT },
887         { 'R',          REQ_REFRESH },
888         { KEY_F(5),     REQ_REFRESH },
889         { 'O',          REQ_MAXIMIZE },
890         { ',',          REQ_PARENT },
891
892         /* View specific */
893         { 'u',          REQ_STATUS_UPDATE },
894         { '!',          REQ_STATUS_REVERT },
895         { 'M',          REQ_STATUS_MERGE },
896         { '1',          REQ_STAGE_UPDATE_LINE },
897         { '@',          REQ_STAGE_NEXT },
898         { '[',          REQ_DIFF_CONTEXT_DOWN },
899         { ']',          REQ_DIFF_CONTEXT_UP },
900
901         /* Cursor navigation */
902         { 'k',          REQ_MOVE_UP },
903         { 'j',          REQ_MOVE_DOWN },
904         { KEY_HOME,     REQ_MOVE_FIRST_LINE },
905         { KEY_END,      REQ_MOVE_LAST_LINE },
906         { KEY_NPAGE,    REQ_MOVE_PAGE_DOWN },
907         { KEY_CTL('D'), REQ_MOVE_PAGE_DOWN },
908         { ' ',          REQ_MOVE_PAGE_DOWN },
909         { KEY_PPAGE,    REQ_MOVE_PAGE_UP },
910         { KEY_CTL('U'), REQ_MOVE_PAGE_UP },
911         { 'b',          REQ_MOVE_PAGE_UP },
912         { '-',          REQ_MOVE_PAGE_UP },
913
914         /* Scrolling */
915         { '|',          REQ_SCROLL_FIRST_COL },
916         { KEY_LEFT,     REQ_SCROLL_LEFT },
917         { KEY_RIGHT,    REQ_SCROLL_RIGHT },
918         { KEY_IC,       REQ_SCROLL_LINE_UP },
919         { KEY_CTL('Y'), REQ_SCROLL_LINE_UP },
920         { KEY_DC,       REQ_SCROLL_LINE_DOWN },
921         { KEY_CTL('E'), REQ_SCROLL_LINE_DOWN },
922         { 'w',          REQ_SCROLL_PAGE_UP },
923         { 's',          REQ_SCROLL_PAGE_DOWN },
924
925         /* Searching */
926         { '/',          REQ_SEARCH },
927         { '?',          REQ_SEARCH_BACK },
928         { 'n',          REQ_FIND_NEXT },
929         { 'N',          REQ_FIND_PREV },
930
931         /* Misc */
932         { 'Q',          REQ_QUIT },
933         { 'z',          REQ_STOP_LOADING },
934         { 'v',          REQ_SHOW_VERSION },
935         { 'r',          REQ_SCREEN_REDRAW },
936         { KEY_CTL('L'), REQ_SCREEN_REDRAW },
937         { 'o',          REQ_OPTIONS },
938         { '.',          REQ_TOGGLE_LINENO },
939         { 'D',          REQ_TOGGLE_DATE },
940         { 'A',          REQ_TOGGLE_AUTHOR },
941         { 'g',          REQ_TOGGLE_REV_GRAPH },
942         { '~',          REQ_TOGGLE_GRAPHIC },
943         { '#',          REQ_TOGGLE_FILENAME },
944         { 'F',          REQ_TOGGLE_REFS },
945         { 'I',          REQ_TOGGLE_SORT_ORDER },
946         { 'i',          REQ_TOGGLE_SORT_FIELD },
947         { 'W',          REQ_TOGGLE_IGNORE_SPACE },
948         { 'X',          REQ_TOGGLE_ID },
949         { '%',          REQ_TOGGLE_FILES },
950         { '$',          REQ_TOGGLE_TITLE_OVERFLOW },
951         { ':',          REQ_PROMPT },
952         { 'e',          REQ_EDIT },
953 };
954
955 struct keymap {
956         const char *name;
957         struct keymap *next;
958         struct keybinding *data;
959         size_t size;
960         bool hidden;
961 };
962
963 static struct keymap generic_keymap = { "generic" };
964 #define is_generic_keymap(keymap) ((keymap) == &generic_keymap)
965
966 static struct keymap *keymaps = &generic_keymap;
967
968 static void
969 add_keymap(struct keymap *keymap)
970 {
971         keymap->next = keymaps;
972         keymaps = keymap;
973 }
974
975 static struct keymap *
976 get_keymap(const char *name)
977 {
978         struct keymap *keymap = keymaps;
979
980         while (keymap) {
981                 if (!strcasecmp(keymap->name, name))
982                         return keymap;
983                 keymap = keymap->next;
984         }
985
986         return NULL;
987 }
988
989
990 static void
991 add_keybinding(struct keymap *table, enum request request, int key)
992 {
993         size_t i;
994
995         for (i = 0; i < table->size; i++) {
996                 if (table->data[i].alias == key) {
997                         table->data[i].request = request;
998                         return;
999                 }
1000         }
1001
1002         table->data = realloc(table->data, (table->size + 1) * sizeof(*table->data));
1003         if (!table->data)
1004                 die("Failed to allocate keybinding");
1005         table->data[table->size].alias = key;
1006         table->data[table->size++].request = request;
1007
1008         if (request == REQ_NONE && is_generic_keymap(table)) {
1009                 int i;
1010
1011                 for (i = 0; i < ARRAY_SIZE(default_keybindings); i++)
1012                         if (default_keybindings[i].alias == key)
1013                                 default_keybindings[i].request = REQ_NONE;
1014         }
1015 }
1016
1017 /* Looks for a key binding first in the given map, then in the generic map, and
1018  * lastly in the default keybindings. */
1019 static enum request
1020 get_keybinding(struct keymap *keymap, int key)
1021 {
1022         size_t i;
1023
1024         for (i = 0; i < keymap->size; i++)
1025                 if (keymap->data[i].alias == key)
1026                         return keymap->data[i].request;
1027
1028         for (i = 0; i < generic_keymap.size; i++)
1029                 if (generic_keymap.data[i].alias == key)
1030                         return generic_keymap.data[i].request;
1031
1032         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++)
1033                 if (default_keybindings[i].alias == key)
1034                         return default_keybindings[i].request;
1035
1036         return (enum request) key;
1037 }
1038
1039
1040 struct key {
1041         const char *name;
1042         int value;
1043 };
1044
1045 static const struct key key_table[] = {
1046         { "Enter",      KEY_RETURN },
1047         { "Space",      ' ' },
1048         { "Backspace",  KEY_BACKSPACE },
1049         { "Tab",        KEY_TAB },
1050         { "Escape",     KEY_ESC },
1051         { "Left",       KEY_LEFT },
1052         { "Right",      KEY_RIGHT },
1053         { "Up",         KEY_UP },
1054         { "Down",       KEY_DOWN },
1055         { "Insert",     KEY_IC },
1056         { "Delete",     KEY_DC },
1057         { "Hash",       '#' },
1058         { "Home",       KEY_HOME },
1059         { "End",        KEY_END },
1060         { "PageUp",     KEY_PPAGE },
1061         { "PageDown",   KEY_NPAGE },
1062         { "F1",         KEY_F(1) },
1063         { "F2",         KEY_F(2) },
1064         { "F3",         KEY_F(3) },
1065         { "F4",         KEY_F(4) },
1066         { "F5",         KEY_F(5) },
1067         { "F6",         KEY_F(6) },
1068         { "F7",         KEY_F(7) },
1069         { "F8",         KEY_F(8) },
1070         { "F9",         KEY_F(9) },
1071         { "F10",        KEY_F(10) },
1072         { "F11",        KEY_F(11) },
1073         { "F12",        KEY_F(12) },
1074 };
1075
1076 static int
1077 get_key_value(const char *name)
1078 {
1079         int i;
1080
1081         for (i = 0; i < ARRAY_SIZE(key_table); i++)
1082                 if (!strcasecmp(key_table[i].name, name))
1083                         return key_table[i].value;
1084
1085         if (strlen(name) == 3 && name[0] == '^' && name[1] == '[' && isprint(*name))
1086                 return (int)name[2] + 0x80;
1087         if (strlen(name) == 2 && name[0] == '^' && isprint(*name))
1088                 return (int)name[1] & 0x1f;
1089         if (strlen(name) == 1 && isprint(*name))
1090                 return (int) *name;
1091         return ERR;
1092 }
1093
1094 static const char *
1095 get_key_name(int key_value)
1096 {
1097         static char key_char[] = "'X'\0";
1098         const char *seq = NULL;
1099         int key;
1100
1101         for (key = 0; key < ARRAY_SIZE(key_table); key++)
1102                 if (key_table[key].value == key_value)
1103                         seq = key_table[key].name;
1104
1105         if (seq == NULL && key_value < 0x7f) {
1106                 char *s = key_char + 1;
1107
1108                 if (key_value >= 0x20) {
1109                         *s++ = key_value;
1110                 } else {
1111                         *s++ = '^';
1112                         *s++ = 0x40 | (key_value & 0x1f);
1113                 }
1114                 *s++ = '\'';
1115                 *s++ = '\0';
1116                 seq = key_char;
1117         }
1118
1119         return seq ? seq : "(no key)";
1120 }
1121
1122 static bool
1123 append_key(char *buf, size_t *pos, const struct keybinding *keybinding)
1124 {
1125         const char *sep = *pos > 0 ? ", " : "";
1126         const char *keyname = get_key_name(keybinding->alias);
1127
1128         return string_nformat(buf, BUFSIZ, pos, "%s%s", sep, keyname);
1129 }
1130
1131 static bool
1132 append_keymap_request_keys(char *buf, size_t *pos, enum request request,
1133                            struct keymap *keymap, bool all)
1134 {
1135         int i;
1136
1137         for (i = 0; i < keymap->size; i++) {
1138                 if (keymap->data[i].request == request) {
1139                         if (!append_key(buf, pos, &keymap->data[i]))
1140                                 return FALSE;
1141                         if (!all)
1142                                 break;
1143                 }
1144         }
1145
1146         return TRUE;
1147 }
1148
1149 #define get_view_key(view, request) get_keys(&(view)->ops->keymap, request, FALSE)
1150
1151 static const char *
1152 get_keys(struct keymap *keymap, enum request request, bool all)
1153 {
1154         static char buf[BUFSIZ];
1155         size_t pos = 0;
1156         int i;
1157
1158         buf[pos] = 0;
1159
1160         if (!append_keymap_request_keys(buf, &pos, request, keymap, all))
1161                 return "Too many keybindings!";
1162         if (pos > 0 && !all)
1163                 return buf;
1164
1165         if (!is_generic_keymap(keymap)) {
1166                 /* Only the generic keymap includes the default keybindings when
1167                  * listing all keys. */
1168                 if (all)
1169                         return buf;
1170
1171                 if (!append_keymap_request_keys(buf, &pos, request, &generic_keymap, all))
1172                         return "Too many keybindings!";
1173                 if (pos)
1174                         return buf;
1175         }
1176
1177         for (i = 0; i < ARRAY_SIZE(default_keybindings); i++) {
1178                 if (default_keybindings[i].request == request) {
1179                         if (!append_key(buf, &pos, &default_keybindings[i]))
1180                                 return "Too many keybindings!";
1181                         if (!all)
1182                                 return buf;
1183                 }
1184         }
1185
1186         return buf;
1187 }
1188
1189 enum run_request_flag {
1190         RUN_REQUEST_DEFAULT     = 0,
1191         RUN_REQUEST_FORCE       = 1,
1192         RUN_REQUEST_SILENT      = 2,
1193         RUN_REQUEST_CONFIRM     = 4,
1194         RUN_REQUEST_EXIT        = 8,
1195         RUN_REQUEST_INTERNAL    = 16,
1196 };
1197
1198 struct run_request {
1199         struct keymap *keymap;
1200         int key;
1201         const char **argv;
1202         bool silent;
1203         bool confirm;
1204         bool exit;
1205         bool internal;
1206 };
1207
1208 static struct run_request *run_request;
1209 static size_t run_requests;
1210
1211 DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8)
1212
1213 static bool
1214 add_run_request(struct keymap *keymap, int key, const char **argv, enum run_request_flag flags)
1215 {
1216         bool force = flags & RUN_REQUEST_FORCE;
1217         struct run_request *req;
1218
1219         if (!force && get_keybinding(keymap, key) != key)
1220                 return TRUE;
1221
1222         if (!realloc_run_requests(&run_request, run_requests, 1))
1223                 return FALSE;
1224
1225         if (!argv_copy(&run_request[run_requests].argv, argv))
1226                 return FALSE;
1227
1228         req = &run_request[run_requests++];
1229         req->silent = flags & RUN_REQUEST_SILENT;
1230         req->confirm = flags & RUN_REQUEST_CONFIRM;
1231         req->exit = flags & RUN_REQUEST_EXIT;
1232         req->internal = flags & RUN_REQUEST_INTERNAL;
1233         req->keymap = keymap;
1234         req->key = key;
1235
1236         add_keybinding(keymap, REQ_NONE + run_requests, key);
1237         return TRUE;
1238 }
1239
1240 static struct run_request *
1241 get_run_request(enum request request)
1242 {
1243         if (request <= REQ_NONE || request > REQ_NONE + run_requests)
1244                 return NULL;
1245         return &run_request[request - REQ_NONE - 1];
1246 }
1247
1248 static void
1249 add_builtin_run_requests(void)
1250 {
1251         const char *cherry_pick[] = { "git", "cherry-pick", "%(commit)", NULL };
1252         const char *checkout[] = { "git", "checkout", "%(branch)", NULL };
1253         const char *commit[] = { "git", "commit", NULL };
1254         const char *gc[] = { "git", "gc", NULL };
1255
1256         add_run_request(get_keymap("main"), 'C', cherry_pick, RUN_REQUEST_CONFIRM);
1257         add_run_request(get_keymap("status"), 'C', commit, RUN_REQUEST_DEFAULT);
1258         add_run_request(get_keymap("branch"), 'C', checkout, RUN_REQUEST_CONFIRM);
1259         add_run_request(get_keymap("generic"), 'G', gc, RUN_REQUEST_CONFIRM);
1260 }
1261
1262 /*
1263  * User config file handling.
1264  */
1265
1266 #define OPT_ERR_INFO \
1267         OPT_ERR_(INTEGER_VALUE_OUT_OF_BOUND, "Integer value out of bound"), \
1268         OPT_ERR_(INVALID_STEP_VALUE, "Invalid step value"), \
1269         OPT_ERR_(NO_OPTION_VALUE, "No option value"), \
1270         OPT_ERR_(NO_VALUE_ASSIGNED, "No value assigned"), \
1271         OPT_ERR_(OBSOLETE_REQUEST_NAME, "Obsolete request name"), \
1272         OPT_ERR_(OUT_OF_MEMORY, "Out of memory"), \
1273         OPT_ERR_(TOO_MANY_OPTION_ARGUMENTS, "Too many option arguments"), \
1274         OPT_ERR_(FILE_DOES_NOT_EXIST, "File does not exist"), \
1275         OPT_ERR_(UNKNOWN_ATTRIBUTE, "Unknown attribute"), \
1276         OPT_ERR_(UNKNOWN_COLOR, "Unknown color"), \
1277         OPT_ERR_(UNKNOWN_COLOR_NAME, "Unknown color name"), \
1278         OPT_ERR_(UNKNOWN_KEY, "Unknown key"), \
1279         OPT_ERR_(UNKNOWN_KEY_MAP, "Unknown key map"), \
1280         OPT_ERR_(UNKNOWN_OPTION_COMMAND, "Unknown option command"), \
1281         OPT_ERR_(UNKNOWN_REQUEST_NAME, "Unknown request name"), \
1282         OPT_ERR_(UNKNOWN_VARIABLE_NAME, "Unknown variable name"), \
1283         OPT_ERR_(UNMATCHED_QUOTATION, "Unmatched quotation"), \
1284         OPT_ERR_(WRONG_NUMBER_OF_ARGUMENTS, "Wrong number of arguments"),
1285
1286 enum option_code {
1287 #define OPT_ERR_(name, msg) OPT_ERR_ ## name
1288         OPT_ERR_INFO
1289 #undef  OPT_ERR_
1290         OPT_OK
1291 };
1292
1293 static const char *option_errors[] = {
1294 #define OPT_ERR_(name, msg) msg
1295         OPT_ERR_INFO
1296 #undef  OPT_ERR_
1297 };
1298
1299 static const struct enum_map color_map[] = {
1300 #define COLOR_MAP(name) ENUM_MAP(#name, COLOR_##name)
1301         COLOR_MAP(DEFAULT),
1302         COLOR_MAP(BLACK),
1303         COLOR_MAP(BLUE),
1304         COLOR_MAP(CYAN),
1305         COLOR_MAP(GREEN),
1306         COLOR_MAP(MAGENTA),
1307         COLOR_MAP(RED),
1308         COLOR_MAP(WHITE),
1309         COLOR_MAP(YELLOW),
1310 };
1311
1312 static const struct enum_map attr_map[] = {
1313 #define ATTR_MAP(name) ENUM_MAP(#name, A_##name)
1314         ATTR_MAP(NORMAL),
1315         ATTR_MAP(BLINK),
1316         ATTR_MAP(BOLD),
1317         ATTR_MAP(DIM),
1318         ATTR_MAP(REVERSE),
1319         ATTR_MAP(STANDOUT),
1320         ATTR_MAP(UNDERLINE),
1321 };
1322
1323 #define set_attribute(attr, name)       map_enum(attr, attr_map, name)
1324
1325 static enum option_code
1326 parse_step(double *opt, const char *arg)
1327 {
1328         *opt = atoi(arg);
1329         if (!strchr(arg, '%'))
1330                 return OPT_OK;
1331
1332         /* "Shift down" so 100% and 1 does not conflict. */
1333         *opt = (*opt - 1) / 100;
1334         if (*opt >= 1.0) {
1335                 *opt = 0.99;
1336                 return OPT_ERR_INVALID_STEP_VALUE;
1337         }
1338         if (*opt < 0.0) {
1339                 *opt = 1;
1340                 return OPT_ERR_INVALID_STEP_VALUE;
1341         }
1342         return OPT_OK;
1343 }
1344
1345 static enum option_code
1346 parse_int(int *opt, const char *arg, int min, int max)
1347 {
1348         int value = atoi(arg);
1349
1350         if (min <= value && value <= max) {
1351                 *opt = value;
1352                 return OPT_OK;
1353         }
1354
1355         return OPT_ERR_INTEGER_VALUE_OUT_OF_BOUND;
1356 }
1357
1358 #define parse_id(opt, arg) \
1359         parse_int(opt, arg, 4, SIZEOF_REV - 1)
1360
1361 static bool
1362 set_color(int *color, const char *name)
1363 {
1364         if (map_enum(color, color_map, name))
1365                 return TRUE;
1366         if (!prefixcmp(name, "color"))
1367                 return parse_int(color, name + 5, 0, 255) == OPT_OK;
1368         /* Used when reading git colors. Git expects a plain int w/o prefix.  */
1369         return parse_int(color, name, 0, 255) == OPT_OK;
1370 }
1371
1372 /* Wants: object fgcolor bgcolor [attribute] */
1373 static enum option_code
1374 option_color_command(int argc, const char *argv[])
1375 {
1376         struct line_info *info;
1377
1378         if (argc < 3)
1379                 return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS;
1380
1381         if (*argv[0] == '"' || *argv[0] == '\'') {
1382                 info = add_custom_color(argv[0]);
1383         } else {
1384                 info = get_line_info(argv[0]);
1385         }
1386         if (!info) {
1387                 static const struct enum_map obsolete[] = {
1388                         ENUM_MAP("main-delim",  LINE_DELIMITER),
1389                         ENUM_MAP("main-date",   LINE_DATE),
1390                         ENUM_MAP("main-author", LINE_AUTHOR),
1391                         ENUM_MAP("blame-id",    LINE_ID),
1392                 };
1393                 int index;
1394
1395                 if (!map_enum(&index, obsolete, argv[0]))
1396                         return OPT_ERR_UNKNOWN_COLOR_NAME;
1397                 info = &line_info[index];
1398         }
1399
1400         if (!set_color(&info->fg, argv[1]) ||
1401             !set_color(&info->bg, argv[2]))
1402                 return OPT_ERR_UNKNOWN_COLOR;
1403
1404         info->attr = 0;
1405         while (argc-- > 3) {
1406                 int attr;
1407
1408                 if (!set_attribute(&attr, argv[argc]))
1409                         return OPT_ERR_UNKNOWN_ATTRIBUTE;
1410                 info->attr |= attr;
1411         }
1412
1413         return OPT_OK;
1414 }
1415
1416 static enum option_code
1417 parse_bool_matched(bool *opt, const char *arg, bool *matched)
1418 {
1419         *opt = (!strcmp(arg, "1") || !strcmp(arg, "true") || !strcmp(arg, "yes"))
1420                 ? TRUE : FALSE;
1421         if (matched)
1422                 *matched = *opt || (!strcmp(arg, "0") || !strcmp(arg, "false") || !strcmp(arg, "no"));
1423         return OPT_OK;
1424 }
1425
1426 #define parse_bool(opt, arg) parse_bool_matched(opt, arg, NULL)
1427
1428 static enum option_code
1429 parse_enum_do(unsigned int *opt, const char *arg,
1430               const struct enum_map *map, size_t map_size)
1431 {
1432         bool is_true;
1433
1434         assert(map_size > 1);
1435
1436         if (map_enum_do(map, map_size, (int *) opt, arg))
1437                 return OPT_OK;
1438
1439         parse_bool(&is_true, arg);
1440         *opt = is_true ? map[1].value : map[0].value;
1441         return OPT_OK;
1442 }
1443
1444 #define parse_enum(opt, arg, map) \
1445         parse_enum_do(opt, arg, map, ARRAY_SIZE(map))
1446
1447 static enum option_code
1448 parse_string(char *opt, const char *arg, size_t optsize)
1449 {
1450         int arglen = strlen(arg);
1451
1452         switch (arg[0]) {
1453         case '\"':
1454         case '\'':
1455                 if (arglen == 1 || arg[arglen - 1] != arg[0])
1456                         return OPT_ERR_UNMATCHED_QUOTATION;
1457                 arg += 1; arglen -= 2;
1458         default:
1459                 string_ncopy_do(opt, optsize, arg, arglen);
1460                 return OPT_OK;
1461         }
1462 }
1463
1464 static enum option_code
1465 parse_encoding(struct encoding **encoding_ref, const char *arg, bool priority)
1466 {
1467         char buf[SIZEOF_STR];
1468         enum option_code code = parse_string(buf, arg, sizeof(buf));
1469
1470         if (code == OPT_OK) {
1471                 struct encoding *encoding = *encoding_ref;
1472
1473                 if (encoding && !priority)
1474                         return code;
1475                 encoding = encoding_open(buf);
1476                 if (encoding)
1477                         *encoding_ref = encoding;
1478         }
1479
1480         return code;
1481 }
1482
1483 static enum option_code
1484 parse_args(const char ***args, const char *argv[])
1485 {
1486         if (!argv_copy(args, argv))
1487                 return OPT_ERR_OUT_OF_MEMORY;
1488         return OPT_OK;
1489 }
1490
1491 /* Wants: name = value */
1492 static enum option_code
1493 option_set_command(int argc, const char *argv[])
1494 {
1495         if (argc < 3)
1496                 return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS;
1497
1498         if (strcmp(argv[1], "="))
1499                 return OPT_ERR_NO_VALUE_ASSIGNED;
1500
1501         if (!strcmp(argv[0], "blame-options"))
1502                 return parse_args(&opt_blame_argv, argv + 2);
1503
1504         if (!strcmp(argv[0], "diff-options"))
1505                 return parse_args(&opt_diff_argv, argv + 2);
1506
1507         if (argc != 3)
1508                 return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS;
1509
1510         if (!strcmp(argv[0], "show-author"))
1511                 return parse_enum(&opt_author, argv[2], author_map);
1512
1513         if (!strcmp(argv[0], "show-date"))
1514                 return parse_enum(&opt_date, argv[2], date_map);
1515
1516         if (!strcmp(argv[0], "show-rev-graph"))
1517                 return parse_bool(&opt_rev_graph, argv[2]);
1518
1519         if (!strcmp(argv[0], "show-refs"))
1520                 return parse_bool(&opt_show_refs, argv[2]);
1521
1522         if (!strcmp(argv[0], "show-changes"))
1523                 return parse_bool(&opt_show_changes, argv[2]);
1524
1525         if (!strcmp(argv[0], "show-notes")) {
1526                 bool matched = FALSE;
1527                 enum option_code res = parse_bool_matched(&opt_notes, argv[2], &matched);
1528
1529                 if (res == OPT_OK && matched) {
1530                         update_notes_arg();
1531                         return res;
1532                 }
1533
1534                 opt_notes = TRUE;
1535                 strcpy(opt_notes_arg, "--show-notes=");
1536                 res = parse_string(opt_notes_arg + 8, argv[2],
1537                                    sizeof(opt_notes_arg) - 8);
1538                 if (res == OPT_OK && opt_notes_arg[8] == '\0')
1539                         opt_notes_arg[7] = '\0';
1540                 return res;
1541         }
1542
1543         if (!strcmp(argv[0], "show-line-numbers"))
1544                 return parse_bool(&opt_line_number, argv[2]);
1545
1546         if (!strcmp(argv[0], "line-graphics"))
1547                 return parse_enum(&opt_line_graphics, argv[2], graphic_map);
1548
1549         if (!strcmp(argv[0], "line-number-interval"))
1550                 return parse_int(&opt_num_interval, argv[2], 1, 1024);
1551
1552         if (!strcmp(argv[0], "author-width"))
1553                 return parse_int(&opt_author_width, argv[2], 0, 1024);
1554
1555         if (!strcmp(argv[0], "filename-width"))
1556                 return parse_int(&opt_filename_width, argv[2], 0, 1024);
1557
1558         if (!strcmp(argv[0], "show-filename"))
1559                 return parse_enum(&opt_filename, argv[2], filename_map);
1560
1561         if (!strcmp(argv[0], "horizontal-scroll"))
1562                 return parse_step(&opt_hscroll, argv[2]);
1563
1564         if (!strcmp(argv[0], "split-view-height"))
1565                 return parse_step(&opt_scale_split_view, argv[2]);
1566
1567         if (!strcmp(argv[0], "vertical-split"))
1568                 return parse_bool(&opt_vsplit, argv[2]);
1569
1570         if (!strcmp(argv[0], "tab-size"))
1571                 return parse_int(&opt_tab_size, argv[2], 1, 1024);
1572
1573         if (!strcmp(argv[0], "diff-context")) {
1574                 enum option_code code = parse_int(&opt_diff_context, argv[2], 0, 999999);
1575
1576                 if (code == OPT_OK)
1577                         update_diff_context_arg(opt_diff_context);
1578                 return code;
1579         }
1580
1581         if (!strcmp(argv[0], "ignore-space")) {
1582                 enum option_code code = parse_enum(&opt_ignore_space, argv[2], ignore_space_map);
1583
1584                 if (code == OPT_OK)
1585                         update_ignore_space_arg();
1586                 return code;
1587         }
1588
1589         if (!strcmp(argv[0], "commit-order")) {
1590                 enum option_code code = parse_enum(&opt_commit_order, argv[2], commit_order_map);
1591
1592                 if (code == OPT_OK)
1593                         update_commit_order_arg();
1594                 return code;
1595         }
1596
1597         if (!strcmp(argv[0], "status-untracked-dirs"))
1598                 return parse_bool(&opt_untracked_dirs_content, argv[2]);
1599
1600         if (!strcmp(argv[0], "read-git-colors"))
1601                 return parse_bool(&opt_read_git_colors, argv[2]);
1602
1603         if (!strcmp(argv[0], "ignore-case"))
1604                 return parse_bool(&opt_ignore_case, argv[2]);
1605
1606         if (!strcmp(argv[0], "focus-child"))
1607                 return parse_bool(&opt_focus_child, argv[2]);
1608
1609         if (!strcmp(argv[0], "wrap-lines"))
1610                 return parse_bool(&opt_wrap_lines, argv[2]);
1611
1612         if (!strcmp(argv[0], "show-id"))
1613                 return parse_bool(&opt_show_id, argv[2]);
1614
1615         if (!strcmp(argv[0], "id-width"))
1616                 return parse_id(&opt_id_cols, argv[2]);
1617
1618         if (!strcmp(argv[0], "title-overflow")) {
1619                 bool matched;
1620                 enum option_code code;
1621
1622                 /*
1623                  * "title-overflow" is considered a boolint.
1624                  * We try to parse it as a boolean (and set the value to 50 if true),
1625                  * otherwise we parse it as an integer and use the given value.
1626                  */
1627                 code = parse_bool_matched(&opt_show_title_overflow, argv[2], &matched);
1628                 if (code == OPT_OK && matched) {
1629                         if (opt_show_title_overflow)
1630                                 opt_title_overflow = 50;
1631                 } else {
1632                         code = parse_int(&opt_title_overflow, argv[2], 2, 1024);
1633                         if (code == OPT_OK)
1634                                 opt_show_title_overflow = TRUE;
1635                 }
1636
1637                 return code;
1638         }
1639
1640         if (!strcmp(argv[0], "editor-line-number"))
1641                 return parse_bool(&opt_editor_lineno, argv[2]);
1642
1643         return OPT_ERR_UNKNOWN_VARIABLE_NAME;
1644 }
1645
1646 /* Wants: mode request key */
1647 static enum option_code
1648 option_bind_command(int argc, const char *argv[])
1649 {
1650         enum request request;
1651         struct keymap *keymap;
1652         int key;
1653
1654         if (argc < 3)
1655                 return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS;
1656
1657         if (!(keymap = get_keymap(argv[0])))
1658                 return OPT_ERR_UNKNOWN_KEY_MAP;
1659
1660         key = get_key_value(argv[1]);
1661         if (key == ERR)
1662                 return OPT_ERR_UNKNOWN_KEY;
1663
1664         request = get_request(argv[2]);
1665         if (request == REQ_UNKNOWN) {
1666                 static const struct enum_map obsolete[] = {
1667                         ENUM_MAP("cherry-pick",         REQ_NONE),
1668                         ENUM_MAP("screen-resize",       REQ_NONE),
1669                         ENUM_MAP("tree-parent",         REQ_PARENT),
1670                 };
1671                 int alias;
1672
1673                 if (map_enum(&alias, obsolete, argv[2])) {
1674                         if (alias != REQ_NONE)
1675                                 add_keybinding(keymap, alias, key);
1676                         return OPT_ERR_OBSOLETE_REQUEST_NAME;
1677                 }
1678         }
1679
1680         if (request == REQ_UNKNOWN) {
1681                 char first = *argv[2]++;
1682
1683                 if (first == '!') {
1684                         enum run_request_flag flags = RUN_REQUEST_FORCE;
1685
1686                         while (*argv[2]) {
1687                                 if (*argv[2] == '@') {
1688                                         flags |= RUN_REQUEST_SILENT;
1689                                 } else if (*argv[2] == '?') {
1690                                         flags |= RUN_REQUEST_CONFIRM;
1691                                 } else if (*argv[2] == '<') {
1692                                         flags |= RUN_REQUEST_EXIT;
1693                                 } else {
1694                                         break;
1695                                 }
1696                                 argv[2]++;
1697                         }
1698
1699                         return add_run_request(keymap, key, argv + 2, flags)
1700                                 ? OPT_OK : OPT_ERR_OUT_OF_MEMORY;
1701                 } else if (first == ':') {
1702                         return add_run_request(keymap, key, argv + 2, RUN_REQUEST_FORCE | RUN_REQUEST_INTERNAL)
1703                                 ? OPT_OK : OPT_ERR_OUT_OF_MEMORY;
1704                 } else {
1705                         return OPT_ERR_UNKNOWN_REQUEST_NAME;
1706                 }
1707         }
1708
1709         add_keybinding(keymap, request, key);
1710
1711         return OPT_OK;
1712 }
1713
1714
1715 static enum option_code load_option_file(const char *path);
1716
1717 static enum option_code
1718 option_source_command(int argc, const char *argv[])
1719 {
1720         if (argc < 1)
1721                 return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS;
1722
1723         return load_option_file(argv[0]);
1724 }
1725
1726 static enum option_code
1727 set_option(const char *opt, char *value)
1728 {
1729         const char *argv[SIZEOF_ARG];
1730         int argc = 0;
1731
1732         if (!argv_from_string(argv, &argc, value))
1733                 return OPT_ERR_TOO_MANY_OPTION_ARGUMENTS;
1734
1735         if (!strcmp(opt, "color"))
1736                 return option_color_command(argc, argv);
1737
1738         if (!strcmp(opt, "set"))
1739                 return option_set_command(argc, argv);
1740
1741         if (!strcmp(opt, "bind"))
1742                 return option_bind_command(argc, argv);
1743
1744         if (!strcmp(opt, "source"))
1745                 return option_source_command(argc, argv);
1746
1747         return OPT_ERR_UNKNOWN_OPTION_COMMAND;
1748 }
1749
1750 struct config_state {
1751         const char *path;
1752         int lineno;
1753         bool errors;
1754 };
1755
1756 static int
1757 read_option(char *opt, size_t optlen, char *value, size_t valuelen, void *data)
1758 {
1759         struct config_state *config = data;
1760         enum option_code status = OPT_ERR_NO_OPTION_VALUE;
1761
1762         config->lineno++;
1763
1764         /* Check for comment markers, since read_properties() will
1765          * only ensure opt and value are split at first " \t". */
1766         optlen = strcspn(opt, "#");
1767         if (optlen == 0)
1768                 return OK;
1769
1770         if (opt[optlen] == 0) {
1771                 /* Look for comment endings in the value. */
1772                 size_t len = strcspn(value, "#");
1773
1774                 if (len < valuelen) {
1775                         valuelen = len;
1776                         value[valuelen] = 0;
1777                 }
1778
1779                 status = set_option(opt, value);
1780         }
1781
1782         if (status != OPT_OK) {
1783                 warn("%s line %d: %s near '%.*s'", config->path, config->lineno,
1784                      option_errors[status], (int) optlen, opt);
1785                 config->errors = TRUE;
1786         }
1787
1788         /* Always keep going if errors are encountered. */
1789         return OK;
1790 }
1791
1792 static enum option_code
1793 load_option_file(const char *path)
1794 {
1795         struct config_state config = { path, 0, FALSE };
1796         struct io io;
1797
1798         /* Do not read configuration from stdin if set to "" */
1799         if (!path || !strlen(path))
1800                 return OPT_OK;
1801
1802         /* It's OK that the file doesn't exist. */
1803         if (!io_open(&io, "%s", path))
1804                 return OPT_ERR_FILE_DOES_NOT_EXIST;
1805
1806         if (io_load(&io, " \t", read_option, &config) == ERR ||
1807             config.errors == TRUE)
1808                 warn("Errors while loading %s.", path);
1809         return OPT_OK;
1810 }
1811
1812 static int
1813 load_options(void)
1814 {
1815         const char *home = getenv("HOME");
1816         const char *tigrc_user = getenv("TIGRC_USER");
1817         const char *tigrc_system = getenv("TIGRC_SYSTEM");
1818         const char *tig_diff_opts = getenv("TIG_DIFF_OPTS");
1819         const bool diff_opts_from_args = !!opt_diff_argv;
1820         char buf[SIZEOF_STR];
1821
1822         if (!tigrc_system)
1823                 tigrc_system = SYSCONFDIR "/tigrc";
1824         load_option_file(tigrc_system);
1825
1826         if (!tigrc_user) {
1827                 if (!home || !string_format(buf, "%s/.tigrc", home))
1828                         return ERR;
1829                 tigrc_user = buf;
1830         }
1831         load_option_file(tigrc_user);
1832
1833         /* Add _after_ loading config files to avoid adding run requests
1834          * that conflict with keybindings. */
1835         add_builtin_run_requests();
1836
1837         if (!diff_opts_from_args && tig_diff_opts && *tig_diff_opts) {
1838                 static const char *diff_opts[SIZEOF_ARG] = { NULL };
1839                 int argc = 0;
1840
1841                 if (!string_format(buf, "%s", tig_diff_opts) ||
1842                     !argv_from_string(diff_opts, &argc, buf))
1843                         die("TIG_DIFF_OPTS contains too many arguments");
1844                 else if (!argv_copy(&opt_diff_argv, diff_opts))
1845                         die("Failed to format TIG_DIFF_OPTS arguments");
1846         }
1847
1848         return OK;
1849 }
1850
1851
1852 /*
1853  * The viewer
1854  */
1855
1856 struct view;
1857 struct view_ops;
1858
1859 /* The display array of active views and the index of the current view. */
1860 static struct view *display[2];
1861 static WINDOW *display_win[2];
1862 static WINDOW *display_title[2];
1863 static WINDOW *display_sep;
1864
1865 static unsigned int current_view;
1866
1867 #define foreach_displayed_view(view, i) \
1868         for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
1869
1870 #define displayed_views()       (display[1] != NULL ? 2 : 1)
1871
1872 /* Current head and commit ID */
1873 static char ref_blob[SIZEOF_REF]        = "";
1874 static char ref_commit[SIZEOF_REF]      = "HEAD";
1875 static char ref_head[SIZEOF_REF]        = "HEAD";
1876 static char ref_branch[SIZEOF_REF]      = "";
1877 static char ref_status[SIZEOF_STR]      = "";
1878
1879 enum view_flag {
1880         VIEW_NO_FLAGS = 0,
1881         VIEW_ALWAYS_LINENO      = 1 << 0,
1882         VIEW_CUSTOM_STATUS      = 1 << 1,
1883         VIEW_ADD_DESCRIBE_REF   = 1 << 2,
1884         VIEW_ADD_PAGER_REFS     = 1 << 3,
1885         VIEW_OPEN_DIFF          = 1 << 4,
1886         VIEW_NO_REF             = 1 << 5,
1887         VIEW_NO_GIT_DIR         = 1 << 6,
1888         VIEW_DIFF_LIKE          = 1 << 7,
1889         VIEW_STDIN              = 1 << 8,
1890         VIEW_SEND_CHILD_ENTER   = 1 << 9,
1891         VIEW_FILE_FILTER        = 1 << 10,
1892 };
1893
1894 #define view_has_flags(view, flag)      ((view)->ops->flags & (flag))
1895
1896 struct position {
1897         unsigned long offset;   /* Offset of the window top */
1898         unsigned long col;      /* Offset from the window side. */
1899         unsigned long lineno;   /* Current line number */
1900 };
1901
1902 struct view {
1903         const char *name;       /* View name */
1904         const char *id;         /* Points to either of ref_{head,commit,blob} */
1905
1906         struct view_ops *ops;   /* View operations */
1907
1908         char ref[SIZEOF_REF];   /* Hovered commit reference */
1909         char vid[SIZEOF_REF];   /* View ID. Set to id member when updating. */
1910
1911         int height, width;      /* The width and height of the main window */
1912         WINDOW *win;            /* The main window */
1913
1914         /* Navigation */
1915         struct position pos;    /* Current position. */
1916         struct position prev_pos; /* Previous position. */
1917
1918         /* Searching */
1919         char grep[SIZEOF_STR];  /* Search string */
1920         regex_t *regex;         /* Pre-compiled regexp */
1921
1922         /* If non-NULL, points to the view that opened this view. If this view
1923          * is closed tig will switch back to the parent view. */
1924         struct view *parent;
1925         struct view *prev;
1926
1927         /* Buffering */
1928         size_t lines;           /* Total number of lines */
1929         struct line *line;      /* Line index */
1930         unsigned int digits;    /* Number of digits in the lines member. */
1931
1932         /* Number of lines with custom status, not to be counted in the
1933          * view title. */
1934         unsigned int custom_lines;
1935
1936         /* Drawing */
1937         struct line *curline;   /* Line currently being drawn. */
1938         enum line_type curtype; /* Attribute currently used for drawing. */
1939         unsigned long col;      /* Column when drawing. */
1940         bool has_scrolled;      /* View was scrolled. */
1941
1942         /* Loading */
1943         const char **argv;      /* Shell command arguments. */
1944         const char *dir;        /* Directory from which to execute. */
1945         struct io io;
1946         struct io *pipe;
1947         time_t start_time;
1948         time_t update_secs;
1949         struct encoding *encoding;
1950         bool unrefreshable;
1951
1952         /* Private data */
1953         void *private;
1954 };
1955
1956 enum open_flags {
1957         OPEN_DEFAULT = 0,       /* Use default view switching. */
1958         OPEN_SPLIT = 1,         /* Split current view. */
1959         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
1960         OPEN_REFRESH = 16,      /* Refresh view using previous command. */
1961         OPEN_PREPARED = 32,     /* Open already prepared command. */
1962         OPEN_EXTRA = 64,        /* Open extra data from command. */
1963 };
1964
1965 struct view_ops {
1966         /* What type of content being displayed. Used in the title bar. */
1967         const char *type;
1968         /* What keymap does this view have */
1969         struct keymap keymap;
1970         /* Flags to control the view behavior. */
1971         enum view_flag flags;
1972         /* Size of private data. */
1973         size_t private_size;
1974         /* Open and reads in all view content. */
1975         bool (*open)(struct view *view, enum open_flags flags);
1976         /* Read one line; updates view->line. */
1977         bool (*read)(struct view *view, char *data);
1978         /* Draw one line; @lineno must be < view->height. */
1979         bool (*draw)(struct view *view, struct line *line, unsigned int lineno);
1980         /* Depending on view handle a special requests. */
1981         enum request (*request)(struct view *view, enum request request, struct line *line);
1982         /* Search for regexp in a line. */
1983         bool (*grep)(struct view *view, struct line *line);
1984         /* Select line */
1985         void (*select)(struct view *view, struct line *line);
1986 };
1987
1988 #define VIEW_OPS(id, name, ref) name##_ops
1989 static struct view_ops VIEW_INFO(VIEW_OPS);
1990
1991 static struct view views[] = {
1992 #define VIEW_DATA(id, name, ref) \
1993         { #name, ref, &name##_ops }
1994         VIEW_INFO(VIEW_DATA)
1995 };
1996
1997 #define VIEW(req)       (&views[(req) - REQ_OFFSET - 1])
1998
1999 #define foreach_view(view, i) \
2000         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
2001
2002 #define view_is_displayed(view) \
2003         (view == display[0] || view == display[1])
2004
2005 #define view_has_line(view, line_) \
2006         ((view)->line <= (line_) && (line_) < (view)->line + (view)->lines)
2007
2008 static bool
2009 forward_request_to_child(struct view *child, enum request request)
2010 {
2011         return displayed_views() == 2 && view_is_displayed(child) &&
2012                 !strcmp(child->vid, child->id);
2013 }
2014
2015 static enum request
2016 view_request(struct view *view, enum request request)
2017 {
2018         if (!view || !view->lines)
2019                 return request;
2020
2021         if (request == REQ_ENTER && !opt_focus_child &&
2022             view_has_flags(view, VIEW_SEND_CHILD_ENTER)) {
2023                 struct view *child = display[1];
2024
2025                 if (forward_request_to_child(child, request)) {
2026                         view_request(child, request);
2027                         return REQ_NONE;
2028                 }
2029         }
2030
2031         if (request == REQ_REFRESH && view->unrefreshable) {
2032                 report("This view can not be refreshed");
2033                 return REQ_NONE;
2034         }
2035
2036         return view->ops->request(view, request, &view->line[view->pos.lineno]);
2037 }
2038
2039 /*
2040  * View drawing.
2041  */
2042
2043 static inline void
2044 set_view_attr(struct view *view, enum line_type type)
2045 {
2046         if (!view->curline->selected && view->curtype != type) {
2047                 (void) wattrset(view->win, get_line_attr(type));
2048                 wchgat(view->win, -1, 0, get_line_color(type), NULL);
2049                 view->curtype = type;
2050         }
2051 }
2052
2053 #define VIEW_MAX_LEN(view) ((view)->width + (view)->pos.col - (view)->col)
2054
2055 static bool
2056 draw_chars(struct view *view, enum line_type type, const char *string,
2057            int max_len, bool use_tilde)
2058 {
2059         static char out_buffer[BUFSIZ * 2];
2060         int len = 0;
2061         int col = 0;
2062         int trimmed = FALSE;
2063         size_t skip = view->pos.col > view->col ? view->pos.col - view->col : 0;
2064
2065         if (max_len <= 0)
2066                 return VIEW_MAX_LEN(view) <= 0;
2067
2068         len = utf8_length(&string, skip, &col, max_len, &trimmed, use_tilde, opt_tab_size);
2069
2070         set_view_attr(view, type);
2071         if (len > 0) {
2072                 if (opt_iconv_out != ICONV_NONE) {
2073                         size_t inlen = len + 1;
2074                         char *instr = calloc(1, inlen);
2075                         ICONV_CONST char *inbuf = (ICONV_CONST char *) instr;
2076                         if (!instr)
2077                             return VIEW_MAX_LEN(view) <= 0;
2078
2079                         strncpy(instr, string, len);
2080
2081                         char *outbuf = out_buffer;
2082                         size_t outlen = sizeof(out_buffer);
2083
2084                         size_t ret;
2085
2086                         ret = iconv(opt_iconv_out, &inbuf, &inlen, &outbuf, &outlen);
2087                         if (ret != (size_t) -1) {
2088                                 string = out_buffer;
2089                                 len = sizeof(out_buffer) - outlen;
2090                         }
2091                         free(instr);
2092                 }
2093
2094                 waddnstr(view->win, string, len);
2095
2096                 if (trimmed && use_tilde) {
2097                         set_view_attr(view, LINE_DELIMITER);
2098                         waddch(view->win, '~');
2099                         col++;
2100                 }
2101         }
2102
2103         view->col += col;
2104         return VIEW_MAX_LEN(view) <= 0;
2105 }
2106
2107 static bool
2108 draw_space(struct view *view, enum line_type type, int max, int spaces)
2109 {
2110         static char space[] = "                    ";
2111
2112         spaces = MIN(max, spaces);
2113
2114         while (spaces > 0) {
2115                 int len = MIN(spaces, sizeof(space) - 1);
2116
2117                 if (draw_chars(view, type, space, len, FALSE))
2118                         return TRUE;
2119                 spaces -= len;
2120         }
2121
2122         return VIEW_MAX_LEN(view) <= 0;
2123 }
2124
2125 static bool
2126 draw_text_expanded(struct view *view, enum line_type type, const char *string, int max_len, bool use_tilde)
2127 {
2128         static char text[SIZEOF_STR];
2129
2130         do {
2131                 size_t pos = string_expand(text, sizeof(text), string, opt_tab_size);
2132
2133                 if (draw_chars(view, type, text, max_len, use_tilde))
2134                         return TRUE;
2135                 string += pos;
2136         } while (*string);
2137
2138         return VIEW_MAX_LEN(view) <= 0;
2139 }
2140
2141 static bool
2142 draw_text(struct view *view, enum line_type type, const char *string)
2143 {
2144         return draw_text_expanded(view, type, string, VIEW_MAX_LEN(view), TRUE);
2145 }
2146
2147 static bool
2148 draw_text_overflow(struct view *view, const char *text, bool on, int overflow, enum line_type type)
2149 {
2150         if (on) {
2151                 int len = strlen(text);
2152
2153                 if (draw_text_expanded(view, type, text, overflow, FALSE))
2154                         return TRUE;
2155
2156                 text = len > overflow ? text + overflow : "";
2157                 type = LINE_OVERFLOW;
2158         }
2159
2160         if (*text && draw_chars(view, type, text, VIEW_MAX_LEN(view), TRUE))
2161                 return TRUE;
2162
2163         return VIEW_MAX_LEN(view) <= 0;
2164 }
2165
2166 #define draw_commit_title(view, text, offset) \
2167         draw_text_overflow(view, text, opt_show_title_overflow, opt_title_overflow + offset, LINE_DEFAULT)
2168
2169 static bool PRINTF_LIKE(3, 4)
2170 draw_formatted(struct view *view, enum line_type type, const char *format, ...)
2171 {
2172         char text[SIZEOF_STR];
2173         int retval;
2174
2175         FORMAT_BUFFER(text, sizeof(text), format, retval, TRUE);
2176         return retval >= 0 ? draw_text(view, type, text) : VIEW_MAX_LEN(view) <= 0;
2177 }
2178
2179 static bool
2180 draw_graphic(struct view *view, enum line_type type, const chtype graphic[], size_t size, bool separator)
2181 {
2182         size_t skip = view->pos.col > view->col ? view->pos.col - view->col : 0;
2183         int max = VIEW_MAX_LEN(view);
2184         int i;
2185
2186         if (max < size)
2187                 size = max;
2188
2189         set_view_attr(view, type);
2190         /* Using waddch() instead of waddnstr() ensures that
2191          * they'll be rendered correctly for the cursor line. */
2192         for (i = skip; i < size; i++)
2193                 waddch(view->win, graphic[i]);
2194
2195         view->col += size;
2196         if (separator) {
2197                 if (size < max && skip <= size)
2198                         waddch(view->win, ' ');
2199                 view->col++;
2200         }
2201
2202         return VIEW_MAX_LEN(view) <= 0;
2203 }
2204
2205 static bool
2206 draw_field(struct view *view, enum line_type type, const char *text, int width, bool trim)
2207 {
2208         int max = MIN(VIEW_MAX_LEN(view), width + 1);
2209         int col = view->col;
2210
2211         if (!text)
2212                 return draw_space(view, type, max, max);
2213
2214         return draw_chars(view, type, text, max - 1, trim)
2215             || draw_space(view, LINE_DEFAULT, max - (view->col - col), max);
2216 }
2217
2218 static bool
2219 draw_date(struct view *view, struct time *time)
2220 {
2221         const char *date = mkdate(time, opt_date);
2222         int cols = opt_date == DATE_SHORT ? DATE_SHORT_WIDTH : DATE_WIDTH;
2223
2224         if (opt_date == DATE_NO)
2225                 return FALSE;
2226
2227         return draw_field(view, LINE_DATE, date, cols, FALSE);
2228 }
2229
2230 static bool
2231 draw_author(struct view *view, const struct ident *author)
2232 {
2233         bool trim = author_trim(opt_author_width);
2234         const char *text = mkauthor(author, opt_author_width, opt_author);
2235
2236         if (opt_author == AUTHOR_NO)
2237                 return FALSE;
2238
2239         return draw_field(view, LINE_AUTHOR, text, opt_author_width, trim);
2240 }
2241
2242 static bool
2243 draw_id(struct view *view, enum line_type type, const char *id)
2244 {
2245         return draw_field(view, type, id, opt_id_cols, FALSE);
2246 }
2247
2248 static bool
2249 draw_filename(struct view *view, const char *filename, bool auto_enabled)
2250 {
2251         bool trim = filename && strlen(filename) >= opt_filename_width;
2252
2253         if (opt_filename == FILENAME_NO)
2254                 return FALSE;
2255
2256         if (opt_filename == FILENAME_AUTO && !auto_enabled)
2257                 return FALSE;
2258
2259         return draw_field(view, LINE_FILENAME, filename, opt_filename_width, trim);
2260 }
2261
2262 static bool
2263 draw_mode(struct view *view, mode_t mode)
2264 {
2265         const char *str = mkmode(mode);
2266
2267         return draw_field(view, LINE_MODE, str, STRING_SIZE("-rw-r--r--"), FALSE);
2268 }
2269
2270 static bool
2271 draw_lineno(struct view *view, unsigned int lineno)
2272 {
2273         char number[10];
2274         int digits3 = view->digits < 3 ? 3 : view->digits;
2275         int max = MIN(VIEW_MAX_LEN(view), digits3);
2276         char *text = NULL;
2277         chtype separator = opt_line_graphics ? ACS_VLINE : '|';
2278
2279         if (!opt_line_number)
2280                 return FALSE;
2281
2282         lineno += view->pos.offset + 1;
2283         if (lineno == 1 || (lineno % opt_num_interval) == 0) {
2284                 static char fmt[] = "%1ld";
2285
2286                 fmt[1] = '0' + (view->digits <= 9 ? digits3 : 1);
2287                 if (string_format(number, fmt, lineno))
2288                         text = number;
2289         }
2290         if (text)
2291                 draw_chars(view, LINE_LINE_NUMBER, text, max, TRUE);
2292         else
2293                 draw_space(view, LINE_LINE_NUMBER, max, digits3);
2294         return draw_graphic(view, LINE_DEFAULT, &separator, 1, TRUE);
2295 }
2296
2297 static bool
2298 draw_refs(struct view *view, struct ref_list *refs)
2299 {
2300         size_t i;
2301
2302         if (!opt_show_refs || !refs)
2303                 return FALSE;
2304
2305         for (i = 0; i < refs->size; i++) {
2306                 struct ref *ref = refs->refs[i];
2307                 enum line_type type = get_line_type_from_ref(ref);
2308
2309                 if (draw_formatted(view, type, "[%s]", ref->name))
2310                         return TRUE;
2311
2312                 if (draw_text(view, LINE_DEFAULT, " "))
2313                         return TRUE;
2314         }
2315
2316         return FALSE;
2317 }
2318
2319 static bool
2320 draw_view_line(struct view *view, unsigned int lineno)
2321 {
2322         struct line *line;
2323         bool selected = (view->pos.offset + lineno == view->pos.lineno);
2324
2325         assert(view_is_displayed(view));
2326
2327         if (view->pos.offset + lineno >= view->lines)
2328                 return FALSE;
2329
2330         line = &view->line[view->pos.offset + lineno];
2331
2332         wmove(view->win, lineno, 0);
2333         if (line->cleareol)
2334                 wclrtoeol(view->win);
2335         view->col = 0;
2336         view->curline = line;
2337         view->curtype = LINE_NONE;
2338         line->selected = FALSE;
2339         line->dirty = line->cleareol = 0;
2340
2341         if (selected) {
2342                 set_view_attr(view, LINE_CURSOR);
2343                 line->selected = TRUE;
2344                 view->ops->select(view, line);
2345         }
2346
2347         return view->ops->draw(view, line, lineno);
2348 }
2349
2350 static void
2351 redraw_view_dirty(struct view *view)
2352 {
2353         bool dirty = FALSE;
2354         int lineno;
2355
2356         for (lineno = 0; lineno < view->height; lineno++) {
2357                 if (view->pos.offset + lineno >= view->lines)
2358                         break;
2359                 if (!view->line[view->pos.offset + lineno].dirty)
2360                         continue;
2361                 dirty = TRUE;
2362                 if (!draw_view_line(view, lineno))
2363                         break;
2364         }
2365
2366         if (!dirty)
2367                 return;
2368         wnoutrefresh(view->win);
2369 }
2370
2371 static void
2372 redraw_view_from(struct view *view, int lineno)
2373 {
2374         assert(0 <= lineno && lineno < view->height);
2375
2376         for (; lineno < view->height; lineno++) {
2377                 if (!draw_view_line(view, lineno))
2378                         break;
2379         }
2380
2381         wnoutrefresh(view->win);
2382 }
2383
2384 static void
2385 redraw_view(struct view *view)
2386 {
2387         werase(view->win);
2388         redraw_view_from(view, 0);
2389 }
2390
2391
2392 static void
2393 update_view_title(struct view *view)
2394 {
2395         char buf[SIZEOF_STR];
2396         char state[SIZEOF_STR];
2397         size_t bufpos = 0, statelen = 0;
2398         WINDOW *window = display[0] == view ? display_title[0] : display_title[1];
2399         struct line *line = &view->line[view->pos.lineno];
2400
2401         assert(view_is_displayed(view));
2402
2403         if (!view_has_flags(view, VIEW_CUSTOM_STATUS) && view_has_line(view, line) &&
2404             line->lineno) {
2405                 unsigned int view_lines = view->pos.offset + view->height;
2406                 unsigned int lines = view->lines
2407                                    ? MIN(view_lines, view->lines) * 100 / view->lines
2408                                    : 0;
2409
2410                 string_format_from(state, &statelen, " - %s %d of %zd (%d%%)",
2411                                    view->ops->type,
2412                                    line->lineno,
2413                                    view->lines - view->custom_lines,
2414                                    lines);
2415
2416         }
2417
2418         if (view->pipe) {
2419                 time_t secs = time(NULL) - view->start_time;
2420
2421                 /* Three git seconds are a long time ... */
2422                 if (secs > 2)
2423                         string_format_from(state, &statelen, " loading %lds", secs);
2424         }
2425
2426         string_format_from(buf, &bufpos, "[%s]", view->name);
2427         if (*view->ref && bufpos < view->width) {
2428                 size_t refsize = strlen(view->ref);
2429                 size_t minsize = bufpos + 1 + /* abbrev= */ 7 + 1 + statelen;
2430
2431                 if (minsize < view->width)
2432                         refsize = view->width - minsize + 7;
2433                 string_format_from(buf, &bufpos, " %.*s", (int) refsize, view->ref);
2434         }
2435
2436         if (statelen && bufpos < view->width) {
2437                 string_format_from(buf, &bufpos, "%s", state);
2438         }
2439
2440         if (view == display[current_view])
2441                 wbkgdset(window, get_line_attr(LINE_TITLE_FOCUS));
2442         else
2443                 wbkgdset(window, get_line_attr(LINE_TITLE_BLUR));
2444
2445         mvwaddnstr(window, 0, 0, buf, bufpos);
2446         wclrtoeol(window);
2447         wnoutrefresh(window);
2448 }
2449
2450 static int
2451 apply_step(double step, int value)
2452 {
2453         if (step >= 1)
2454                 return (int) step;
2455         value *= step + 0.01;
2456         return value ? value : 1;
2457 }
2458
2459 static void
2460 apply_horizontal_split(struct view *base, struct view *view)
2461 {
2462         view->width   = base->width;
2463         view->height  = apply_step(opt_scale_split_view, base->height);
2464         view->height  = MAX(view->height, MIN_VIEW_HEIGHT);
2465         view->height  = MIN(view->height, base->height - MIN_VIEW_HEIGHT);
2466         base->height -= view->height;
2467 }
2468
2469 static void
2470 apply_vertical_split(struct view *base, struct view *view)
2471 {
2472         view->height = base->height;
2473         view->width  = apply_step(opt_scale_vsplit_view, base->width);
2474         view->width  = MAX(view->width, MIN_VIEW_WIDTH);
2475         view->width  = MIN(view->width, base->width - MIN_VIEW_WIDTH);
2476         base->width -= view->width;
2477 }
2478
2479 static void
2480 redraw_display_separator(bool clear)
2481 {
2482         if (displayed_views() > 1 && opt_vsplit) {
2483                 chtype separator = opt_line_graphics ? ACS_VLINE : '|';
2484
2485                 if (clear)
2486                         wclear(display_sep);
2487                 wbkgd(display_sep, separator + get_line_attr(LINE_TITLE_BLUR));
2488                 wnoutrefresh(display_sep);
2489         }
2490 }
2491
2492 static void
2493 resize_display(void)
2494 {
2495         int x, y, i;
2496         struct view *base = display[0];
2497         struct view *view = display[1] ? display[1] : display[0];
2498
2499         /* Setup window dimensions */
2500
2501         getmaxyx(stdscr, base->height, base->width);
2502         set_int_environment_variable("COLUMNS", base->width);
2503         set_int_environment_variable("LINES", base->height);
2504
2505         /* Make room for the status window. */
2506         base->height -= 1;
2507
2508         if (view != base) {
2509                 if (opt_vsplit) {
2510                         apply_vertical_split(base, view);
2511
2512                         /* Make room for the separator bar. */
2513                         view->width -= 1;
2514                 } else {
2515                         apply_horizontal_split(base, view);
2516                 }
2517
2518                 /* Make room for the title bar. */
2519                 view->height -= 1;
2520         }
2521
2522         /* Make room for the title bar. */
2523         base->height -= 1;
2524
2525         x = y = 0;
2526
2527         foreach_displayed_view (view, i) {
2528                 if (!display_win[i]) {
2529                         display_win[i] = newwin(view->height, view->width, y, x);
2530                         if (!display_win[i])
2531                                 die("Failed to create %s view", view->name);
2532
2533                         scrollok(display_win[i], FALSE);
2534
2535                         display_title[i] = newwin(1, view->width, y + view->height, x);
2536                         if (!display_title[i])
2537                                 die("Failed to create title window");
2538
2539                 } else {
2540                         wresize(display_win[i], view->height, view->width);
2541                         mvwin(display_win[i], y, x);
2542                         wresize(display_title[i], 1, view->width);
2543                         mvwin(display_title[i], y + view->height, x);
2544                 }
2545
2546                 if (i > 0 && opt_vsplit) {
2547                         if (!display_sep) {
2548                                 display_sep = newwin(view->height, 1, 0, x - 1);
2549                                 if (!display_sep)
2550                                         die("Failed to create separator window");
2551
2552                         } else {
2553                                 wresize(display_sep, view->height, 1);
2554                                 mvwin(display_sep, 0, x - 1);
2555                         }
2556                 }
2557
2558                 view->win = display_win[i];
2559
2560                 if (opt_vsplit)
2561                         x += view->width + 1;
2562                 else
2563                         y += view->height + 1;
2564         }
2565
2566         redraw_display_separator(FALSE);
2567 }
2568
2569 static void
2570 redraw_display(bool clear)
2571 {
2572         struct view *view;
2573         int i;
2574
2575         foreach_displayed_view (view, i) {
2576                 if (clear)
2577                         wclear(view->win);
2578                 redraw_view(view);
2579                 update_view_title(view);
2580         }
2581
2582         redraw_display_separator(clear);
2583 }
2584
2585 /*
2586  * Option management
2587  */
2588
2589 #define TOGGLE_MENU \
2590         TOGGLE_(LINENO,    '.', "line numbers",      &opt_line_number, NULL) \
2591         TOGGLE_(DATE,      'D', "dates",             &opt_date, date_map) \
2592         TOGGLE_(AUTHOR,    'A', "author",            &opt_author, author_map) \
2593         TOGGLE_(GRAPHIC,   '~', "graphics",          &opt_line_graphics, graphic_map) \
2594         TOGGLE_(REV_GRAPH, 'g', "revision graph",    &opt_rev_graph, NULL) \
2595         TOGGLE_(FILENAME,  '#', "file names",        &opt_filename, filename_map) \
2596         TOGGLE_(IGNORE_SPACE, 'W', "space changes",  &opt_ignore_space, ignore_space_map) \
2597         TOGGLE_(COMMIT_ORDER, 'l', "commit order",   &opt_commit_order, commit_order_map) \
2598         TOGGLE_(REFS,      'F', "reference display", &opt_show_refs, NULL) \
2599         TOGGLE_(CHANGES,   'C', "local change display", &opt_show_changes, NULL) \
2600         TOGGLE_(ID,        'X', "commit ID display", &opt_show_id, NULL) \
2601         TOGGLE_(FILES,     '%', "file filtering",    &opt_file_filter, NULL) \
2602         TOGGLE_(TITLE_OVERFLOW, '$', "commit title overflow display", &opt_show_title_overflow, NULL) \
2603
2604 static bool
2605 toggle_option(struct view *view, enum request request, char msg[SIZEOF_STR])
2606 {
2607         const struct {
2608                 enum request request;
2609                 const struct enum_map *map;
2610                 size_t map_size;
2611         } data[] = {
2612 #define TOGGLE_(id, key, help, value, map) { REQ_TOGGLE_ ## id, map, (map != NULL ? ARRAY_SIZE(map) : 0) },
2613                 TOGGLE_MENU
2614 #undef  TOGGLE_
2615         };
2616         const struct menu_item menu[] = {
2617 #define TOGGLE_(id, key, help, value, map) { key, help, value },
2618                 TOGGLE_MENU
2619 #undef  TOGGLE_
2620                 { 0 }
2621         };
2622         int i = 0;
2623
2624         if (request == REQ_OPTIONS) {
2625                 if (!prompt_menu("Toggle option", menu, &i))
2626                         return FALSE;
2627         } else {
2628                 while (i < ARRAY_SIZE(data) && data[i].request != request)
2629                         i++;
2630                 if (i >= ARRAY_SIZE(data))
2631                         die("Invalid request (%d)", request);
2632         }
2633
2634         if (data[i].map != NULL) {
2635                 unsigned int *opt = menu[i].data;
2636
2637                 *opt = (*opt + 1) % data[i].map_size;
2638                 if (data[i].map == ignore_space_map) {
2639                         update_ignore_space_arg();
2640                         string_format_size(msg, SIZEOF_STR,
2641                                 "Ignoring %s %s", enum_name(data[i].map[*opt]), menu[i].text);
2642                         return TRUE;
2643
2644                 } else if (data[i].map == commit_order_map) {
2645                         update_commit_order_arg();
2646                         string_format_size(msg, SIZEOF_STR,
2647                                 "Using %s %s", enum_name(data[i].map[*opt]), menu[i].text);
2648                         return TRUE;
2649                 }
2650
2651                 string_format_size(msg, SIZEOF_STR,
2652                         "Displaying %s %s", enum_name(data[i].map[*opt]), menu[i].text);
2653
2654         } else {
2655                 bool *option = menu[i].data;
2656
2657                 *option = !*option;
2658                 string_format_size(msg, SIZEOF_STR,
2659                         "%sabling %s", *option ? "En" : "Dis", menu[i].text);
2660
2661                 if (option == &opt_file_filter)
2662                         return TRUE;
2663         }
2664
2665         return FALSE;
2666 }
2667
2668
2669 /*
2670  * Navigation
2671  */
2672
2673 static bool
2674 goto_view_line(struct view *view, unsigned long offset, unsigned long lineno)
2675 {
2676         if (lineno >= view->lines)
2677                 lineno = view->lines > 0 ? view->lines - 1 : 0;
2678
2679         if (offset > lineno || offset + view->height <= lineno) {
2680                 unsigned long half = view->height / 2;
2681
2682                 if (lineno > half)
2683                         offset = lineno - half;
2684                 else
2685                         offset = 0;
2686         }
2687
2688         if (offset != view->pos.offset || lineno != view->pos.lineno) {
2689                 view->pos.offset = offset;
2690                 view->pos.lineno = lineno;
2691                 return TRUE;
2692         }
2693
2694         return FALSE;
2695 }
2696
2697 /* Scrolling backend */
2698 static void
2699 do_scroll_view(struct view *view, int lines)
2700 {
2701         bool redraw_current_line = FALSE;
2702
2703         /* The rendering expects the new offset. */
2704         view->pos.offset += lines;
2705
2706         assert(0 <= view->pos.offset && view->pos.offset < view->lines);
2707         assert(lines);
2708
2709         /* Move current line into the view. */
2710         if (view->pos.lineno < view->pos.offset) {
2711                 view->pos.lineno = view->pos.offset;
2712                 redraw_current_line = TRUE;
2713         } else if (view->pos.lineno >= view->pos.offset + view->height) {
2714                 view->pos.lineno = view->pos.offset + view->height - 1;
2715                 redraw_current_line = TRUE;
2716         }
2717
2718         assert(view->pos.offset <= view->pos.lineno && view->pos.lineno < view->lines);
2719
2720         /* Redraw the whole screen if scrolling is pointless. */
2721         if (view->height < ABS(lines)) {
2722                 redraw_view(view);
2723
2724         } else {
2725                 int line = lines > 0 ? view->height - lines : 0;
2726                 int end = line + ABS(lines);
2727
2728                 scrollok(view->win, TRUE);
2729                 wscrl(view->win, lines);
2730                 scrollok(view->win, FALSE);
2731
2732                 while (line < end && draw_view_line(view, line))
2733                         line++;
2734
2735                 if (redraw_current_line)
2736                         draw_view_line(view, view->pos.lineno - view->pos.offset);
2737                 wnoutrefresh(view->win);
2738         }
2739
2740         view->has_scrolled = TRUE;
2741         report_clear();
2742 }
2743
2744 /* Scroll frontend */
2745 static void
2746 scroll_view(struct view *view, enum request request)
2747 {
2748         int lines = 1;
2749
2750         assert(view_is_displayed(view));
2751
2752         switch (request) {
2753         case REQ_SCROLL_FIRST_COL:
2754                 view->pos.col = 0;
2755                 redraw_view_from(view, 0);
2756                 report_clear();
2757                 return;
2758         case REQ_SCROLL_LEFT:
2759                 if (view->pos.col == 0) {
2760                         report("Cannot scroll beyond the first column");
2761                         return;
2762                 }
2763                 if (view->pos.col <= apply_step(opt_hscroll, view->width))
2764                         view->pos.col = 0;
2765                 else
2766                         view->pos.col -= apply_step(opt_hscroll, view->width);
2767                 redraw_view_from(view, 0);
2768                 report_clear();
2769                 return;
2770         case REQ_SCROLL_RIGHT:
2771                 view->pos.col += apply_step(opt_hscroll, view->width);
2772                 redraw_view(view);
2773                 report_clear();
2774                 return;
2775         case REQ_SCROLL_PAGE_DOWN:
2776                 lines = view->height;
2777         case REQ_SCROLL_LINE_DOWN:
2778                 if (view->pos.offset + lines > view->lines)
2779                         lines = view->lines - view->pos.offset;
2780
2781                 if (lines == 0 || view->pos.offset + view->height >= view->lines) {
2782                         report("Cannot scroll beyond the last line");
2783                         return;
2784                 }
2785                 break;
2786
2787         case REQ_SCROLL_PAGE_UP:
2788                 lines = view->height;
2789         case REQ_SCROLL_LINE_UP:
2790                 if (lines > view->pos.offset)
2791                         lines = view->pos.offset;
2792
2793                 if (lines == 0) {
2794                         report("Cannot scroll beyond the first line");
2795                         return;
2796                 }
2797
2798                 lines = -lines;
2799                 break;
2800
2801         default:
2802                 die("request %d not handled in switch", request);
2803         }
2804
2805         do_scroll_view(view, lines);
2806 }
2807
2808 /* Cursor moving */
2809 static void
2810 move_view(struct view *view, enum request request)
2811 {
2812         int scroll_steps = 0;
2813         int steps;
2814
2815         switch (request) {
2816         case REQ_MOVE_FIRST_LINE:
2817                 steps = -view->pos.lineno;
2818                 break;
2819
2820         case REQ_MOVE_LAST_LINE:
2821                 steps = view->lines - view->pos.lineno - 1;
2822                 break;
2823
2824         case REQ_MOVE_PAGE_UP:
2825                 steps = view->height > view->pos.lineno
2826                       ? -view->pos.lineno : -view->height;
2827                 break;
2828
2829         case REQ_MOVE_PAGE_DOWN:
2830                 steps = view->pos.lineno + view->height >= view->lines
2831                       ? view->lines - view->pos.lineno - 1 : view->height;
2832                 break;
2833
2834         case REQ_MOVE_UP:
2835         case REQ_PREVIOUS:
2836                 steps = -1;
2837                 break;
2838
2839         case REQ_MOVE_DOWN:
2840         case REQ_NEXT:
2841                 steps = 1;
2842                 break;
2843
2844         default:
2845                 die("request %d not handled in switch", request);
2846         }
2847
2848         if (steps <= 0 && view->pos.lineno == 0) {
2849                 report("Cannot move beyond the first line");
2850                 return;
2851
2852         } else if (steps >= 0 && view->pos.lineno + 1 >= view->lines) {
2853                 report("Cannot move beyond the last line");
2854                 return;
2855         }
2856
2857         /* Move the current line */
2858         view->pos.lineno += steps;
2859         assert(0 <= view->pos.lineno && view->pos.lineno < view->lines);
2860
2861         /* Check whether the view needs to be scrolled */
2862         if (view->pos.lineno < view->pos.offset ||
2863             view->pos.lineno >= view->pos.offset + view->height) {
2864                 scroll_steps = steps;
2865                 if (steps < 0 && -steps > view->pos.offset) {
2866                         scroll_steps = -view->pos.offset;
2867
2868                 } else if (steps > 0) {
2869                         if (view->pos.lineno == view->lines - 1 &&
2870                             view->lines > view->height) {
2871                                 scroll_steps = view->lines - view->pos.offset - 1;
2872                                 if (scroll_steps >= view->height)
2873                                         scroll_steps -= view->height - 1;
2874                         }
2875                 }
2876         }
2877
2878         if (!view_is_displayed(view)) {
2879                 view->pos.offset += scroll_steps;
2880                 assert(0 <= view->pos.offset && view->pos.offset < view->lines);
2881                 view->ops->select(view, &view->line[view->pos.lineno]);
2882                 return;
2883         }
2884
2885         /* Repaint the old "current" line if we be scrolling */
2886         if (ABS(steps) < view->height)
2887                 draw_view_line(view, view->pos.lineno - steps - view->pos.offset);
2888
2889         if (scroll_steps) {
2890                 do_scroll_view(view, scroll_steps);
2891                 return;
2892         }
2893
2894         /* Draw the current line */
2895         draw_view_line(view, view->pos.lineno - view->pos.offset);
2896
2897         wnoutrefresh(view->win);
2898         report_clear();
2899 }
2900
2901
2902 /*
2903  * Searching
2904  */
2905
2906 static void search_view(struct view *view, enum request request);
2907
2908 static bool
2909 grep_text(struct view *view, const char *text[])
2910 {
2911         regmatch_t pmatch;
2912         size_t i;
2913
2914         for (i = 0; text[i]; i++)
2915                 if (*text[i] && !regexec(view->regex, text[i], 1, &pmatch, 0))
2916                         return TRUE;
2917         return FALSE;
2918 }
2919
2920 static void
2921 select_view_line(struct view *view, unsigned long lineno)
2922 {
2923         struct position old = view->pos;
2924
2925         if (goto_view_line(view, view->pos.offset, lineno)) {
2926                 if (view_is_displayed(view)) {
2927                         if (old.offset != view->pos.offset) {
2928                                 redraw_view(view);
2929                         } else {
2930                                 draw_view_line(view, old.lineno - view->pos.offset);
2931                                 draw_view_line(view, view->pos.lineno - view->pos.offset);
2932                                 wnoutrefresh(view->win);
2933                         }
2934                 } else {
2935                         view->ops->select(view, &view->line[view->pos.lineno]);
2936                 }
2937         }
2938 }
2939
2940 static void
2941 find_next(struct view *view, enum request request)
2942 {
2943         unsigned long lineno = view->pos.lineno;
2944         int direction;
2945
2946         if (!*view->grep) {
2947                 if (!*opt_search)
2948                         report("No previous search");
2949                 else
2950                         search_view(view, request);
2951                 return;
2952         }
2953
2954         switch (request) {
2955         case REQ_SEARCH:
2956         case REQ_FIND_NEXT:
2957                 direction = 1;
2958                 break;
2959
2960         case REQ_SEARCH_BACK:
2961         case REQ_FIND_PREV:
2962                 direction = -1;
2963                 break;
2964
2965         default:
2966                 return;
2967         }
2968
2969         if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
2970                 lineno += direction;
2971
2972         /* Note, lineno is unsigned long so will wrap around in which case it
2973          * will become bigger than view->lines. */
2974         for (; lineno < view->lines; lineno += direction) {
2975                 if (view->ops->grep(view, &view->line[lineno])) {
2976                         select_view_line(view, lineno);
2977                         report("Line %ld matches '%s'", lineno + 1, view->grep);
2978                         return;
2979                 }
2980         }
2981
2982         report("No match found for '%s'", view->grep);
2983 }
2984
2985 static void
2986 search_view(struct view *view, enum request request)
2987 {
2988         int regex_err;
2989         int regex_flags = opt_ignore_case ? REG_ICASE : 0;
2990
2991         if (view->regex) {
2992                 regfree(view->regex);
2993                 *view->grep = 0;
2994         } else {
2995                 view->regex = calloc(1, sizeof(*view->regex));
2996                 if (!view->regex)
2997                         return;
2998         }
2999
3000         regex_err = regcomp(view->regex, opt_search, REG_EXTENDED | regex_flags);
3001         if (regex_err != 0) {
3002                 char buf[SIZEOF_STR] = "unknown error";
3003
3004                 regerror(regex_err, view->regex, buf, sizeof(buf));
3005                 report("Search failed: %s", buf);
3006                 return;
3007         }
3008
3009         string_copy(view->grep, opt_search);
3010
3011         find_next(view, request);
3012 }
3013
3014 /*
3015  * Incremental updating
3016  */
3017
3018 static inline bool
3019 check_position(struct position *pos)
3020 {
3021         return pos->lineno || pos->col || pos->offset;
3022 }
3023
3024 static inline void
3025 clear_position(struct position *pos)
3026 {
3027         memset(pos, 0, sizeof(*pos));
3028 }
3029
3030 static void
3031 reset_view(struct view *view)
3032 {
3033         int i;
3034
3035         for (i = 0; i < view->lines; i++)
3036                 if (!view->line[i].dont_free)
3037                         free(view->line[i].data);
3038         free(view->line);
3039
3040         view->prev_pos = view->pos;
3041         clear_position(&view->pos);
3042
3043         view->line = NULL;
3044         view->lines  = 0;
3045         view->vid[0] = 0;
3046         view->custom_lines = 0;
3047         view->update_secs = 0;
3048 }
3049
3050 struct format_context {
3051         struct view *view;
3052         char buf[SIZEOF_STR];
3053         size_t bufpos;
3054         bool file_filter;
3055 };
3056
3057 static bool
3058 format_expand_arg(struct format_context *format, const char *name)
3059 {
3060         static struct {
3061                 const char *name;
3062                 size_t namelen;
3063                 const char *value;
3064                 const char *value_if_empty;
3065         } vars[] = {
3066 #define FORMAT_VAR(name, value, value_if_empty) \
3067         { name, STRING_SIZE(name), value, value_if_empty }
3068                 FORMAT_VAR("%(directory)",      opt_path,       "."),
3069                 FORMAT_VAR("%(file)",           opt_file,       ""),
3070                 FORMAT_VAR("%(ref)",            opt_ref,        "HEAD"),
3071                 FORMAT_VAR("%(head)",           ref_head,       ""),
3072                 FORMAT_VAR("%(commit)",         ref_commit,     ""),
3073                 FORMAT_VAR("%(blob)",           ref_blob,       ""),
3074                 FORMAT_VAR("%(branch)",         ref_branch,     ""),
3075         };
3076         int i;
3077
3078         if (!prefixcmp(name, "%(prompt")) {
3079                 const char *value = read_prompt("Command argument: ");
3080
3081                 return string_format_from(format->buf, &format->bufpos, "%s", value);
3082         }
3083
3084         for (i = 0; i < ARRAY_SIZE(vars); i++) {
3085                 const char *value;
3086
3087                 if (strncmp(name, vars[i].name, vars[i].namelen))
3088                         continue;
3089
3090                 if (vars[i].value == opt_file && !format->file_filter)
3091                         return TRUE;
3092
3093                 value = *vars[i].value ? vars[i].value : vars[i].value_if_empty;
3094                 if (!*value)
3095                         return TRUE;
3096
3097                 return string_format_from(format->buf, &format->bufpos, "%s", value);
3098         }
3099
3100         report("Unknown replacement: `%s`", name);
3101         return NULL;
3102 }
3103
3104 static bool
3105 format_append_arg(struct format_context *format, const char ***dst_argv, const char *arg)
3106 {
3107         format->bufpos = 0;
3108
3109         while (arg) {
3110                 char *next = strstr(arg, "%(");
3111                 int len = next ? next - arg : strlen(arg);
3112
3113                 if (len && !string_format_from(format->buf, &format->bufpos, "%.*s", len, arg))
3114                         return FALSE;
3115
3116                 if (next && !format_expand_arg(format, next))
3117                         return FALSE;
3118
3119                 arg = next ? strchr(next, ')') + 1 : NULL;
3120         }
3121
3122         return argv_append(dst_argv, format->buf);
3123 }
3124
3125 static bool
3126 format_append_argv(struct format_context *format, const char ***dst_argv, const char *src_argv[])
3127 {
3128         int argc;
3129
3130         if (!src_argv)
3131                 return TRUE;
3132
3133         for (argc = 0; src_argv[argc]; argc++)
3134                 if (!format_append_arg(format, dst_argv, src_argv[argc]))
3135                         return FALSE;
3136
3137         return src_argv[argc] == NULL;
3138 }
3139
3140 static bool
3141 format_argv(struct view *view, const char ***dst_argv, const char *src_argv[], bool first, bool file_filter)
3142 {
3143         struct format_context format = { view, "", 0, file_filter };
3144         int argc;
3145
3146         argv_free(*dst_argv);
3147
3148         for (argc = 0; src_argv[argc]; argc++) {
3149                 const char *arg = src_argv[argc];
3150
3151                 if (!strcmp(arg, "%(fileargs)")) {
3152                         if (file_filter && !argv_append_array(dst_argv, opt_file_argv))
3153                                 break;
3154
3155                 } else if (!strcmp(arg, "%(diffargs)")) {
3156                         if (!format_append_argv(&format, dst_argv, opt_diff_argv))
3157                                 break;
3158
3159                 } else if (!strcmp(arg, "%(blameargs)")) {
3160                         if (!format_append_argv(&format, dst_argv, opt_blame_argv))
3161                                 break;
3162
3163                 } else if (!strcmp(arg, "%(revargs)") ||
3164                            (first && !strcmp(arg, "%(commit)"))) {
3165                         if (!argv_append_array(dst_argv, opt_rev_argv))
3166                                 break;
3167
3168                 } else if (!format_append_arg(&format, dst_argv, arg)) {
3169                         break;
3170                 }
3171         }
3172
3173         return src_argv[argc] == NULL;
3174 }
3175
3176 static bool
3177 restore_view_position(struct view *view)
3178 {
3179         /* A view without a previous view is the first view */
3180         if (!view->prev && opt_lineno && opt_lineno <= view->lines) {
3181                 select_view_line(view, opt_lineno - 1);
3182                 opt_lineno = 0;
3183         }
3184
3185         /* Ensure that the view position is in a valid state. */
3186         if (!check_position(&view->prev_pos) ||
3187             (view->pipe && view->lines <= view->prev_pos.lineno))
3188                 return goto_view_line(view, view->pos.offset, view->pos.lineno);
3189
3190         /* Changing the view position cancels the restoring. */
3191         /* FIXME: Changing back to the first line is not detected. */
3192         if (check_position(&view->pos)) {
3193                 clear_position(&view->prev_pos);
3194                 return FALSE;
3195         }
3196
3197         if (goto_view_line(view, view->prev_pos.offset, view->prev_pos.lineno) &&
3198             view_is_displayed(view))
3199                 werase(view->win);
3200
3201         view->pos.col = view->prev_pos.col;
3202         clear_position(&view->prev_pos);
3203
3204         return TRUE;
3205 }
3206
3207 static void
3208 end_update(struct view *view, bool force)
3209 {
3210         if (!view->pipe)
3211                 return;
3212         while (!view->ops->read(view, NULL))
3213                 if (!force)
3214                         return;
3215         if (force)
3216                 io_kill(view->pipe);
3217         io_done(view->pipe);
3218         view->pipe = NULL;
3219 }
3220
3221 static void
3222 setup_update(struct view *view, const char *vid)
3223 {
3224         reset_view(view);
3225         /* XXX: Do not use string_copy_rev(), it copies until first space. */
3226         string_ncopy(view->vid, vid, strlen(vid));
3227         view->pipe = &view->io;
3228         view->start_time = time(NULL);
3229 }
3230
3231 static bool
3232 begin_update(struct view *view, const char *dir, const char **argv, enum open_flags flags)
3233 {
3234         bool use_stdin = view_has_flags(view, VIEW_STDIN) && opt_stdin;
3235         bool extra = !!(flags & (OPEN_EXTRA));
3236         bool reload = !!(flags & (OPEN_RELOAD | OPEN_REFRESH | OPEN_PREPARED | OPEN_EXTRA));
3237         bool refresh = flags & (OPEN_REFRESH | OPEN_PREPARED);
3238         enum io_type io_type = use_stdin ? IO_RD_STDIN : IO_RD;
3239
3240         opt_stdin = FALSE;
3241
3242         if ((!reload && !strcmp(view->vid, view->id)) ||
3243             ((flags & OPEN_REFRESH) && view->unrefreshable))
3244                 return TRUE;
3245
3246         if (view->pipe) {
3247                 if (extra)
3248                         io_done(view->pipe);
3249                 else
3250                         end_update(view, TRUE);
3251         }
3252
3253         view->unrefreshable = use_stdin;
3254
3255         if (!refresh && argv) {
3256                 bool file_filter = !view_has_flags(view, VIEW_FILE_FILTER) || opt_file_filter;
3257
3258                 view->dir = dir;
3259                 if (!format_argv(view, &view->argv, argv, !view->prev, file_filter)) {
3260                         report("Failed to format %s arguments", view->name);
3261                         return FALSE;
3262                 }
3263
3264                 /* Put the current ref_* value to the view title ref
3265                  * member. This is needed by the blob view. Most other
3266                  * views sets it automatically after loading because the
3267                  * first line is a commit line. */
3268                 string_copy_rev(view->ref, view->id);
3269         }
3270
3271         if (view->argv && view->argv[0] &&
3272             !io_run(&view->io, io_type, view->dir, view->argv)) {
3273                 report("Failed to open %s view", view->name);
3274                 return FALSE;
3275         }
3276
3277         if (!extra)
3278                 setup_update(view, view->id);
3279
3280         return TRUE;
3281 }
3282
3283 static bool
3284 update_view(struct view *view)
3285 {
3286         char *line;
3287         /* Clear the view and redraw everything since the tree sorting
3288          * might have rearranged things. */
3289         bool redraw = view->lines == 0;
3290         bool can_read = TRUE;
3291         struct encoding *encoding = view->encoding ? view->encoding : opt_encoding;
3292
3293         if (!view->pipe)
3294                 return TRUE;
3295
3296         if (!io_can_read(view->pipe, FALSE)) {
3297                 if (view->lines == 0 && view_is_displayed(view)) {
3298                         time_t secs = time(NULL) - view->start_time;
3299
3300                         if (secs > 1 && secs > view->update_secs) {
3301                                 if (view->update_secs == 0)
3302                                         redraw_view(view);
3303                                 update_view_title(view);
3304                                 view->update_secs = secs;
3305                         }
3306                 }
3307                 return TRUE;
3308         }
3309
3310         for (; (line = io_get(view->pipe, '\n', can_read)); can_read = FALSE) {
3311                 if (encoding) {
3312                         line = encoding_convert(encoding, line);
3313                 }
3314
3315                 if (!view->ops->read(view, line)) {
3316                         report("Allocation failure");
3317                         end_update(view, TRUE);
3318                         return FALSE;
3319                 }
3320         }
3321
3322         {
3323                 unsigned long lines = view->lines;
3324                 int digits;
3325
3326                 for (digits = 0; lines; digits++)
3327                         lines /= 10;
3328
3329                 /* Keep the displayed view in sync with line number scaling. */
3330                 if (digits != view->digits) {
3331                         view->digits = digits;
3332                         if (opt_line_number || view_has_flags(view, VIEW_ALWAYS_LINENO))
3333                                 redraw = TRUE;
3334                 }
3335         }
3336
3337         if (io_error(view->pipe)) {
3338                 report("Failed to read: %s", io_strerror(view->pipe));
3339                 end_update(view, TRUE);
3340
3341         } else if (io_eof(view->pipe)) {
3342                 end_update(view, FALSE);
3343         }
3344
3345         if (restore_view_position(view))
3346                 redraw = TRUE;
3347
3348         if (!view_is_displayed(view))
3349                 return TRUE;
3350
3351         if (redraw)
3352                 redraw_view_from(view, 0);
3353         else
3354                 redraw_view_dirty(view);
3355
3356         /* Update the title _after_ the redraw so that if the redraw picks up a
3357          * commit reference in view->ref it'll be available here. */
3358         update_view_title(view);
3359         return TRUE;
3360 }
3361
3362 DEFINE_ALLOCATOR(realloc_lines, struct line, 256)
3363
3364 static struct line *
3365 add_line(struct view *view, const void *data, enum line_type type, size_t data_size, bool custom)
3366 {
3367         struct line *line;
3368
3369         if (!realloc_lines(&view->line, view->lines, 1))
3370                 return NULL;
3371
3372         if (data_size) {
3373                 void *alloc_data = calloc(1, data_size);
3374
3375                 if (!alloc_data)
3376                         return NULL;
3377
3378                 if (data)
3379                         memcpy(alloc_data, data, data_size);
3380                 data = alloc_data;
3381         }
3382
3383         line = &view->line[view->lines++];
3384         memset(line, 0, sizeof(*line));
3385         line->type = type;
3386         line->data = (void *) data;
3387         line->dirty = 1;
3388
3389         if (custom)
3390                 view->custom_lines++;
3391         else
3392                 line->lineno = view->lines - view->custom_lines;
3393
3394         return line;
3395 }
3396
3397 static struct line *
3398 add_line_alloc_(struct view *view, void **ptr, enum line_type type, size_t data_size, bool custom)
3399 {
3400         struct line *line = add_line(view, NULL, type, data_size, custom);
3401
3402         if (line)
3403                 *ptr = line->data;
3404         return line;
3405 }
3406
3407 #define add_line_alloc(view, data_ptr, type, extra_size, custom) \
3408         add_line_alloc_(view, (void **) data_ptr, type, sizeof(**data_ptr) + extra_size, custom)
3409
3410 static struct line *
3411 add_line_nodata(struct view *view, enum line_type type)
3412 {
3413         return add_line(view, NULL, type, 0, FALSE);
3414 }
3415
3416 static struct line *
3417 add_line_static_data(struct view *view, const void *data, enum line_type type)
3418 {
3419         struct line *line = add_line(view, data, type, 0, FALSE);
3420
3421         if (line)
3422                 line->dont_free = TRUE;
3423         return line;
3424 }
3425
3426 static struct line *
3427 add_line_text(struct view *view, const char *text, enum line_type type)
3428 {
3429         return add_line(view, text, type, strlen(text) + 1, FALSE);
3430 }
3431
3432 static struct line * PRINTF_LIKE(3, 4)
3433 add_line_format(struct view *view, enum line_type type, const char *fmt, ...)
3434 {
3435         char buf[SIZEOF_STR];
3436         int retval;
3437
3438         FORMAT_BUFFER(buf, sizeof(buf), fmt, retval, FALSE);
3439         return retval >= 0 ? add_line_text(view, buf, type) : NULL;
3440 }
3441
3442 /*
3443  * View opening
3444  */
3445
3446 static void
3447 split_view(struct view *prev, struct view *view)
3448 {
3449         display[1] = view;
3450         current_view = opt_focus_child ? 1 : 0;
3451         view->parent = prev;
3452         resize_display();
3453
3454         if (prev->pos.lineno - prev->pos.offset >= prev->height) {
3455                 /* Take the title line into account. */
3456                 int lines = prev->pos.lineno - prev->pos.offset - prev->height + 1;
3457
3458                 /* Scroll the view that was split if the current line is
3459                  * outside the new limited view. */
3460                 do_scroll_view(prev, lines);
3461         }
3462
3463         if (view != prev && view_is_displayed(prev)) {
3464                 /* "Blur" the previous view. */
3465                 update_view_title(prev);
3466         }
3467 }
3468
3469 static void
3470 maximize_view(struct view *view, bool redraw)
3471 {
3472         memset(display, 0, sizeof(display));
3473         current_view = 0;
3474         display[current_view] = view;
3475         resize_display();
3476         if (redraw) {
3477                 redraw_display(FALSE);
3478                 report_clear();
3479         }
3480 }
3481
3482 static void
3483 load_view(struct view *view, struct view *prev, enum open_flags flags)
3484 {
3485         if (view->pipe)
3486                 end_update(view, TRUE);
3487         if (view->ops->private_size) {
3488                 if (!view->private)
3489                         view->private = calloc(1, view->ops->private_size);
3490                 else
3491                         memset(view->private, 0, view->ops->private_size);
3492         }
3493
3494         /* When prev == view it means this is the first loaded view. */
3495         if (prev && view != prev) {
3496                 view->prev = prev;
3497         }
3498
3499         if (!view->ops->open(view, flags))
3500                 return;
3501
3502         if (prev) {
3503                 bool split = !!(flags & OPEN_SPLIT);
3504
3505                 if (split) {
3506                         split_view(prev, view);
3507                 } else {
3508                         maximize_view(view, FALSE);
3509                 }
3510         }
3511
3512         restore_view_position(view);
3513
3514         if (view->pipe && view->lines == 0) {
3515                 /* Clear the old view and let the incremental updating refill
3516                  * the screen. */
3517                 werase(view->win);
3518                 if (!(flags & (OPEN_RELOAD | OPEN_REFRESH)))
3519                         clear_position(&view->prev_pos);
3520                 report_clear();
3521         } else if (view_is_displayed(view)) {
3522                 redraw_view(view);
3523                 report_clear();
3524         }
3525 }
3526
3527 #define refresh_view(view) load_view(view, NULL, OPEN_REFRESH)
3528 #define reload_view(view) load_view(view, NULL, OPEN_RELOAD)
3529
3530 static void
3531 open_view(struct view *prev, enum request request, enum open_flags flags)
3532 {
3533         bool reload = !!(flags & (OPEN_RELOAD | OPEN_PREPARED));
3534         struct view *view = VIEW(request);
3535         int nviews = displayed_views();
3536
3537         assert(flags ^ OPEN_REFRESH);
3538
3539         if (view == prev && nviews == 1 && !reload) {
3540                 report("Already in %s view", view->name);
3541                 return;
3542         }
3543
3544         if (!view_has_flags(view, VIEW_NO_GIT_DIR) && !opt_git_dir[0]) {
3545                 report("The %s view is disabled in pager view", view->name);
3546                 return;
3547         }
3548
3549         load_view(view, prev ? prev : view, flags);
3550 }
3551
3552 static void
3553 open_argv(struct view *prev, struct view *view, const char *argv[], const char *dir, enum open_flags flags)
3554 {
3555         enum request request = view - views + REQ_OFFSET + 1;
3556
3557         if (view->pipe)
3558                 end_update(view, TRUE);
3559         view->dir = dir;
3560
3561         if (!argv_copy(&view->argv, argv)) {
3562                 report("Failed to open %s view: %s", view->name, io_strerror(&view->io));
3563         } else {
3564                 open_view(prev, request, flags | OPEN_PREPARED);
3565         }
3566 }
3567
3568 static bool
3569 open_external_viewer(const char *argv[], const char *dir, bool confirm, const char *notice)
3570 {
3571         bool ok;
3572
3573         def_prog_mode();           /* save current tty modes */
3574         endwin();                  /* restore original tty modes */
3575         ok = io_run_fg(argv, dir);
3576         if (confirm) {
3577                 if (!ok && *notice)
3578                         fprintf(stderr, "%s", notice);
3579                 fprintf(stderr, "Press Enter to continue");
3580                 getc(opt_tty);
3581         }
3582         reset_prog_mode();
3583         redraw_display(TRUE);
3584         return ok;
3585 }
3586
3587 static void
3588 open_mergetool(const char *file)
3589 {
3590         const char *mergetool_argv[] = { "git", "mergetool", file, NULL };
3591
3592         open_external_viewer(mergetool_argv, opt_cdup, TRUE, "");
3593 }
3594
3595 #define EDITOR_LINENO_MSG \
3596         "*** Your editor reported an error while opening the file.\n" \
3597         "*** This is probably because it doesn't support the line\n" \
3598         "*** number argument added automatically. The line number\n" \
3599         "*** has been disabled for now. You can permanently disable\n" \
3600         "*** it by adding the following line to ~/.tigrc\n" \
3601         "***    set editor-line-number = no\n"
3602
3603 static void
3604 open_editor(const char *file, unsigned int lineno)
3605 {
3606         const char *editor_argv[SIZEOF_ARG + 3] = { "vi", file, NULL };
3607         char editor_cmd[SIZEOF_STR];
3608         char lineno_cmd[SIZEOF_STR];
3609         const char *editor;
3610         int argc = 0;
3611
3612         editor = getenv("GIT_EDITOR");
3613         if (!editor && *opt_editor)
3614                 editor = opt_editor;
3615         if (!editor)
3616                 editor = getenv("VISUAL");
3617         if (!editor)
3618                 editor = getenv("EDITOR");
3619         if (!editor)
3620                 editor = "vi";
3621
3622         string_ncopy(editor_cmd, editor, strlen(editor));
3623         if (!argv_from_string_no_quotes(editor_argv, &argc, editor_cmd)) {
3624                 report("Failed to read editor command");
3625                 return;
3626         }
3627
3628         if (lineno && opt_editor_lineno && string_format(lineno_cmd, "+%u", lineno))
3629                 editor_argv[argc++] = lineno_cmd;
3630         editor_argv[argc] = file;
3631         if (!open_external_viewer(editor_argv, opt_cdup, TRUE, EDITOR_LINENO_MSG))
3632                 opt_editor_lineno = FALSE;
3633 }
3634
3635 static enum request run_prompt_command(struct view *view, char *cmd);
3636
3637 static enum request
3638 open_run_request(struct view *view, enum request request)
3639 {
3640         struct run_request *req = get_run_request(request);
3641         const char **argv = NULL;
3642         bool confirmed = FALSE;
3643
3644         request = REQ_NONE;
3645
3646         if (!req) {
3647                 report("Unknown run request");
3648                 return request;
3649         }
3650
3651         if (format_argv(view, &argv, req->argv, FALSE, TRUE)) {
3652                 if (req->internal) {
3653                         char cmd[SIZEOF_STR];
3654
3655                         if (argv_to_string(argv, cmd, sizeof(cmd), " ")) {
3656                                 request = run_prompt_command(view, cmd);
3657                         }
3658                 }
3659                 else {
3660                         confirmed = !req->confirm;
3661
3662                         if (req->confirm) {
3663                                 char cmd[SIZEOF_STR], prompt[SIZEOF_STR];
3664
3665                                 if (argv_to_string(argv, cmd, sizeof(cmd), " ") &&
3666                                     string_format(prompt, "Run `%s`?", cmd) &&
3667                                     prompt_yesno(prompt)) {
3668                                         confirmed = TRUE;
3669                                 }
3670                         }
3671
3672                         if (confirmed && argv_remove_quotes(argv)) {
3673                                 if (req->silent)
3674                                         io_run_bg(argv);
3675                                 else
3676                                         open_external_viewer(argv, NULL, !req->exit, "");
3677                         }
3678                 }
3679         }
3680
3681         if (argv)
3682                 argv_free(argv);
3683         free(argv);
3684
3685         if (request == REQ_NONE) {
3686                 if (req->confirm && !confirmed)
3687                         request = REQ_NONE;
3688
3689                 else if (req->exit)
3690                         request = REQ_QUIT;
3691
3692                 else if (!view->unrefreshable)
3693                         request = REQ_REFRESH;
3694         }
3695         return request;
3696 }
3697
3698 /*
3699  * User request switch noodle
3700  */
3701
3702 static int
3703 view_driver(struct view *view, enum request request)
3704 {
3705         int i;
3706
3707         if (request == REQ_NONE)
3708                 return TRUE;
3709
3710         if (request > REQ_NONE) {
3711                 request = open_run_request(view, request);
3712
3713                 // exit quickly rather than going through view_request and back
3714                 if (request == REQ_QUIT)
3715                         return FALSE;
3716         }
3717
3718         request = view_request(view, request);
3719         if (request == REQ_NONE)
3720                 return TRUE;
3721
3722         switch (request) {
3723         case REQ_MOVE_UP:
3724         case REQ_MOVE_DOWN:
3725         case REQ_MOVE_PAGE_UP:
3726         case REQ_MOVE_PAGE_DOWN:
3727         case REQ_MOVE_FIRST_LINE:
3728         case REQ_MOVE_LAST_LINE:
3729                 move_view(view, request);
3730                 break;
3731
3732         case REQ_SCROLL_FIRST_COL:
3733         case REQ_SCROLL_LEFT:
3734         case REQ_SCROLL_RIGHT:
3735         case REQ_SCROLL_LINE_DOWN:
3736         case REQ_SCROLL_LINE_UP:
3737         case REQ_SCROLL_PAGE_DOWN:
3738         case REQ_SCROLL_PAGE_UP:
3739                 scroll_view(view, request);
3740                 break;
3741
3742         case REQ_VIEW_MAIN:
3743         case REQ_VIEW_DIFF:
3744         case REQ_VIEW_LOG:
3745         case REQ_VIEW_TREE:
3746         case REQ_VIEW_HELP:
3747         case REQ_VIEW_BRANCH:
3748         case REQ_VIEW_BLAME:
3749         case REQ_VIEW_BLOB:
3750         case REQ_VIEW_STATUS:
3751         case REQ_VIEW_STAGE:
3752         case REQ_VIEW_PAGER:
3753                 open_view(view, request, OPEN_DEFAULT);
3754                 break;
3755
3756         case REQ_NEXT:
3757         case REQ_PREVIOUS:
3758                 if (view->parent) {
3759                         int line;
3760
3761                         view = view->parent;
3762                         line = view->pos.lineno;
3763                         move_view(view, request);
3764                         if (view_is_displayed(view))
3765                                 update_view_title(view);
3766                         if (line != view->pos.lineno)
3767                                 view_request(view, REQ_ENTER);
3768                 } else {
3769                         move_view(view, request);
3770                 }
3771                 break;
3772
3773         case REQ_VIEW_NEXT:
3774         {
3775                 int nviews = displayed_views();
3776                 int next_view = (current_view + 1) % nviews;
3777
3778                 if (next_view == current_view) {
3779                         report("Only one view is displayed");
3780                         break;
3781                 }
3782
3783                 current_view = next_view;
3784                 /* Blur out the title of the previous view. */
3785                 update_view_title(view);
3786                 report_clear();
3787                 break;
3788         }
3789         case REQ_REFRESH:
3790                 report("Refreshing is not yet supported for the %s view", view->name);
3791                 break;
3792
3793         case REQ_MAXIMIZE:
3794                 if (displayed_views() == 2)
3795                         maximize_view(view, TRUE);
3796                 break;
3797
3798         case REQ_OPTIONS:
3799         case REQ_TOGGLE_LINENO:
3800         case REQ_TOGGLE_DATE:
3801         case REQ_TOGGLE_AUTHOR:
3802         case REQ_TOGGLE_FILENAME:
3803         case REQ_TOGGLE_GRAPHIC:
3804         case REQ_TOGGLE_REV_GRAPH:
3805         case REQ_TOGGLE_REFS:
3806         case REQ_TOGGLE_CHANGES:
3807         case REQ_TOGGLE_IGNORE_SPACE:
3808         case REQ_TOGGLE_ID:
3809         case REQ_TOGGLE_FILES:
3810         case REQ_TOGGLE_TITLE_OVERFLOW:
3811                 {
3812                         char action[SIZEOF_STR] = "";
3813                         bool reload = toggle_option(view, request, action);
3814
3815                         if (reload && view_has_flags(view, VIEW_DIFF_LIKE))
3816                                 reload_view(view);
3817                         else
3818                                 redraw_display(FALSE);
3819
3820                         if (*action)
3821                                 report("%s", action);
3822                 }
3823                 break;
3824
3825         case REQ_TOGGLE_SORT_FIELD:
3826         case REQ_TOGGLE_SORT_ORDER:
3827                 report("Sorting is not yet supported for the %s view", view->name);
3828                 break;
3829
3830         case REQ_DIFF_CONTEXT_UP:
3831         case REQ_DIFF_CONTEXT_DOWN:
3832                 report("Changing the diff context is not yet supported for the %s view", view->name);
3833                 break;
3834
3835         case REQ_SEARCH:
3836         case REQ_SEARCH_BACK:
3837                 search_view(view, request);
3838                 break;
3839
3840         case REQ_FIND_NEXT:
3841         case REQ_FIND_PREV:
3842                 find_next(view, request);
3843                 break;
3844
3845         case REQ_STOP_LOADING:
3846                 foreach_view(view, i) {
3847                         if (view->pipe)
3848                                 report("Stopped loading the %s view", view->name),
3849                         end_update(view, TRUE);
3850                 }
3851                 break;
3852
3853         case REQ_SHOW_VERSION:
3854                 report("tig-%s (built %s)", TIG_VERSION, __DATE__);
3855                 return TRUE;
3856
3857         case REQ_SCREEN_REDRAW:
3858                 redraw_display(TRUE);
3859                 break;
3860
3861         case REQ_EDIT:
3862                 report("Nothing to edit");
3863                 break;
3864
3865         case REQ_ENTER:
3866                 report("Nothing to enter");
3867                 break;
3868
3869         case REQ_VIEW_CLOSE:
3870                 /* XXX: Mark closed views by letting view->prev point to the
3871                  * view itself. Parents to closed view should never be
3872                  * followed. */
3873                 if (view->prev && view->prev != view) {
3874                         maximize_view(view->prev, TRUE);
3875                         view->prev = view;
3876                         break;
3877                 }
3878                 /* Fall-through */
3879         case REQ_QUIT:
3880                 return FALSE;
3881
3882         default:
3883                 report("Unknown key, press %s for help",
3884                        get_view_key(view, REQ_VIEW_HELP));
3885                 return TRUE;
3886         }
3887
3888         return TRUE;
3889 }
3890
3891
3892 /*
3893  * View backend utilities
3894  */
3895
3896 enum sort_field {
3897         ORDERBY_NAME,
3898         ORDERBY_DATE,
3899         ORDERBY_AUTHOR,
3900 };
3901
3902 struct sort_state {
3903         const enum sort_field *fields;
3904         size_t size, current;
3905         bool reverse;
3906 };
3907
3908 #define SORT_STATE(fields) { fields, ARRAY_SIZE(fields), 0 }
3909 #define get_sort_field(state) ((state).fields[(state).current])
3910 #define sort_order(state, result) ((state).reverse ? -(result) : (result))
3911
3912 static void
3913 sort_view(struct view *view, enum request request, struct sort_state *state,
3914           int (*compare)(const void *, const void *))
3915 {
3916         switch (request) {
3917         case REQ_TOGGLE_SORT_FIELD:
3918                 state->current = (state->current + 1) % state->size;
3919                 break;
3920
3921         case REQ_TOGGLE_SORT_ORDER:
3922                 state->reverse = !state->reverse;
3923                 break;
3924         default:
3925                 die("Not a sort request");
3926         }
3927
3928         qsort(view->line, view->lines, sizeof(*view->line), compare);
3929         redraw_view(view);
3930 }
3931
3932 static bool
3933 update_diff_context(enum request request)
3934 {
3935         int diff_context = opt_diff_context;
3936
3937         switch (request) {
3938         case REQ_DIFF_CONTEXT_UP:
3939                 opt_diff_context += 1;
3940                 update_diff_context_arg(opt_diff_context);
3941                 break;
3942
3943         case REQ_DIFF_CONTEXT_DOWN:
3944                 if (opt_diff_context == 0) {
3945                         report("Diff context cannot be less than zero");
3946                         break;
3947                 }
3948                 opt_diff_context -= 1;
3949                 update_diff_context_arg(opt_diff_context);
3950                 break;
3951
3952         default:
3953                 die("Not a diff context request");
3954         }
3955
3956         return diff_context != opt_diff_context;
3957 }
3958
3959 DEFINE_ALLOCATOR(realloc_authors, struct ident *, 256)
3960
3961 /* Small author cache to reduce memory consumption. It uses binary
3962  * search to lookup or find place to position new entries. No entries
3963  * are ever freed. */
3964 static struct ident *
3965 get_author(const char *name, const char *email)
3966 {
3967         static struct ident **authors;
3968         static size_t authors_size;
3969         int from = 0, to = authors_size - 1;
3970         struct ident *ident;
3971
3972         while (from <= to) {
3973                 size_t pos = (to + from) / 2;
3974                 int cmp = strcmp(name, authors[pos]->name);
3975
3976                 if (!cmp)
3977                         return authors[pos];
3978
3979                 if (cmp < 0)
3980                         to = pos - 1;
3981                 else
3982                         from = pos + 1;
3983         }
3984
3985         if (!realloc_authors(&authors, authors_size, 1))
3986                 return NULL;
3987         ident = calloc(1, sizeof(*ident));
3988         if (!ident)
3989                 return NULL;
3990         ident->name = strdup(name);
3991         ident->email = strdup(email);
3992         if (!ident->name || !ident->email) {
3993                 free((void *) ident->name);
3994                 free(ident);
3995                 return NULL;
3996         }
3997
3998         memmove(authors + from + 1, authors + from, (authors_size - from) * sizeof(*authors));
3999         authors[from] = ident;
4000         authors_size++;
4001
4002         return ident;
4003 }
4004
4005 static void
4006 parse_timesec(struct time *time, const char *sec)
4007 {
4008         time->sec = (time_t) atol(sec);
4009 }
4010
4011 static void
4012 parse_timezone(struct time *time, const char *zone)
4013 {
4014         long tz;
4015
4016         tz  = ('0' - zone[1]) * 60 * 60 * 10;
4017         tz += ('0' - zone[2]) * 60 * 60;
4018         tz += ('0' - zone[3]) * 60 * 10;
4019         tz += ('0' - zone[4]) * 60;
4020
4021         if (zone[0] == '-')
4022                 tz = -tz;
4023
4024         time->tz = tz;
4025         time->sec -= tz;
4026 }
4027
4028 /* Parse author lines where the name may be empty:
4029  *      author  <email@address.tld> 1138474660 +0100
4030  */
4031 static void
4032 parse_author_line(char *ident, const struct ident **author, struct time *time)
4033 {
4034         char *nameend = strchr(ident, '<');
4035         char *emailend = strchr(ident, '>');
4036         const char *name, *email = "";
4037
4038         if (nameend && emailend)
4039                 *nameend = *emailend = 0;
4040         name = chomp_string(ident);
4041         if (nameend)
4042                 email = chomp_string(nameend + 1);
4043         if (!*name)
4044                 name = *email ? email : unknown_ident.name;
4045         if (!*email)
4046                 email = *name ? name : unknown_ident.email;
4047
4048         *author = get_author(name, email);
4049
4050         /* Parse epoch and timezone */
4051         if (time && emailend && emailend[1] == ' ') {
4052                 char *secs = emailend + 2;
4053                 char *zone = strchr(secs, ' ');
4054
4055                 parse_timesec(time, secs);
4056
4057                 if (zone && strlen(zone) == STRING_SIZE(" +0700"))
4058                         parse_timezone(time, zone + 1);
4059         }
4060 }
4061
4062 static struct line *
4063 find_line_by_type(struct view *view, struct line *line, enum line_type type, int direction)
4064 {
4065         for (; view_has_line(view, line); line += direction)
4066                 if (line->type == type)
4067                         return line;
4068
4069         return NULL;
4070 }
4071
4072 #define find_prev_line_by_type(view, line, type) \
4073         find_line_by_type(view, line, type, -1)
4074
4075 #define find_next_line_by_type(view, line, type) \
4076         find_line_by_type(view, line, type, 1)
4077
4078 /*
4079  * Blame
4080  */
4081
4082 struct blame_commit {
4083         char id[SIZEOF_REV];            /* SHA1 ID. */
4084         char title[128];                /* First line of the commit message. */
4085         const struct ident *author;     /* Author of the commit. */
4086         struct time time;               /* Date from the author ident. */
4087         char filename[128];             /* Name of file. */
4088         char parent_id[SIZEOF_REV];     /* Parent/previous SHA1 ID. */
4089         char parent_filename[128];      /* Parent/previous name of file. */
4090 };
4091
4092 struct blame_header {
4093         char id[SIZEOF_REV];            /* SHA1 ID. */
4094         size_t orig_lineno;
4095         size_t lineno;
4096         size_t group;
4097 };
4098
4099 static bool
4100 parse_number(const char **posref, size_t *number, size_t min, size_t max)
4101 {
4102         const char *pos = *posref;
4103
4104         *posref = NULL;
4105         pos = strchr(pos + 1, ' ');
4106         if (!pos || !isdigit(pos[1]))
4107                 return FALSE;
4108         *number = atoi(pos + 1);
4109         if (*number < min || *number > max)
4110                 return FALSE;
4111
4112         *posref = pos;
4113         return TRUE;
4114 }
4115
4116 static bool
4117 parse_blame_header(struct blame_header *header, const char *text, size_t max_lineno)
4118 {
4119         const char *pos = text + SIZEOF_REV - 2;
4120
4121         if (strlen(text) <= SIZEOF_REV || pos[1] != ' ')
4122                 return FALSE;
4123
4124         string_ncopy(header->id, text, SIZEOF_REV);
4125
4126         if (!parse_number(&pos, &header->orig_lineno, 1, 9999999) ||
4127             !parse_number(&pos, &header->lineno, 1, max_lineno) ||
4128             !parse_number(&pos, &header->group, 1, max_lineno - header->lineno + 1))
4129                 return FALSE;
4130
4131         return TRUE;
4132 }
4133
4134 static bool
4135 match_blame_header(const char *name, char **line)
4136 {
4137         size_t namelen = strlen(name);
4138         bool matched = !strncmp(name, *line, namelen);
4139
4140         if (matched)
4141                 *line += namelen;
4142
4143         return matched;
4144 }
4145
4146 static bool
4147 parse_blame_info(struct blame_commit *commit, char *line)
4148 {
4149         if (match_blame_header("author ", &line)) {
4150                 parse_author_line(line, &commit->author, NULL);
4151
4152         } else if (match_blame_header("author-time ", &line)) {
4153                 parse_timesec(&commit->time, line);
4154
4155         } else if (match_blame_header("author-tz ", &line)) {
4156                 parse_timezone(&commit->time, line);
4157
4158         } else if (match_blame_header("summary ", &line)) {
4159                 string_ncopy(commit->title, line, strlen(line));
4160
4161         } else if (match_blame_header("previous ", &line)) {
4162                 if (strlen(line) <= SIZEOF_REV)
4163                         return FALSE;
4164                 string_copy_rev(commit->parent_id, line);
4165                 line += SIZEOF_REV;
4166                 string_ncopy(commit->parent_filename, line, strlen(line));
4167
4168         } else if (match_blame_header("filename ", &line)) {
4169                 string_ncopy(commit->filename, line, strlen(line));
4170                 return TRUE;
4171         }
4172
4173         return FALSE;
4174 }
4175
4176 /*
4177  * Pager backend
4178  */
4179
4180 static bool
4181 pager_draw(struct view *view, struct line *line, unsigned int lineno)
4182 {
4183         if (draw_lineno(view, lineno))
4184                 return TRUE;
4185
4186         if (line->wrapped && draw_text(view, LINE_DELIMITER, "+"))
4187                 return TRUE;
4188
4189         draw_text(view, line->type, line->data);
4190         return TRUE;
4191 }
4192
4193 static bool
4194 add_describe_ref(char *buf, size_t *bufpos, const char *commit_id, const char *sep)
4195 {
4196         const char *describe_argv[] = { "git", "describe", commit_id, NULL };
4197         char ref[SIZEOF_STR];
4198
4199         if (!io_run_buf(describe_argv, ref, sizeof(ref)) || !*ref)
4200                 return TRUE;
4201
4202         /* This is the only fatal call, since it can "corrupt" the buffer. */
4203         if (!string_nformat(buf, SIZEOF_STR, bufpos, "%s%s", sep, ref))
4204                 return FALSE;
4205
4206         return TRUE;
4207 }
4208
4209 static void
4210 add_pager_refs(struct view *view, const char *commit_id)
4211 {
4212         char buf[SIZEOF_STR];
4213         struct ref_list *list;
4214         size_t bufpos = 0, i;
4215         const char *sep = "Refs: ";
4216         bool is_tag = FALSE;
4217
4218         list = get_ref_list(commit_id);
4219         if (!list) {
4220                 if (view_has_flags(view, VIEW_ADD_DESCRIBE_REF))
4221                         goto try_add_describe_ref;
4222                 return;
4223         }
4224
4225         for (i = 0; i < list->size; i++) {
4226                 struct ref *ref = list->refs[i];
4227                 const char *fmt = ref->tag    ? "%s[%s]" :
4228                                   ref->remote ? "%s<%s>" : "%s%s";
4229
4230                 if (!string_format_from(buf, &bufpos, fmt, sep, ref->name))
4231                         return;
4232                 sep = ", ";
4233                 if (ref->tag)
4234                         is_tag = TRUE;
4235         }
4236
4237         if (!is_tag && view_has_flags(view, VIEW_ADD_DESCRIBE_REF)) {
4238 try_add_describe_ref:
4239                 /* Add <tag>-g<commit_id> "fake" reference. */
4240                 if (!add_describe_ref(buf, &bufpos, commit_id, sep))
4241                         return;
4242         }
4243
4244         if (bufpos == 0)
4245                 return;
4246
4247         add_line_text(view, buf, LINE_PP_REFS);
4248 }
4249
4250 static struct line *
4251 pager_wrap_line(struct view *view, const char *data, enum line_type type)
4252 {
4253         size_t first_line = 0;
4254         bool has_first_line = FALSE;
4255         size_t datalen = strlen(data);
4256         size_t lineno = 0;
4257
4258         while (datalen > 0 || !has_first_line) {
4259                 bool wrapped = !!first_line;
4260                 size_t linelen = string_expanded_length(data, datalen, opt_tab_size, view->width - !!wrapped);
4261                 struct line *line;
4262                 char *text;
4263
4264                 line = add_line(view, NULL, type, linelen + 1, wrapped);
4265                 if (!line)
4266                         break;
4267                 if (!has_first_line) {
4268                         first_line = view->lines - 1;
4269                         has_first_line = TRUE;
4270                 }
4271
4272                 if (!wrapped)
4273                         lineno = line->lineno;
4274
4275                 line->wrapped = wrapped;
4276                 line->lineno = lineno;
4277                 text = line->data;
4278                 if (linelen)
4279                         strncpy(text, data, linelen);
4280                 text[linelen] = 0;
4281
4282                 datalen -= linelen;
4283                 data += linelen;
4284         }
4285
4286         return has_first_line ? &view->line[first_line] : NULL;
4287 }
4288
4289 static bool
4290 pager_common_read(struct view *view, const char *data, enum line_type type)
4291 {
4292         struct line *line;
4293
4294         if (!data)
4295                 return TRUE;
4296
4297         if (opt_wrap_lines) {
4298                 line = pager_wrap_line(view, data, type);
4299         } else {
4300                 line = add_line_text(view, data, type);
4301         }
4302
4303         if (!line)
4304                 return FALSE;
4305
4306         if (line->type == LINE_COMMIT && view_has_flags(view, VIEW_ADD_PAGER_REFS))
4307                 add_pager_refs(view, data + STRING_SIZE("commit "));
4308
4309         return TRUE;
4310 }
4311
4312 static bool
4313 pager_read(struct view *view, char *data)
4314 {
4315         if (!data)
4316                 return TRUE;
4317
4318         return pager_common_read(view, data, get_line_type(data));
4319 }
4320
4321 static enum request
4322 pager_request(struct view *view, enum request request, struct line *line)
4323 {
4324         int split = 0;
4325
4326         if (request != REQ_ENTER)
4327                 return request;
4328
4329         if (line->type == LINE_COMMIT && view_has_flags(view, VIEW_OPEN_DIFF)) {
4330                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
4331                 split = 1;
4332         }
4333
4334         /* Always scroll the view even if it was split. That way
4335          * you can use Enter to scroll through the log view and
4336          * split open each commit diff. */
4337         scroll_view(view, REQ_SCROLL_LINE_DOWN);
4338
4339         /* FIXME: A minor workaround. Scrolling the view will call report_clear()
4340          * but if we are scrolling a non-current view this won't properly
4341          * update the view title. */
4342         if (split)
4343                 update_view_title(view);
4344
4345         return REQ_NONE;
4346 }
4347
4348 static bool
4349 pager_grep(struct view *view, struct line *line)
4350 {
4351         const char *text[] = { line->data, NULL };
4352
4353         return grep_text(view, text);
4354 }
4355
4356 static void
4357 pager_select(struct view *view, struct line *line)
4358 {
4359         if (line->type == LINE_COMMIT) {
4360                 char *text = (char *)line->data + STRING_SIZE("commit ");
4361
4362                 if (!view_has_flags(view, VIEW_NO_REF))
4363                         string_copy_rev(view->ref, text);
4364                 string_copy_rev(ref_commit, text);
4365         }
4366 }
4367
4368 static bool
4369 pager_open(struct view *view, enum open_flags flags)
4370 {
4371         if (display[0] == NULL) {
4372                 if (!io_open(&view->io, "%s", ""))
4373                         die("Failed to open stdin");
4374                 flags = OPEN_PREPARED;
4375
4376         } else if (!view->pipe && !view->lines && !(flags & OPEN_PREPARED)) {
4377                 report("No pager content, press %s to run command from prompt",
4378                         get_view_key(view, REQ_PROMPT));
4379                 return FALSE;
4380         }
4381
4382         return begin_update(view, NULL, NULL, flags);
4383 }
4384
4385 static struct view_ops pager_ops = {
4386         "line",
4387         { "pager" },
4388         VIEW_OPEN_DIFF | VIEW_NO_REF | VIEW_NO_GIT_DIR,
4389         0,
4390         pager_open,
4391         pager_read,
4392         pager_draw,
4393         pager_request,
4394         pager_grep,
4395         pager_select,
4396 };
4397
4398 static bool
4399 log_open(struct view *view, enum open_flags flags)
4400 {
4401         static const char *log_argv[] = {
4402                 "git", "log", opt_encoding_arg, "--no-color", "--cc", "--stat", "-n100", "%(head)", NULL
4403         };
4404
4405         return begin_update(view, NULL, log_argv, flags);
4406 }
4407
4408 static enum request
4409 log_request(struct view *view, enum request request, struct line *line)
4410 {
4411         switch (request) {
4412         case REQ_REFRESH:
4413                 load_refs();
4414                 refresh_view(view);
4415                 return REQ_NONE;
4416         default:
4417                 return pager_request(view, request, line);
4418         }
4419 }
4420
4421 static struct view_ops log_ops = {
4422         "line",
4423         { "log" },
4424         VIEW_ADD_PAGER_REFS | VIEW_OPEN_DIFF | VIEW_SEND_CHILD_ENTER,
4425         0,
4426         log_open,
4427         pager_read,
4428         pager_draw,
4429         log_request,
4430         pager_grep,
4431         pager_select,
4432 };
4433
4434 struct diff_state {
4435         bool after_commit_title;
4436         bool reading_diff_stat;
4437         bool combined_diff;
4438 };
4439
4440 #define DIFF_LINE_COMMIT_TITLE 1
4441
4442 static bool
4443 diff_open(struct view *view, enum open_flags flags)
4444 {
4445         static const char *diff_argv[] = {
4446                 "git", "show", opt_encoding_arg, "--pretty=fuller", "--no-color", "--root",
4447                         "--patch-with-stat",
4448                         opt_notes_arg, opt_diff_context_arg, opt_ignore_space_arg,
4449                         "%(diffargs)", "%(commit)", "--", "%(fileargs)", NULL
4450         };
4451
4452         return begin_update(view, NULL, diff_argv, flags);
4453 }
4454
4455 static bool
4456 diff_common_read(struct view *view, const char *data, struct diff_state *state)
4457 {
4458         enum line_type type = get_line_type(data);
4459
4460         if (!view->lines && type != LINE_COMMIT)
4461                 state->reading_diff_stat = TRUE;
4462
4463         if (state->reading_diff_stat) {
4464                 size_t len = strlen(data);
4465                 char *pipe = strchr(data, '|');
4466                 bool has_histogram = data[len - 1] == '-' || data[len - 1] == '+';
4467                 bool has_bin_diff = pipe && strstr(pipe, "Bin") && strstr(pipe, "->");
4468                 bool has_rename = data[len - 1] == '0' && (strstr(data, "=>") || !strncmp(data, " ...", 4));
4469
4470                 if (pipe && (has_histogram || has_bin_diff || has_rename)) {
4471                         return add_line_text(view, data, LINE_DIFF_STAT) != NULL;
4472                 } else {
4473                         state->reading_diff_stat = FALSE;
4474                 }
4475
4476         } else if (!strcmp(data, "---")) {
4477                 state->reading_diff_stat = TRUE;
4478         }
4479
4480         if (!state->after_commit_title && !prefixcmp(data, "    ")) {
4481                 struct line *line = add_line_text(view, data, LINE_COMMIT);
4482
4483                 if (line)
4484                         line->user_flags |= DIFF_LINE_COMMIT_TITLE;
4485                 state->after_commit_title = TRUE;
4486                 return line != NULL;
4487         }
4488
4489         if (type == LINE_DIFF_HEADER) {
4490                 const int len = line_info[LINE_DIFF_HEADER].linelen;
4491
4492                 if (!strncmp(data + len, "combined ", strlen("combined ")) ||
4493                     !strncmp(data + len, "cc ", strlen("cc ")))
4494                         state->combined_diff = TRUE;
4495         }
4496
4497         /* ADD2 and DEL2 are only valid in combined diff hunks */
4498         if (!state->combined_diff && (type == LINE_DIFF_ADD2 || type == LINE_DIFF_DEL2))
4499                 type = LINE_DEFAULT;
4500
4501         return pager_common_read(view, data, type);
4502 }
4503
4504 static bool
4505 diff_find_stat_entry(struct view *view, struct line *line, enum line_type type)
4506 {
4507         struct line *marker = find_next_line_by_type(view, line, type);
4508
4509         return marker &&
4510                 line == find_prev_line_by_type(view, marker, LINE_DIFF_HEADER);
4511 }
4512
4513 static enum request
4514 diff_common_enter(struct view *view, enum request request, struct line *line)
4515 {
4516         if (line->type == LINE_DIFF_STAT) {
4517                 int file_number = 0;
4518
4519                 while (view_has_line(view, line) && line->type == LINE_DIFF_STAT) {
4520                         file_number++;
4521                         line--;
4522                 }
4523
4524                 for (line = view->line; view_has_line(view, line); line++) {
4525                         line = find_next_line_by_type(view, line, LINE_DIFF_HEADER);
4526                         if (!line)
4527                                 break;
4528
4529                         if (diff_find_stat_entry(view, line, LINE_DIFF_INDEX)
4530                             || diff_find_stat_entry(view, line, LINE_DIFF_SIMILARITY)) {
4531                                 if (file_number == 1) {
4532                                         break;
4533                                 }
4534                                 file_number--;
4535                         }
4536                 }
4537
4538                 if (!line) {
4539                         report("Failed to find file diff");
4540                         return REQ_NONE;
4541                 }
4542
4543                 select_view_line(view, line - view->line);
4544                 report_clear();
4545                 return REQ_NONE;
4546
4547         } else {
4548                 return pager_request(view, request, line);
4549         }
4550 }
4551
4552 static bool
4553 diff_common_draw_part(struct view *view, enum line_type *type, char **text, char c, enum line_type next_type)
4554 {
4555         char *sep = strchr(*text, c);
4556
4557         if (sep != NULL) {
4558                 *sep = 0;
4559                 draw_text(view, *type, *text);
4560                 *sep = c;
4561                 *text = sep;
4562                 *type = next_type;
4563         }
4564
4565         return sep != NULL;
4566 }
4567
4568 static bool
4569 diff_common_draw(struct view *view, struct line *line, unsigned int lineno)
4570 {
4571         char *text = line->data;
4572         enum line_type type = line->type;
4573
4574         if (draw_lineno(view, lineno))
4575                 return TRUE;
4576
4577         if (line->wrapped && draw_text(view, LINE_DELIMITER, "+"))
4578                 return TRUE;
4579
4580         if (type == LINE_DIFF_STAT) {
4581                 diff_common_draw_part(view, &type, &text, '|', LINE_DEFAULT);
4582                 if (diff_common_draw_part(view, &type, &text, 'B', LINE_DEFAULT)) {
4583                         /* Handle binary diffstat: Bin <deleted> -> <added> bytes */
4584                         diff_common_draw_part(view, &type, &text, ' ', LINE_DIFF_DEL);
4585                         diff_common_draw_part(view, &type, &text, '-', LINE_DEFAULT);
4586                         diff_common_draw_part(view, &type, &text, ' ', LINE_DIFF_ADD);
4587                         diff_common_draw_part(view, &type, &text, 'b', LINE_DEFAULT);
4588
4589                 } else {
4590                         diff_common_draw_part(view, &type, &text, '+', LINE_DIFF_ADD);
4591                         diff_common_draw_part(view, &type, &text, '-', LINE_DIFF_DEL);
4592                 }
4593         }
4594
4595         if (line->user_flags & DIFF_LINE_COMMIT_TITLE)
4596                 draw_commit_title(view, text, 4);
4597         else
4598                 draw_text(view, type, text);
4599         return TRUE;
4600 }
4601
4602 static bool
4603 diff_read(struct view *view, char *data)
4604 {
4605         struct diff_state *state = view->private;
4606
4607         if (!data) {
4608                 /* Fall back to retry if no diff will be shown. */
4609                 if (view->lines == 0 && opt_file_argv) {
4610                         int pos = argv_size(view->argv)
4611                                 - argv_size(opt_file_argv) - 1;
4612
4613                         if (pos > 0 && !strcmp(view->argv[pos], "--")) {
4614                                 for (; view->argv[pos]; pos++) {
4615                                         free((void *) view->argv[pos]);
4616                                         view->argv[pos] = NULL;
4617                                 }
4618
4619                                 if (view->pipe)
4620                                         io_done(view->pipe);
4621                                 if (io_run(&view->io, IO_RD, view->dir, view->argv))
4622                                         return FALSE;
4623                         }
4624                 }
4625                 return TRUE;
4626         }
4627
4628         return diff_common_read(view, data, state);
4629 }
4630
4631 static bool
4632 diff_blame_line(const char *ref, const char *file, unsigned long lineno,
4633                 struct blame_header *header, struct blame_commit *commit)
4634 {
4635         char line_arg[SIZEOF_STR];
4636         const char *blame_argv[] = {
4637                 "git", "blame", opt_encoding_arg, "-p", line_arg, ref, "--", file, NULL
4638         };
4639         struct io io;
4640         bool ok = FALSE;
4641         char *buf;
4642
4643         if (!string_format(line_arg, "-L%ld,+1", lineno))
4644                 return FALSE;
4645
4646         if (!io_run(&io, IO_RD, opt_cdup, blame_argv))
4647                 return FALSE;
4648
4649         while ((buf = io_get(&io, '\n', TRUE))) {
4650                 if (header) {
4651                         if (!parse_blame_header(header, buf, 9999999))
4652                                 break;
4653                         header = NULL;
4654
4655                 } else if (parse_blame_info(commit, buf)) {
4656                         ok = TRUE;
4657                         break;
4658                 }
4659         }
4660
4661         if (io_error(&io))
4662                 ok = FALSE;
4663
4664         io_done(&io);
4665         return ok;
4666 }
4667
4668 static unsigned int
4669 diff_get_lineno(struct view *view, struct line *line)
4670 {
4671         const struct line *header, *chunk;
4672         const char *data;
4673         unsigned int lineno;
4674
4675         /* Verify that we are after a diff header and one of its chunks */
4676         header = find_prev_line_by_type(view, line, LINE_DIFF_HEADER);
4677         chunk = find_prev_line_by_type(view, line, LINE_DIFF_CHUNK);
4678         if (!header || !chunk || chunk < header)
4679                 return 0;
4680
4681         /*
4682          * In a chunk header, the number after the '+' sign is the number of its
4683          * following line, in the new version of the file. We increment this
4684          * number for each non-deletion line, until the given line position.
4685          */
4686         data = strchr(chunk->data, '+');
4687         if (!data)
4688                 return 0;
4689
4690         lineno = atoi(data);
4691         chunk++;
4692         while (chunk++ < line)
4693                 if (chunk->type != LINE_DIFF_DEL)
4694                         lineno++;
4695
4696         return lineno;
4697 }
4698
4699 static bool
4700 parse_chunk_lineno(int *lineno, const char *chunk, int marker)
4701 {
4702         return prefixcmp(chunk, "@@ -") ||
4703                !(chunk = strchr(chunk, marker)) ||
4704                parse_int(lineno, chunk + 1, 0, 9999999) != OPT_OK;
4705 }
4706
4707 static enum request
4708 diff_trace_origin(struct view *view, struct line *line)
4709 {
4710         struct line *diff = find_prev_line_by_type(view, line, LINE_DIFF_HEADER);
4711         struct line *chunk = find_prev_line_by_type(view, line, LINE_DIFF_CHUNK);
4712         const char *chunk_data;
4713         int chunk_marker = line->type == LINE_DIFF_DEL ? '-' : '+';
4714         int lineno = 0;
4715         const char *file = NULL;
4716         char ref[SIZEOF_REF];
4717         struct blame_header header;
4718         struct blame_commit commit;
4719
4720         if (!diff || !chunk || chunk == line) {
4721                 report("The line to trace must be inside a diff chunk");
4722                 return REQ_NONE;
4723         }
4724
4725         for (; diff < line && !file; diff++) {
4726                 const char *data = diff->data;
4727
4728                 if (!prefixcmp(data, "--- a/")) {
4729                         file = data + STRING_SIZE("--- a/");
4730                         break;
4731                 }
4732         }
4733
4734         if (diff == line || !file) {
4735                 report("Failed to read the file name");
4736                 return REQ_NONE;
4737         }
4738
4739         chunk_data = chunk->data;
4740
4741         if (parse_chunk_lineno(&lineno, chunk_data, chunk_marker)) {
4742                 report("Failed to read the line number");
4743                 return REQ_NONE;
4744         }
4745
4746         if (lineno == 0) {
4747                 report("This is the origin of the line");
4748                 return REQ_NONE;
4749         }
4750
4751         for (chunk += 1; chunk < line; chunk++) {
4752                 if (chunk->type == LINE_DIFF_ADD) {
4753                         lineno += chunk_marker == '+';
4754                 } else if (chunk->type == LINE_DIFF_DEL) {
4755                         lineno += chunk_marker == '-';
4756                 } else {
4757                         lineno++;
4758                 }
4759         }
4760
4761         if (chunk_marker == '+')
4762                 string_copy(ref, view->vid);
4763         else
4764                 string_format(ref, "%s^", view->vid);
4765
4766         if (!diff_blame_line(ref, file, lineno, &header, &commit)) {
4767                 report("Failed to read blame data");
4768                 return REQ_NONE;
4769         }
4770
4771         string_ncopy(opt_file, commit.filename, strlen(commit.filename));
4772         string_copy(opt_ref, header.id);
4773         opt_goto_line = header.orig_lineno - 1;
4774
4775         return REQ_VIEW_BLAME;
4776 }
4777
4778 static const char *
4779 diff_get_pathname(struct view *view, struct line *line)
4780 {
4781         const struct line *header;
4782         const char *dst = NULL;
4783         const char *prefixes[] = { " b/", "cc ", "combined " };
4784         int i;
4785
4786         header = find_prev_line_by_type(view, line, LINE_DIFF_HEADER);
4787         if (!header)
4788                 return NULL;
4789
4790         for (i = 0; i < ARRAY_SIZE(prefixes) && !dst; i++)
4791                 dst = strstr(header->data, prefixes[i]);
4792
4793         return dst ? dst + strlen(prefixes[--i]) : NULL;
4794 }
4795
4796 static enum request
4797 diff_request(struct view *view, enum request request, struct line *line)
4798 {
4799         const char *file;
4800
4801         switch (request) {
4802         case REQ_VIEW_BLAME:
4803                 return diff_trace_origin(view, line);
4804
4805         case REQ_DIFF_CONTEXT_UP:
4806         case REQ_DIFF_CONTEXT_DOWN:
4807                 if (!update_diff_context(request))
4808                         return REQ_NONE;
4809                 reload_view(view);
4810                 return REQ_NONE;
4811
4812
4813         case REQ_EDIT:
4814                 file = diff_get_pathname(view, line);
4815                 if (!file || access(file, R_OK))
4816                         return pager_request(view, request, line);
4817                 open_editor(file, diff_get_lineno(view, line));
4818                 return REQ_NONE;
4819
4820         case REQ_ENTER:
4821                 return diff_common_enter(view, request, line);
4822
4823         case REQ_REFRESH:
4824                 reload_view(view);
4825                 return REQ_NONE;
4826
4827         default:
4828                 return pager_request(view, request, line);
4829         }
4830 }
4831
4832 static void
4833 diff_select(struct view *view, struct line *line)
4834 {
4835         if (line->type == LINE_DIFF_STAT) {
4836                 string_format(view->ref, "Press '%s' to jump to file diff",
4837                               get_view_key(view, REQ_ENTER));
4838         } else {
4839                 const char *file = diff_get_pathname(view, line);
4840
4841                 if (file) {
4842                         string_format(view->ref, "Changes to '%s'", file);
4843                         string_format(opt_file, "%s", file);
4844                         ref_blob[0] = 0;
4845                 } else {
4846                         string_ncopy(view->ref, view->id, strlen(view->id));
4847                         pager_select(view, line);
4848                 }
4849         }
4850 }
4851
4852 static struct view_ops diff_ops = {
4853         "line",
4854         { "diff" },
4855         VIEW_DIFF_LIKE | VIEW_ADD_DESCRIBE_REF | VIEW_ADD_PAGER_REFS | VIEW_STDIN | VIEW_FILE_FILTER,
4856         sizeof(struct diff_state),
4857         diff_open,
4858         diff_read,
4859         diff_common_draw,
4860         diff_request,
4861         pager_grep,
4862         diff_select,
4863 };
4864
4865 /*
4866  * Help backend
4867  */
4868
4869 static bool
4870 help_draw(struct view *view, struct line *line, unsigned int lineno)
4871 {
4872         if (line->type == LINE_HELP_KEYMAP) {
4873                 struct keymap *keymap = line->data;
4874
4875                 draw_formatted(view, line->type, "[%c] %s bindings",
4876                                keymap->hidden ? '+' : '-', keymap->name);
4877                 return TRUE;
4878         } else {
4879                 return pager_draw(view, line, lineno);
4880         }
4881 }
4882
4883 static bool
4884 help_open_keymap_title(struct view *view, struct keymap *keymap)
4885 {
4886         add_line_static_data(view, keymap, LINE_HELP_KEYMAP);
4887         return keymap->hidden;
4888 }
4889
4890 static void
4891 help_open_keymap(struct view *view, struct keymap *keymap)
4892 {
4893         const char *group = NULL;
4894         char buf[SIZEOF_STR];
4895         bool add_title = TRUE;
4896         int i;
4897
4898         for (i = 0; i < ARRAY_SIZE(req_info); i++) {
4899                 const char *key = NULL;
4900
4901                 if (req_info[i].request == REQ_NONE)
4902                         continue;
4903
4904                 if (!req_info[i].request) {
4905                         group = req_info[i].help;
4906                         continue;
4907                 }
4908
4909                 key = get_keys(keymap, req_info[i].request, TRUE);
4910                 if (!key || !*key)
4911                         continue;
4912
4913                 if (add_title && help_open_keymap_title(view, keymap))
4914                         return;
4915                 add_title = FALSE;
4916
4917                 if (group) {
4918                         add_line_text(view, group, LINE_HELP_GROUP);
4919                         group = NULL;
4920                 }
4921
4922                 add_line_format(view, LINE_DEFAULT, "    %-25s %-20s %s", key,
4923                                 enum_name(req_info[i]), req_info[i].help);
4924         }
4925
4926         group = "External commands:";
4927
4928         for (i = 0; i < run_requests; i++) {
4929                 struct run_request *req = get_run_request(REQ_NONE + i + 1);
4930                 const char *key;
4931
4932                 if (!req || req->keymap != keymap)
4933                         continue;
4934
4935                 key = get_key_name(req->key);
4936                 if (!*key)
4937                         key = "(no key defined)";
4938
4939                 if (add_title && help_open_keymap_title(view, keymap))
4940                         return;
4941                 add_title = FALSE;
4942
4943                 if (group) {
4944                         add_line_text(view, group, LINE_HELP_GROUP);
4945                         group = NULL;
4946                 }
4947
4948                 if (!argv_to_string(req->argv, buf, sizeof(buf), " "))
4949                         return;
4950
4951                 add_line_format(view, LINE_DEFAULT, "    %-25s `%s`", key, buf);
4952         }
4953 }
4954
4955 static bool
4956 help_open(struct view *view, enum open_flags flags)
4957 {
4958         struct keymap *keymap;
4959
4960         reset_view(view);
4961         add_line_text(view, "Quick reference for tig keybindings:", LINE_DEFAULT);
4962         add_line_text(view, "", LINE_DEFAULT);
4963
4964         for (keymap = keymaps; keymap; keymap = keymap->next)
4965                 help_open_keymap(view, keymap);
4966
4967         return TRUE;
4968 }
4969
4970 static enum request
4971 help_request(struct view *view, enum request request, struct line *line)
4972 {
4973         switch (request) {
4974         case REQ_ENTER:
4975                 if (line->type == LINE_HELP_KEYMAP) {
4976                         struct keymap *keymap = line->data;
4977
4978                         keymap->hidden = !keymap->hidden;
4979                         refresh_view(view);
4980                 }
4981
4982                 return REQ_NONE;
4983         default:
4984                 return pager_request(view, request, line);
4985         }
4986 }
4987
4988 static struct view_ops help_ops = {
4989         "line",
4990         { "help" },
4991         VIEW_NO_GIT_DIR,
4992         0,
4993         help_open,
4994         NULL,
4995         help_draw,
4996         help_request,
4997         pager_grep,
4998         pager_select,
4999 };