Add support for loading repo config
[tig] / tig.c
1 /* Copyright (c) 2006 Jonas Fonseca <fonseca@diku.dk>
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 as
5  * published by the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  */
12 /**
13  * TIG(1)
14  * ======
15  *
16  * NAME
17  * ----
18  * tig - text-mode interface for git
19  *
20  * SYNOPSIS
21  * --------
22  * [verse]
23  * tig [options]
24  * tig [options] [--] [git log options]
25  * tig [options] log  [git log options]
26  * tig [options] diff [git diff options]
27  * tig [options] show [git show options]
28  * tig [options] <    [git command output]
29  *
30  * DESCRIPTION
31  * -----------
32  * Browse changes in a git repository. Additionally, tig(1) can also act
33  * as a pager for output of various git commands.
34  *
35  * When browsing repositories, tig(1) uses the underlying git commands
36  * to present the user with various views, such as summarized commit log
37  * and showing the commit with the log message, diffstat, and the diff.
38  *
39  * Using tig(1) as a pager, it will display input from stdin and try
40  * to colorize it.
41  **/
42
43 #ifndef VERSION
44 #define VERSION "tig-0.3"
45 #endif
46
47 #ifndef DEBUG
48 #define NDEBUG
49 #endif
50
51 #include <assert.h>
52 #include <errno.h>
53 #include <ctype.h>
54 #include <signal.h>
55 #include <stdarg.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <time.h>
61
62 #include <curses.h>
63
64 static void die(const char *err, ...);
65 static void report(const char *msg, ...);
66 static void set_nonblocking_input(bool loading);
67 static size_t utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed);
68
69 #define ABS(x)          ((x) >= 0  ? (x) : -(x))
70 #define MIN(x, y)       ((x) < (y) ? (x) :  (y))
71
72 #define ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))
73 #define STRING_SIZE(x)  (sizeof(x) - 1)
74
75 #define SIZEOF_REF      256     /* Size of symbolic or SHA1 ID. */
76 #define SIZEOF_CMD      1024    /* Size of command buffer. */
77
78 /* This color name can be used to refer to the default term colors. */
79 #define COLOR_DEFAULT   (-1)
80
81 #define TIG_HELP        "(d)iff, (l)og, (m)ain, (q)uit, (h)elp"
82
83 /* The format and size of the date column in the main view. */
84 #define DATE_FORMAT     "%Y-%m-%d %H:%M"
85 #define DATE_COLS       STRING_SIZE("2006-04-29 14:21 ")
86
87 #define AUTHOR_COLS     20
88
89 /* The default interval between line numbers. */
90 #define NUMBER_INTERVAL 1
91
92 #define TABSIZE         8
93
94 #define SCALE_SPLIT_VIEW(height)        ((height) * 2 / 3)
95
96 /* Some ascii-shorthands fitted into the ncurses namespace. */
97 #define KEY_TAB         '\t'
98 #define KEY_RETURN      '\r'
99 #define KEY_ESC         27
100
101
102 /* User action requests. */
103 enum request {
104         /* Offset all requests to avoid conflicts with ncurses getch values. */
105         REQ_OFFSET = KEY_MAX + 1,
106
107         /* XXX: Keep the view request first and in sync with views[]. */
108         REQ_VIEW_MAIN,
109         REQ_VIEW_DIFF,
110         REQ_VIEW_LOG,
111         REQ_VIEW_HELP,
112         REQ_VIEW_PAGER,
113
114         REQ_ENTER,
115         REQ_QUIT,
116         REQ_PROMPT,
117         REQ_SCREEN_REDRAW,
118         REQ_SCREEN_RESIZE,
119         REQ_SCREEN_UPDATE,
120         REQ_SHOW_VERSION,
121         REQ_STOP_LOADING,
122         REQ_TOGGLE_LINE_NUMBERS,
123         REQ_VIEW_NEXT,
124         REQ_VIEW_CLOSE,
125         REQ_NEXT,
126         REQ_PREVIOUS,
127
128         REQ_MOVE_UP,
129         REQ_MOVE_DOWN,
130         REQ_MOVE_PAGE_UP,
131         REQ_MOVE_PAGE_DOWN,
132         REQ_MOVE_FIRST_LINE,
133         REQ_MOVE_LAST_LINE,
134
135         REQ_SCROLL_LINE_UP,
136         REQ_SCROLL_LINE_DOWN,
137         REQ_SCROLL_PAGE_UP,
138         REQ_SCROLL_PAGE_DOWN,
139 };
140
141 struct ref {
142         char *name;             /* Ref name; tag or head names are shortened. */
143         char id[41];            /* Commit SHA1 ID */
144         unsigned int tag:1;     /* Is it a tag? */
145         unsigned int next:1;    /* For ref lists: are there more refs? */
146 };
147
148 static struct ref **get_refs(char *id);
149
150
151 /*
152  * String helpers
153  */
154
155 static inline void
156 string_ncopy(char *dst, const char *src, int dstlen)
157 {
158         strncpy(dst, src, dstlen - 1);
159         dst[dstlen - 1] = 0;
160
161 }
162
163 /* Shorthand for safely copying into a fixed buffer. */
164 #define string_copy(dst, src) \
165         string_ncopy(dst, src, sizeof(dst))
166
167
168 /* Shell quoting
169  *
170  * NOTE: The following is a slightly modified copy of the git project's shell
171  * quoting routines found in the quote.c file.
172  *
173  * Help to copy the thing properly quoted for the shell safety.  any single
174  * quote is replaced with '\'', any exclamation point is replaced with '\!',
175  * and the whole thing is enclosed in a
176  *
177  * E.g.
178  *  original     sq_quote     result
179  *  name     ==> name      ==> 'name'
180  *  a b      ==> a b       ==> 'a b'
181  *  a'b      ==> a'\''b    ==> 'a'\''b'
182  *  a!b      ==> a'\!'b    ==> 'a'\!'b'
183  */
184
185 static size_t
186 sq_quote(char buf[SIZEOF_CMD], size_t bufsize, const char *src)
187 {
188         char c;
189
190 #define BUFPUT(x) do { if (bufsize < SIZEOF_CMD) buf[bufsize++] = (x); } while (0)
191
192         BUFPUT('\'');
193         while ((c = *src++)) {
194                 if (c == '\'' || c == '!') {
195                         BUFPUT('\'');
196                         BUFPUT('\\');
197                         BUFPUT(c);
198                         BUFPUT('\'');
199                 } else {
200                         BUFPUT(c);
201                 }
202         }
203         BUFPUT('\'');
204
205         return bufsize;
206 }
207
208
209 /**
210  * OPTIONS
211  * -------
212  **/
213
214 static const char usage[] =
215 VERSION " (" __DATE__ ")\n"
216 "\n"
217 "Usage: tig [options]\n"
218 "   or: tig [options] [--] [git log options]\n"
219 "   or: tig [options] log  [git log options]\n"
220 "   or: tig [options] diff [git diff options]\n"
221 "   or: tig [options] show [git show options]\n"
222 "   or: tig [options] <    [git command output]\n"
223 "\n"
224 "Options:\n"
225 "  -l                          Start up in log view\n"
226 "  -d                          Start up in diff view\n"
227 "  -n[I], --line-number[=I]    Show line numbers with given interval\n"
228 "  -t[N], --tab-size[=N]       Set number of spaces for tab expansion\n"
229 "  --                          Mark end of tig options\n"
230 "  -v, --version               Show version and exit\n"
231 "  -h, --help                  Show help message and exit\n";
232
233 /* Option and state variables. */
234 static bool opt_line_number     = FALSE;
235 static int opt_num_interval     = NUMBER_INTERVAL;
236 static int opt_tab_size         = TABSIZE;
237 static enum request opt_request = REQ_VIEW_MAIN;
238 static char opt_cmd[SIZEOF_CMD] = "";
239 static char opt_encoding[20]    = "UTF-8";
240 static FILE *opt_pipe           = NULL;
241
242 /* Returns the index of log or diff command or -1 to exit. */
243 static bool
244 parse_options(int argc, char *argv[])
245 {
246         int i;
247
248         for (i = 1; i < argc; i++) {
249                 char *opt = argv[i];
250
251                 /**
252                  * -l::
253                  *      Start up in log view using the internal log command.
254                  **/
255                 if (!strcmp(opt, "-l")) {
256                         opt_request = REQ_VIEW_LOG;
257                         continue;
258                 }
259
260                 /**
261                  * -d::
262                  *      Start up in diff view using the internal diff command.
263                  **/
264                 if (!strcmp(opt, "-d")) {
265                         opt_request = REQ_VIEW_DIFF;
266                         continue;
267                 }
268
269                 /**
270                  * -n[INTERVAL], --line-number[=INTERVAL]::
271                  *      Prefix line numbers in log and diff view.
272                  *      Optionally, with interval different than each line.
273                  **/
274                 if (!strncmp(opt, "-n", 2) ||
275                     !strncmp(opt, "--line-number", 13)) {
276                         char *num = opt;
277
278                         if (opt[1] == 'n') {
279                                 num = opt + 2;
280
281                         } else if (opt[STRING_SIZE("--line-number")] == '=') {
282                                 num = opt + STRING_SIZE("--line-number=");
283                         }
284
285                         if (isdigit(*num))
286                                 opt_num_interval = atoi(num);
287
288                         opt_line_number = TRUE;
289                         continue;
290                 }
291
292                 /**
293                  * -t[NSPACES], --tab-size[=NSPACES]::
294                  *      Set the number of spaces tabs should be expanded to.
295                  **/
296                 if (!strncmp(opt, "-t", 2) ||
297                     !strncmp(opt, "--tab-size", 10)) {
298                         char *num = opt;
299
300                         if (opt[1] == 't') {
301                                 num = opt + 2;
302
303                         } else if (opt[STRING_SIZE("--tab-size")] == '=') {
304                                 num = opt + STRING_SIZE("--tab-size=");
305                         }
306
307                         if (isdigit(*num))
308                                 opt_tab_size = MIN(atoi(num), TABSIZE);
309                         continue;
310                 }
311
312                 /**
313                  * -v, --version::
314                  *      Show version and exit.
315                  **/
316                 if (!strcmp(opt, "-v") ||
317                     !strcmp(opt, "--version")) {
318                         printf("tig version %s\n", VERSION);
319                         return FALSE;
320                 }
321
322                 /**
323                  * -h, --help::
324                  *      Show help message and exit.
325                  **/
326                 if (!strcmp(opt, "-h") ||
327                     !strcmp(opt, "--help")) {
328                         printf(usage);
329                         return FALSE;
330                 }
331
332                 /**
333                  * \--::
334                  *      End of tig(1) options. Useful when specifying command
335                  *      options for the main view. Example:
336                  *
337                  *              $ tig -- --since=1.month
338                  **/
339                 if (!strcmp(opt, "--")) {
340                         i++;
341                         break;
342                 }
343
344                 /**
345                  * log [git log options]::
346                  *      Open log view using the given git log options.
347                  *
348                  * diff [git diff options]::
349                  *      Open diff view using the given git diff options.
350                  *
351                  * show [git show options]::
352                  *      Open diff view using the given git show options.
353                  **/
354                 if (!strcmp(opt, "log") ||
355                     !strcmp(opt, "diff") ||
356                     !strcmp(opt, "show")) {
357                         opt_request = opt[0] == 'l'
358                                     ? REQ_VIEW_LOG : REQ_VIEW_DIFF;
359                         break;
360                 }
361
362                 /**
363                  * [git log options]::
364                  *      tig(1) will stop the option parsing when the first
365                  *      command line parameter not starting with "-" is
366                  *      encountered. All options including this one will be
367                  *      passed to git log when loading the main view.
368                  *      This makes it possible to say:
369                  *
370                  *      $ tig tag-1.0..HEAD
371                  **/
372                 if (opt[0] && opt[0] != '-')
373                         break;
374
375                 die("unknown command '%s'", opt);
376         }
377
378         if (!isatty(STDIN_FILENO)) {
379                 /**
380                  * Pager mode
381                  * ~~~~~~~~~~
382                  * If stdin is a pipe, any log or diff options will be ignored and the
383                  * pager view will be opened loading data from stdin. The pager mode
384                  * can be used for colorizing output from various git commands.
385                  *
386                  * Example on how to colorize the output of git-show(1):
387                  *
388                  *      $ git show | tig
389                  **/
390                 opt_request = REQ_VIEW_PAGER;
391                 opt_pipe = stdin;
392
393         } else if (i < argc) {
394                 size_t buf_size;
395
396                 /**
397                  * Git command options
398                  * ~~~~~~~~~~~~~~~~~~~
399                  * All git command options specified on the command line will
400                  * be passed to the given command and all will be shell quoted
401                  * before they are passed to the shell.
402                  *
403                  * NOTE: If you specify options for the main view, you should
404                  * not use the `--pretty` option as this option will be set
405                  * automatically to the format expected by the main view.
406                  *
407                  * Example on how to open the log view and show both author and
408                  * committer information:
409                  *
410                  *      $ tig log --pretty=fuller
411                  *
412                  * See the <<refspec, "Specifying revisions">> section below
413                  * for an introduction to revision options supported by the git
414                  * commands. For details on specific git command options, refer
415                  * to the man page of the command in question.
416                  **/
417
418                 if (opt_request == REQ_VIEW_MAIN)
419                         /* XXX: This is vulnerable to the user overriding
420                          * options required for the main view parser. */
421                         string_copy(opt_cmd, "git log --stat --pretty=raw");
422                 else
423                         string_copy(opt_cmd, "git");
424                 buf_size = strlen(opt_cmd);
425
426                 while (buf_size < sizeof(opt_cmd) && i < argc) {
427                         opt_cmd[buf_size++] = ' ';
428                         buf_size = sq_quote(opt_cmd, buf_size, argv[i++]);
429                 }
430
431                 if (buf_size >= sizeof(opt_cmd))
432                         die("command too long");
433
434                 opt_cmd[buf_size] = 0;
435
436         }
437
438         return TRUE;
439 }
440
441
442 /**
443  * ENVIRONMENT VARIABLES
444  * ---------------------
445  * Several options related to the interface with git can be configured
446  * via environment options.
447  *
448  * Repository references
449  * ~~~~~~~~~~~~~~~~~~~~~
450  * Commits that are referenced by tags and branch heads will be marked
451  * by the reference name surrounded by '[' and ']':
452  *
453  *      2006-03-26 19:42 Petr Baudis         | [cogito-0.17.1] Cogito 0.17.1
454  *
455  * If you want to filter out certain directories under `.git/refs/`, say
456  * `tmp` you can do it by setting the following variable:
457  *
458  *      $ TIG_LS_REMOTE="git ls-remote . | sed /\/tmp\//d" tig
459  *
460  * Or set the variable permanently in your environment.
461  *
462  * TIG_LS_REMOTE::
463  *      Set command for retrieving all repository references. The command
464  *      should output data in the same format as git-ls-remote(1).
465  **/
466
467 #define TIG_LS_REMOTE \
468         "git ls-remote . 2>/dev/null"
469
470 /**
471  * [[view-commands]]
472  * View commands
473  * ~~~~~~~~~~~~~
474  * It is possible to alter which commands are used for the different views.
475  * If for example you prefer commits in the main view to be sorted by date
476  * and only show 500 commits, use:
477  *
478  *      $ TIG_MAIN_CMD="git log --date-order -n500 --pretty=raw %s" tig
479  *
480  * Or set the variable permanently in your environment.
481  *
482  * Notice, how `%s` is used to specify the commit reference. There can
483  * be a maximum of 5 `%s` ref specifications.
484  *
485  * TIG_DIFF_CMD::
486  *      The command used for the diff view. By default, git show is used
487  *      as a backend.
488  *
489  * TIG_LOG_CMD::
490  *      The command used for the log view. If you prefer to have both
491  *      author and committer shown in the log view be sure to pass
492  *      `--pretty=fuller` to git log.
493  *
494  * TIG_MAIN_CMD::
495  *      The command used for the main view. Note, you must always specify
496  *      the option: `--pretty=raw` since the main view parser expects to
497  *      read that format.
498  **/
499
500 #define TIG_DIFF_CMD \
501         "git show --patch-with-stat --find-copies-harder -B -C %s"
502
503 #define TIG_LOG_CMD     \
504         "git log --cc --stat -n100 %s"
505
506 #define TIG_MAIN_CMD \
507         "git log --topo-order --stat --pretty=raw %s"
508
509 /* ... silently ignore that the following are also exported. */
510
511 #define TIG_HELP_CMD \
512         "man tig 2>/dev/null"
513
514 #define TIG_PAGER_CMD \
515         ""
516
517
518 /*
519  * Line-oriented content detection.
520  */
521
522 #define LINE_INFO \
523 /*   Line type     String to match      Foreground      Background      Attributes
524  *   ---------     ---------------      ----------      ----------      ---------- */ \
525 /* Diff markup */ \
526 LINE(DIFF,         "diff --git ",       COLOR_YELLOW,   COLOR_DEFAULT,  0), \
527 LINE(DIFF_INDEX,   "index ",            COLOR_BLUE,     COLOR_DEFAULT,  0), \
528 LINE(DIFF_CHUNK,   "@@",                COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
529 LINE(DIFF_ADD,     "+",                 COLOR_GREEN,    COLOR_DEFAULT,  0), \
530 LINE(DIFF_DEL,     "-",                 COLOR_RED,      COLOR_DEFAULT,  0), \
531 LINE(DIFF_OLDMODE, "old file mode ",    COLOR_YELLOW,   COLOR_DEFAULT,  0), \
532 LINE(DIFF_NEWMODE, "new file mode ",    COLOR_YELLOW,   COLOR_DEFAULT,  0), \
533 LINE(DIFF_COPY,    "copy ",             COLOR_YELLOW,   COLOR_DEFAULT,  0), \
534 LINE(DIFF_RENAME,  "rename ",           COLOR_YELLOW,   COLOR_DEFAULT,  0), \
535 LINE(DIFF_SIM,     "similarity ",       COLOR_YELLOW,   COLOR_DEFAULT,  0), \
536 LINE(DIFF_DISSIM,  "dissimilarity ",    COLOR_YELLOW,   COLOR_DEFAULT,  0), \
537 /* Pretty print commit header */ \
538 LINE(PP_AUTHOR,    "Author: ",          COLOR_CYAN,     COLOR_DEFAULT,  0), \
539 LINE(PP_COMMIT,    "Commit: ",          COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
540 LINE(PP_MERGE,     "Merge: ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
541 LINE(PP_DATE,      "Date:   ",          COLOR_YELLOW,   COLOR_DEFAULT,  0), \
542 LINE(PP_ADATE,     "AuthorDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
543 LINE(PP_CDATE,     "CommitDate: ",      COLOR_YELLOW,   COLOR_DEFAULT,  0), \
544 /* Raw commit header */ \
545 LINE(COMMIT,       "commit ",           COLOR_GREEN,    COLOR_DEFAULT,  0), \
546 LINE(PARENT,       "parent ",           COLOR_BLUE,     COLOR_DEFAULT,  0), \
547 LINE(TREE,         "tree ",             COLOR_BLUE,     COLOR_DEFAULT,  0), \
548 LINE(AUTHOR,       "author ",           COLOR_CYAN,     COLOR_DEFAULT,  0), \
549 LINE(COMMITTER,    "committer ",        COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
550 /* Misc */ \
551 LINE(DIFF_TREE,    "diff-tree ",        COLOR_BLUE,     COLOR_DEFAULT,  0), \
552 LINE(SIGNOFF,      "    Signed-off-by", COLOR_YELLOW,   COLOR_DEFAULT,  0), \
553 /* UI colors */ \
554 LINE(DEFAULT,      "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  A_NORMAL), \
555 LINE(CURSOR,       "",                  COLOR_WHITE,    COLOR_GREEN,    A_BOLD), \
556 LINE(STATUS,       "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
557 LINE(TITLE_BLUR,   "",                  COLOR_WHITE,    COLOR_BLUE,     0), \
558 LINE(TITLE_FOCUS,  "",                  COLOR_WHITE,    COLOR_BLUE,     A_BOLD), \
559 LINE(MAIN_DATE,    "",                  COLOR_BLUE,     COLOR_DEFAULT,  0), \
560 LINE(MAIN_AUTHOR,  "",                  COLOR_GREEN,    COLOR_DEFAULT,  0), \
561 LINE(MAIN_COMMIT,  "",                  COLOR_DEFAULT,  COLOR_DEFAULT,  0), \
562 LINE(MAIN_DELIM,   "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  0), \
563 LINE(MAIN_TAG,     "",                  COLOR_MAGENTA,  COLOR_DEFAULT,  A_BOLD), \
564 LINE(MAIN_REF,     "",                  COLOR_CYAN,     COLOR_DEFAULT,  A_BOLD),
565
566 enum line_type {
567 #define LINE(type, line, fg, bg, attr) \
568         LINE_##type
569         LINE_INFO
570 #undef  LINE
571 };
572
573 struct line_info {
574         const char *line;       /* The start of line to match. */
575         int linelen;            /* Size of string to match. */
576         int fg, bg, attr;       /* Color and text attributes for the lines. */
577 };
578
579 static struct line_info line_info[] = {
580 #define LINE(type, line, fg, bg, attr) \
581         { (line), STRING_SIZE(line), (fg), (bg), (attr) }
582         LINE_INFO
583 #undef  LINE
584 };
585
586 static enum line_type
587 get_line_type(char *line)
588 {
589         int linelen = strlen(line);
590         enum line_type type;
591
592         for (type = 0; type < ARRAY_SIZE(line_info); type++)
593                 /* Case insensitive search matches Signed-off-by lines better. */
594                 if (linelen >= line_info[type].linelen &&
595                     !strncasecmp(line_info[type].line, line, line_info[type].linelen))
596                         return type;
597
598         return LINE_DEFAULT;
599 }
600
601 static inline int
602 get_line_attr(enum line_type type)
603 {
604         assert(type < ARRAY_SIZE(line_info));
605         return COLOR_PAIR(type) | line_info[type].attr;
606 }
607
608 static void
609 init_colors(void)
610 {
611         int default_bg = COLOR_BLACK;
612         int default_fg = COLOR_WHITE;
613         enum line_type type;
614
615         start_color();
616
617         if (use_default_colors() != ERR) {
618                 default_bg = -1;
619                 default_fg = -1;
620         }
621
622         for (type = 0; type < ARRAY_SIZE(line_info); type++) {
623                 struct line_info *info = &line_info[type];
624                 int bg = info->bg == COLOR_DEFAULT ? default_bg : info->bg;
625                 int fg = info->fg == COLOR_DEFAULT ? default_fg : info->fg;
626
627                 init_pair(type, fg, bg);
628         }
629 }
630
631 struct line {
632         enum line_type type;
633         void *data;             /* User data */
634 };
635
636
637 /**
638  * The viewer
639  * ----------
640  * The display consists of a status window on the last line of the screen and
641  * one or more views. The default is to only show one view at the time but it
642  * is possible to split both the main and log view to also show the commit
643  * diff.
644  *
645  * If you are in the log view and press 'Enter' when the current line is a
646  * commit line, such as:
647  *
648  *      commit 4d55caff4cc89335192f3e566004b4ceef572521
649  *
650  * You will split the view so that the log view is displayed in the top window
651  * and the diff view in the bottom window. You can switch between the two
652  * views by pressing 'Tab'. To maximize the log view again, simply press 'l'.
653  **/
654
655 struct view;
656 struct view_ops;
657
658 /* The display array of active views and the index of the current view. */
659 static struct view *display[2];
660 static unsigned int current_view;
661
662 #define foreach_view(view, i) \
663         for (i = 0; i < ARRAY_SIZE(display) && (view = display[i]); i++)
664
665 #define displayed_views()       (display[1] != NULL ? 2 : 1)
666
667 /**
668  * Current head and commit ID
669  * ~~~~~~~~~~~~~~~~~~~~~~~~~~
670  * The viewer keeps track of both what head and commit ID you are currently
671  * viewing. The commit ID will follow the cursor line and change everytime time
672  * you highlight a different commit. Whenever you reopen the diff view it
673  * will be reloaded, if the commit ID changed.
674  *
675  * The head ID is used when opening the main and log view to indicate from
676  * what revision to show history.
677  **/
678
679 static char ref_commit[SIZEOF_REF]      = "HEAD";
680 static char ref_head[SIZEOF_REF]        = "HEAD";
681
682 struct view {
683         const char *name;       /* View name */
684         const char *cmd_fmt;    /* Default command line format */
685         const char *cmd_env;    /* Command line set via environment */
686         const char *id;         /* Points to either of ref_{head,commit} */
687
688         struct view_ops *ops;   /* View operations */
689
690         char cmd[SIZEOF_CMD];   /* Command buffer */
691         char ref[SIZEOF_REF];   /* Hovered commit reference */
692         char vid[SIZEOF_REF];   /* View ID. Set to id member when updating. */
693
694         int height, width;      /* The width and height of the main window */
695         WINDOW *win;            /* The main window */
696         WINDOW *title;          /* The title window living below the main window */
697
698         /* Navigation */
699         unsigned long offset;   /* Offset of the window top */
700         unsigned long lineno;   /* Current line number */
701
702         /* If non-NULL, points to the view that opened this view. If this view
703          * is closed tig will switch back to the parent view. */
704         struct view *parent;
705
706         /* Buffering */
707         unsigned long lines;    /* Total number of lines */
708         struct line *line;      /* Line index */
709         unsigned int digits;    /* Number of digits in the lines member. */
710
711         /* Loading */
712         FILE *pipe;
713         time_t start_time;
714 };
715
716 struct view_ops {
717         /* What type of content being displayed. Used in the title bar. */
718         const char *type;
719         /* Draw one line; @lineno must be < view->height. */
720         bool (*draw)(struct view *view, struct line *line, unsigned int lineno);
721         /* Read one line; updates view->line. */
722         bool (*read)(struct view *view, struct line *prev, char *data);
723         /* Depending on view, change display based on current line. */
724         bool (*enter)(struct view *view, struct line *line);
725 };
726
727 static struct view_ops pager_ops;
728 static struct view_ops main_ops;
729
730 #define VIEW_STR(name, cmd, env, ref, ops) \
731         { name, cmd, #env, ref, ops }
732
733 #define VIEW_(id, name, ops, ref) \
734         VIEW_STR(name, TIG_##id##_CMD,  TIG_##id##_CMD, ref, ops)
735
736 /**
737  * Views
738  * ~~~~~
739  * tig(1) presents various 'views' of a repository. Each view is based on output
740  * from an external command, most often 'git log', 'git diff', or 'git show'.
741  *
742  * The main view::
743  *      Is the default view, and it shows a one line summary of each commit
744  *      in the chosen list of revisions. The summary includes commit date,
745  *      author, and the first line of the log message. Additionally, any
746  *      repository references, such as tags, will be shown.
747  *
748  * The log view::
749  *      Presents a more rich view of the revision log showing the whole log
750  *      message and the diffstat.
751  *
752  * The diff view::
753  *      Shows either the diff of the current working tree, that is, what
754  *      has changed since the last commit, or the commit diff complete
755  *      with log message, diffstat and diff.
756  *
757  * The pager view::
758  *      Is used for displaying both input from stdin and output from git
759  *      commands entered in the internal prompt.
760  *
761  * The help view::
762  *      Displays the information from the tig(1) man page. For the help view
763  *      to work you need to have the tig(1) man page installed.
764  **/
765
766 static struct view views[] = {
767         VIEW_(MAIN,  "main",  &main_ops,  ref_head),
768         VIEW_(DIFF,  "diff",  &pager_ops, ref_commit),
769         VIEW_(LOG,   "log",   &pager_ops, ref_head),
770         VIEW_(HELP,  "help",  &pager_ops, "static"),
771         VIEW_(PAGER, "pager", &pager_ops, "static"),
772 };
773
774 #define VIEW(req) (&views[(req) - REQ_OFFSET - 1])
775
776
777 static bool
778 draw_view_line(struct view *view, unsigned int lineno)
779 {
780         if (view->offset + lineno >= view->lines)
781                 return FALSE;
782
783         return view->ops->draw(view, &view->line[view->offset + lineno], lineno);
784 }
785
786 static void
787 redraw_view_from(struct view *view, int lineno)
788 {
789         assert(0 <= lineno && lineno < view->height);
790
791         for (; lineno < view->height; lineno++) {
792                 if (!draw_view_line(view, lineno))
793                         break;
794         }
795
796         redrawwin(view->win);
797         wrefresh(view->win);
798 }
799
800 static void
801 redraw_view(struct view *view)
802 {
803         wclear(view->win);
804         redraw_view_from(view, 0);
805 }
806
807
808 /**
809  * Title windows
810  * ~~~~~~~~~~~~~
811  * Each view has a title window which shows the name of the view, current
812  * commit ID if available, and where the view is positioned:
813  *
814  *      [main] c622eefaa485995320bc743431bae0d497b1d875 - commit 1 of 61 (1%)
815  *
816  * By default, the title of the current view is highlighted using bold font.
817  **/
818
819 static void
820 update_view_title(struct view *view)
821 {
822         if (view == display[current_view])
823                 wbkgdset(view->title, get_line_attr(LINE_TITLE_FOCUS));
824         else
825                 wbkgdset(view->title, get_line_attr(LINE_TITLE_BLUR));
826
827         werase(view->title);
828         wmove(view->title, 0, 0);
829
830         if (*view->ref)
831                 wprintw(view->title, "[%s] %s", view->name, view->ref);
832         else
833                 wprintw(view->title, "[%s]", view->name);
834
835         if (view->lines || view->pipe) {
836                 unsigned int lines = view->lines
837                                    ? (view->lineno + 1) * 100 / view->lines
838                                    : 0;
839
840                 wprintw(view->title, " - %s %d of %d (%d%%)",
841                         view->ops->type,
842                         view->lineno + 1,
843                         view->lines,
844                         lines);
845         }
846
847         if (view->pipe) {
848                 time_t secs = time(NULL) - view->start_time;
849
850                 /* Three git seconds are a long time ... */
851                 if (secs > 2)
852                         wprintw(view->title, " %lds", secs);
853         }
854
855
856         wrefresh(view->title);
857 }
858
859 static void
860 resize_display(void)
861 {
862         int offset, i;
863         struct view *base = display[0];
864         struct view *view = display[1] ? display[1] : display[0];
865
866         /* Setup window dimensions */
867
868         getmaxyx(stdscr, base->height, base->width);
869
870         /* Make room for the status window. */
871         base->height -= 1;
872
873         if (view != base) {
874                 /* Horizontal split. */
875                 view->width   = base->width;
876                 view->height  = SCALE_SPLIT_VIEW(base->height);
877                 base->height -= view->height;
878
879                 /* Make room for the title bar. */
880                 view->height -= 1;
881         }
882
883         /* Make room for the title bar. */
884         base->height -= 1;
885
886         offset = 0;
887
888         foreach_view (view, i) {
889                 if (!view->win) {
890                         view->win = newwin(view->height, 0, offset, 0);
891                         if (!view->win)
892                                 die("Failed to create %s view", view->name);
893
894                         scrollok(view->win, TRUE);
895
896                         view->title = newwin(1, 0, offset + view->height, 0);
897                         if (!view->title)
898                                 die("Failed to create title window");
899
900                 } else {
901                         wresize(view->win, view->height, view->width);
902                         mvwin(view->win,   offset, 0);
903                         mvwin(view->title, offset + view->height, 0);
904                         wrefresh(view->win);
905                 }
906
907                 offset += view->height + 1;
908         }
909 }
910
911 static void
912 redraw_display(void)
913 {
914         struct view *view;
915         int i;
916
917         foreach_view (view, i) {
918                 redraw_view(view);
919                 update_view_title(view);
920         }
921 }
922
923
924 /*
925  * Navigation
926  */
927
928 /* Scrolling backend */
929 static void
930 do_scroll_view(struct view *view, int lines, bool redraw)
931 {
932         /* The rendering expects the new offset. */
933         view->offset += lines;
934
935         assert(0 <= view->offset && view->offset < view->lines);
936         assert(lines);
937
938         /* Redraw the whole screen if scrolling is pointless. */
939         if (view->height < ABS(lines)) {
940                 redraw_view(view);
941
942         } else {
943                 int line = lines > 0 ? view->height - lines : 0;
944                 int end = line + ABS(lines);
945
946                 wscrl(view->win, lines);
947
948                 for (; line < end; line++) {
949                         if (!draw_view_line(view, line))
950                                 break;
951                 }
952         }
953
954         /* Move current line into the view. */
955         if (view->lineno < view->offset) {
956                 view->lineno = view->offset;
957                 draw_view_line(view, 0);
958
959         } else if (view->lineno >= view->offset + view->height) {
960                 if (view->lineno == view->offset + view->height) {
961                         /* Clear the hidden line so it doesn't show if the view
962                          * is scrolled up. */
963                         wmove(view->win, view->height, 0);
964                         wclrtoeol(view->win);
965                 }
966                 view->lineno = view->offset + view->height - 1;
967                 draw_view_line(view, view->lineno - view->offset);
968         }
969
970         assert(view->offset <= view->lineno && view->lineno < view->lines);
971
972         if (!redraw)
973                 return;
974
975         redrawwin(view->win);
976         wrefresh(view->win);
977         report("");
978 }
979
980 /* Scroll frontend */
981 static void
982 scroll_view(struct view *view, enum request request)
983 {
984         int lines = 1;
985
986         switch (request) {
987         case REQ_SCROLL_PAGE_DOWN:
988                 lines = view->height;
989         case REQ_SCROLL_LINE_DOWN:
990                 if (view->offset + lines > view->lines)
991                         lines = view->lines - view->offset;
992
993                 if (lines == 0 || view->offset + view->height >= view->lines) {
994                         report("Cannot scroll beyond the last line");
995                         return;
996                 }
997                 break;
998
999         case REQ_SCROLL_PAGE_UP:
1000                 lines = view->height;
1001         case REQ_SCROLL_LINE_UP:
1002                 if (lines > view->offset)
1003                         lines = view->offset;
1004
1005                 if (lines == 0) {
1006                         report("Cannot scroll beyond the first line");
1007                         return;
1008                 }
1009
1010                 lines = -lines;
1011                 break;
1012
1013         default:
1014                 die("request %d not handled in switch", request);
1015         }
1016
1017         do_scroll_view(view, lines, TRUE);
1018 }
1019
1020 /* Cursor moving */
1021 static void
1022 move_view(struct view *view, enum request request, bool redraw)
1023 {
1024         int steps;
1025
1026         switch (request) {
1027         case REQ_MOVE_FIRST_LINE:
1028                 steps = -view->lineno;
1029                 break;
1030
1031         case REQ_MOVE_LAST_LINE:
1032                 steps = view->lines - view->lineno - 1;
1033                 break;
1034
1035         case REQ_MOVE_PAGE_UP:
1036                 steps = view->height > view->lineno
1037                       ? -view->lineno : -view->height;
1038                 break;
1039
1040         case REQ_MOVE_PAGE_DOWN:
1041                 steps = view->lineno + view->height >= view->lines
1042                       ? view->lines - view->lineno - 1 : view->height;
1043                 break;
1044
1045         case REQ_MOVE_UP:
1046                 steps = -1;
1047                 break;
1048
1049         case REQ_MOVE_DOWN:
1050                 steps = 1;
1051                 break;
1052
1053         default:
1054                 die("request %d not handled in switch", request);
1055         }
1056
1057         if (steps <= 0 && view->lineno == 0) {
1058                 report("Cannot move beyond the first line");
1059                 return;
1060
1061         } else if (steps >= 0 && view->lineno + 1 >= view->lines) {
1062                 report("Cannot move beyond the last line");
1063                 return;
1064         }
1065
1066         /* Move the current line */
1067         view->lineno += steps;
1068         assert(0 <= view->lineno && view->lineno < view->lines);
1069
1070         /* Repaint the old "current" line if we be scrolling */
1071         if (ABS(steps) < view->height) {
1072                 int prev_lineno = view->lineno - steps - view->offset;
1073
1074                 wmove(view->win, prev_lineno, 0);
1075                 wclrtoeol(view->win);
1076                 draw_view_line(view,  prev_lineno);
1077         }
1078
1079         /* Check whether the view needs to be scrolled */
1080         if (view->lineno < view->offset ||
1081             view->lineno >= view->offset + view->height) {
1082                 if (steps < 0 && -steps > view->offset) {
1083                         steps = -view->offset;
1084
1085                 } else if (steps > 0) {
1086                         if (view->lineno == view->lines - 1 &&
1087                             view->lines > view->height) {
1088                                 steps = view->lines - view->offset - 1;
1089                                 if (steps >= view->height)
1090                                         steps -= view->height - 1;
1091                         }
1092                 }
1093
1094                 do_scroll_view(view, steps, redraw);
1095                 return;
1096         }
1097
1098         /* Draw the current line */
1099         draw_view_line(view, view->lineno - view->offset);
1100
1101         if (!redraw)
1102                 return;
1103
1104         redrawwin(view->win);
1105         wrefresh(view->win);
1106         report("");
1107 }
1108
1109
1110 /*
1111  * Incremental updating
1112  */
1113
1114 static void
1115 end_update(struct view *view)
1116 {
1117         if (!view->pipe)
1118                 return;
1119         set_nonblocking_input(FALSE);
1120         if (view->pipe == stdin)
1121                 fclose(view->pipe);
1122         else
1123                 pclose(view->pipe);
1124         view->pipe = NULL;
1125 }
1126
1127 static bool
1128 begin_update(struct view *view)
1129 {
1130         const char *id = view->id;
1131
1132         if (view->pipe)
1133                 end_update(view);
1134
1135         if (opt_cmd[0]) {
1136                 string_copy(view->cmd, opt_cmd);
1137                 opt_cmd[0] = 0;
1138                 /* When running random commands, the view ref could have become
1139                  * invalid so clear it. */
1140                 view->ref[0] = 0;
1141         } else {
1142                 const char *format = view->cmd_env ? view->cmd_env : view->cmd_fmt;
1143
1144                 if (snprintf(view->cmd, sizeof(view->cmd), format,
1145                              id, id, id, id, id) >= sizeof(view->cmd))
1146                         return FALSE;
1147         }
1148
1149         /* Special case for the pager view. */
1150         if (opt_pipe) {
1151                 view->pipe = opt_pipe;
1152                 opt_pipe = NULL;
1153         } else {
1154                 view->pipe = popen(view->cmd, "r");
1155         }
1156
1157         if (!view->pipe)
1158                 return FALSE;
1159
1160         set_nonblocking_input(TRUE);
1161
1162         view->offset = 0;
1163         view->lines  = 0;
1164         view->lineno = 0;
1165         string_copy(view->vid, id);
1166
1167         if (view->line) {
1168                 int i;
1169
1170                 for (i = 0; i < view->lines; i++)
1171                         if (view->line[i].data)
1172                                 free(view->line[i].data);
1173
1174                 free(view->line);
1175                 view->line = NULL;
1176         }
1177
1178         view->start_time = time(NULL);
1179
1180         return TRUE;
1181 }
1182
1183 static bool
1184 update_view(struct view *view)
1185 {
1186         char buffer[BUFSIZ];
1187         char *line;
1188         struct line *tmp;
1189         /* The number of lines to read. If too low it will cause too much
1190          * redrawing (and possible flickering), if too high responsiveness
1191          * will suffer. */
1192         unsigned long lines = view->height;
1193         int redraw_from = -1;
1194
1195         if (!view->pipe)
1196                 return TRUE;
1197
1198         /* Only redraw if lines are visible. */
1199         if (view->offset + view->height >= view->lines)
1200                 redraw_from = view->lines - view->offset;
1201
1202         tmp = realloc(view->line, sizeof(*view->line) * (view->lines + lines));
1203         if (!tmp)
1204                 goto alloc_error;
1205
1206         view->line = tmp;
1207
1208         while ((line = fgets(buffer, sizeof(buffer), view->pipe))) {
1209                 int linelen = strlen(line);
1210
1211                 struct line *prev = view->lines
1212                                   ? &view->line[view->lines - 1]
1213                                   : NULL;
1214
1215                 if (linelen)
1216                         line[linelen - 1] = 0;
1217
1218                 if (!view->ops->read(view, prev, line))
1219                         goto alloc_error;
1220
1221                 if (lines-- == 1)
1222                         break;
1223         }
1224
1225         {
1226                 int digits;
1227
1228                 lines = view->lines;
1229                 for (digits = 0; lines; digits++)
1230                         lines /= 10;
1231
1232                 /* Keep the displayed view in sync with line number scaling. */
1233                 if (digits != view->digits) {
1234                         view->digits = digits;
1235                         redraw_from = 0;
1236                 }
1237         }
1238
1239         if (redraw_from >= 0) {
1240                 /* If this is an incremental update, redraw the previous line
1241                  * since for commits some members could have changed when
1242                  * loading the main view. */
1243                 if (redraw_from > 0)
1244                         redraw_from--;
1245
1246                 /* Incrementally draw avoids flickering. */
1247                 redraw_view_from(view, redraw_from);
1248         }
1249
1250         /* Update the title _after_ the redraw so that if the redraw picks up a
1251          * commit reference in view->ref it'll be available here. */
1252         update_view_title(view);
1253
1254         if (ferror(view->pipe)) {
1255                 report("Failed to read: %s", strerror(errno));
1256                 goto end;
1257
1258         } else if (feof(view->pipe)) {
1259                 if (view == VIEW(REQ_VIEW_HELP)) {
1260                         const char *msg = TIG_HELP;
1261
1262                         if (view->lines == 0) {
1263                                 /* Slightly ugly, but abusing view->ref keeps
1264                                  * the error message. */
1265                                 string_copy(view->ref, "No help available");
1266                                 msg = "The tig(1) manpage is not installed";
1267                         }
1268
1269                         report("%s", msg);
1270                         goto end;
1271                 }
1272
1273                 report("");
1274                 goto end;
1275         }
1276
1277         return TRUE;
1278
1279 alloc_error:
1280         report("Allocation failure");
1281
1282 end:
1283         end_update(view);
1284         return FALSE;
1285 }
1286
1287 enum open_flags {
1288         OPEN_DEFAULT = 0,       /* Use default view switching. */
1289         OPEN_SPLIT = 1,         /* Split current view. */
1290         OPEN_BACKGROUNDED = 2,  /* Backgrounded. */
1291         OPEN_RELOAD = 4,        /* Reload view even if it is the current. */
1292 };
1293
1294 static void
1295 open_view(struct view *prev, enum request request, enum open_flags flags)
1296 {
1297         bool backgrounded = !!(flags & OPEN_BACKGROUNDED);
1298         bool split = !!(flags & OPEN_SPLIT);
1299         bool reload = !!(flags & OPEN_RELOAD);
1300         struct view *view = VIEW(request);
1301         int nviews = displayed_views();
1302         struct view *base_view = display[0];
1303
1304         if (view == prev && nviews == 1 && !reload) {
1305                 report("Already in %s view", view->name);
1306                 return;
1307         }
1308
1309         if ((reload || strcmp(view->vid, view->id)) &&
1310             !begin_update(view)) {
1311                 report("Failed to load %s view", view->name);
1312                 return;
1313         }
1314
1315         if (split) {
1316                 display[current_view + 1] = view;
1317                 if (!backgrounded)
1318                         current_view++;
1319         } else {
1320                 /* Maximize the current view. */
1321                 memset(display, 0, sizeof(display));
1322                 current_view = 0;
1323                 display[current_view] = view;
1324         }
1325
1326         /* Resize the view when switching between split- and full-screen,
1327          * or when switching between two different full-screen views. */
1328         if (nviews != displayed_views() ||
1329             (nviews == 1 && base_view != display[0]))
1330                 resize_display();
1331
1332         if (split && prev->lineno - prev->offset >= prev->height) {
1333                 /* Take the title line into account. */
1334                 int lines = prev->lineno - prev->offset - prev->height + 1;
1335
1336                 /* Scroll the view that was split if the current line is
1337                  * outside the new limited view. */
1338                 do_scroll_view(prev, lines, TRUE);
1339         }
1340
1341         if (prev && view != prev) {
1342                 /* Continue loading split views in the background. */
1343                 if (!split)
1344                         end_update(prev);
1345                 else if (!backgrounded)
1346                         /* "Blur" the previous view. */
1347                         update_view_title(prev);
1348
1349                 view->parent = prev;
1350         }
1351
1352         if (view->pipe) {
1353                 /* Clear the old view and let the incremental updating refill
1354                  * the screen. */
1355                 wclear(view->win);
1356                 report("");
1357         } else {
1358                 redraw_view(view);
1359                 if (view == VIEW(REQ_VIEW_HELP))
1360                         report("%s", TIG_HELP);
1361                 else
1362                         report("");
1363         }
1364
1365         /* If the view is backgrounded the above calls to report()
1366          * won't redraw the view title. */
1367         if (backgrounded)
1368                 update_view_title(view);
1369 }
1370
1371
1372 /*
1373  * User request switch noodle
1374  */
1375
1376 static int
1377 view_driver(struct view *view, enum request request)
1378 {
1379         int i;
1380
1381         switch (request) {
1382         case REQ_MOVE_UP:
1383         case REQ_MOVE_DOWN:
1384         case REQ_MOVE_PAGE_UP:
1385         case REQ_MOVE_PAGE_DOWN:
1386         case REQ_MOVE_FIRST_LINE:
1387         case REQ_MOVE_LAST_LINE:
1388                 move_view(view, request, TRUE);
1389                 break;
1390
1391         case REQ_SCROLL_LINE_DOWN:
1392         case REQ_SCROLL_LINE_UP:
1393         case REQ_SCROLL_PAGE_DOWN:
1394         case REQ_SCROLL_PAGE_UP:
1395                 scroll_view(view, request);
1396                 break;
1397
1398         case REQ_VIEW_MAIN:
1399         case REQ_VIEW_DIFF:
1400         case REQ_VIEW_LOG:
1401         case REQ_VIEW_HELP:
1402         case REQ_VIEW_PAGER:
1403                 open_view(view, request, OPEN_DEFAULT);
1404                 break;
1405
1406         case REQ_NEXT:
1407         case REQ_PREVIOUS:
1408                 request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP;
1409
1410                 if (view == VIEW(REQ_VIEW_DIFF) &&
1411                     view->parent == VIEW(REQ_VIEW_MAIN)) {
1412                         bool redraw = display[1] == view;
1413
1414                         view = view->parent;
1415                         move_view(view, request, redraw);
1416                         if (redraw)
1417                                 update_view_title(view);
1418                 } else {
1419                         move_view(view, request, TRUE);
1420                         break;
1421                 }
1422                 /* Fall-through */
1423
1424         case REQ_ENTER:
1425                 if (!view->lines) {
1426                         report("Nothing to enter");
1427                         break;
1428                 }
1429                 return view->ops->enter(view, &view->line[view->lineno]);
1430
1431         case REQ_VIEW_NEXT:
1432         {
1433                 int nviews = displayed_views();
1434                 int next_view = (current_view + 1) % nviews;
1435
1436                 if (next_view == current_view) {
1437                         report("Only one view is displayed");
1438                         break;
1439                 }
1440
1441                 current_view = next_view;
1442                 /* Blur out the title of the previous view. */
1443                 update_view_title(view);
1444                 report("");
1445                 break;
1446         }
1447         case REQ_TOGGLE_LINE_NUMBERS:
1448                 opt_line_number = !opt_line_number;
1449                 redraw_display();
1450                 break;
1451
1452         case REQ_PROMPT:
1453                 /* Always reload^Wrerun commands from the prompt. */
1454                 open_view(view, opt_request, OPEN_RELOAD);
1455                 break;
1456
1457         case REQ_STOP_LOADING:
1458                 foreach_view (view, i) {
1459                         if (view->pipe)
1460                                 report("Stopped loaded the %s view", view->name),
1461                         end_update(view);
1462                 }
1463                 break;
1464
1465         case REQ_SHOW_VERSION:
1466                 report("%s (built %s)", VERSION, __DATE__);
1467                 return TRUE;
1468
1469         case REQ_SCREEN_RESIZE:
1470                 resize_display();
1471                 /* Fall-through */
1472         case REQ_SCREEN_REDRAW:
1473                 redraw_display();
1474                 break;
1475
1476         case REQ_SCREEN_UPDATE:
1477                 doupdate();
1478                 return TRUE;
1479
1480         case REQ_VIEW_CLOSE:
1481                 if (view->parent) {
1482                         memset(display, 0, sizeof(display));
1483                         current_view = 0;
1484                         display[current_view] = view->parent;
1485                         view->parent = NULL;
1486                         resize_display();
1487                         redraw_display();
1488                         break;
1489                 }
1490                 /* Fall-through */
1491         case REQ_QUIT:
1492                 return FALSE;
1493
1494         default:
1495                 /* An unknown key will show most commonly used commands. */
1496                 report("Unknown key, press 'h' for help");
1497                 return TRUE;
1498         }
1499
1500         return TRUE;
1501 }
1502
1503
1504 /*
1505  * Pager backend
1506  */
1507
1508 static bool
1509 pager_draw(struct view *view, struct line *line, unsigned int lineno)
1510 {
1511         char *text = line->data;
1512         enum line_type type = line->type;
1513         int textlen = strlen(text);
1514         int attr;
1515
1516         wmove(view->win, lineno, 0);
1517
1518         if (view->offset + lineno == view->lineno) {
1519                 if (type == LINE_COMMIT) {
1520                         string_copy(view->ref, text + 7);
1521                         string_copy(ref_commit, view->ref);
1522                 }
1523
1524                 type = LINE_CURSOR;
1525                 wchgat(view->win, -1, 0, type, NULL);
1526         }
1527
1528         attr = get_line_attr(type);
1529         wattrset(view->win, attr);
1530
1531         if (opt_line_number || opt_tab_size < TABSIZE) {
1532                 static char spaces[] = "                    ";
1533                 int col_offset = 0, col = 0;
1534
1535                 if (opt_line_number) {
1536                         unsigned long real_lineno = view->offset + lineno + 1;
1537
1538                         if (real_lineno == 1 ||
1539                             (real_lineno % opt_num_interval) == 0) {
1540                                 wprintw(view->win, "%.*d", view->digits, real_lineno);
1541
1542                         } else {
1543                                 waddnstr(view->win, spaces,
1544                                          MIN(view->digits, STRING_SIZE(spaces)));
1545                         }
1546                         waddstr(view->win, ": ");
1547                         col_offset = view->digits + 2;
1548                 }
1549
1550                 while (text && col_offset + col < view->width) {
1551                         int cols_max = view->width - col_offset - col;
1552                         char *pos = text;
1553                         int cols;
1554
1555                         if (*text == '\t') {
1556                                 text++;
1557                                 assert(sizeof(spaces) > TABSIZE);
1558                                 pos = spaces;
1559                                 cols = opt_tab_size - (col % opt_tab_size);
1560
1561                         } else {
1562                                 text = strchr(text, '\t');
1563                                 cols = line ? text - pos : strlen(pos);
1564                         }
1565
1566                         waddnstr(view->win, pos, MIN(cols, cols_max));
1567                         col += cols;
1568                 }
1569
1570         } else {
1571                 int col = 0, pos = 0;
1572
1573                 for (; pos < textlen && col < view->width; pos++, col++)
1574                         if (text[pos] == '\t')
1575                                 col += TABSIZE - (col % TABSIZE) - 1;
1576
1577                 waddnstr(view->win, text, pos);
1578         }
1579
1580         return TRUE;
1581 }
1582
1583 static bool
1584 pager_read(struct view *view, struct line *prev, char *line)
1585 {
1586         /* Compress empty lines in the help view. */
1587         if (view == VIEW(REQ_VIEW_HELP) &&
1588             !*line && prev && !*((char *) prev->data))
1589                 return TRUE;
1590
1591         view->line[view->lines].data = strdup(line);
1592         if (!view->line[view->lines].data)
1593                 return FALSE;
1594
1595         view->line[view->lines].type = get_line_type(line);
1596
1597         view->lines++;
1598         return TRUE;
1599 }
1600
1601 static bool
1602 pager_enter(struct view *view, struct line *line)
1603 {
1604         int split = 0;
1605
1606         if (line->type == LINE_COMMIT &&
1607            (view == VIEW(REQ_VIEW_LOG) ||
1608             view == VIEW(REQ_VIEW_PAGER))) {
1609                 open_view(view, REQ_VIEW_DIFF, OPEN_SPLIT);
1610                 split = 1;
1611         }
1612
1613         /* Always scroll the view even if it was split. That way
1614          * you can use Enter to scroll through the log view and
1615          * split open each commit diff. */
1616         scroll_view(view, REQ_SCROLL_LINE_DOWN);
1617
1618         /* FIXME: A minor workaround. Scrolling the view will call report("")
1619          * but if we are scolling a non-current view this won't properly update
1620          * the view title. */
1621         if (split)
1622                 update_view_title(view);
1623
1624         return TRUE;
1625 }
1626
1627 static struct view_ops pager_ops = {
1628         "line",
1629         pager_draw,
1630         pager_read,
1631         pager_enter,
1632 };
1633
1634
1635 /*
1636  * Main view backend
1637  */
1638
1639 struct commit {
1640         char id[41];            /* SHA1 ID. */
1641         char title[75];         /* The first line of the commit message. */
1642         char author[75];        /* The author of the commit. */
1643         struct tm time;         /* Date from the author ident. */
1644         struct ref **refs;      /* Repository references; tags & branch heads. */
1645 };
1646
1647 static bool
1648 main_draw(struct view *view, struct line *line, unsigned int lineno)
1649 {
1650         char buf[DATE_COLS + 1];
1651         struct commit *commit = line->data;
1652         enum line_type type;
1653         int col = 0;
1654         size_t timelen;
1655         size_t authorlen;
1656         int trimmed;
1657
1658         if (!*commit->author)
1659                 return FALSE;
1660
1661         wmove(view->win, lineno, col);
1662
1663         if (view->offset + lineno == view->lineno) {
1664                 string_copy(view->ref, commit->id);
1665                 string_copy(ref_commit, view->ref);
1666                 type = LINE_CURSOR;
1667                 wattrset(view->win, get_line_attr(type));
1668                 wchgat(view->win, -1, 0, type, NULL);
1669
1670         } else {
1671                 type = LINE_MAIN_COMMIT;
1672                 wattrset(view->win, get_line_attr(LINE_MAIN_DATE));
1673         }
1674
1675         timelen = strftime(buf, sizeof(buf), DATE_FORMAT, &commit->time);
1676         waddnstr(view->win, buf, timelen);
1677         waddstr(view->win, " ");
1678
1679         col += DATE_COLS;
1680         wmove(view->win, lineno, col);
1681         if (type != LINE_CURSOR)
1682                 wattrset(view->win, get_line_attr(LINE_MAIN_AUTHOR));
1683
1684         /* FIXME: Make this optional, and add i18n.commitEncoding support. */
1685         authorlen = utf8_length(commit->author, AUTHOR_COLS - 2, &col, &trimmed);
1686
1687         if (trimmed) {
1688                 waddnstr(view->win, commit->author, authorlen);
1689                 if (type != LINE_CURSOR)
1690                         wattrset(view->win, get_line_attr(LINE_MAIN_DELIM));
1691                 waddch(view->win, '~');
1692         } else {
1693                 waddstr(view->win, commit->author);
1694         }
1695
1696         col += AUTHOR_COLS;
1697         if (type != LINE_CURSOR)
1698                 wattrset(view->win, A_NORMAL);
1699
1700         mvwaddch(view->win, lineno, col, ACS_LTEE);
1701         wmove(view->win, lineno, col + 2);
1702         col += 2;
1703
1704         if (commit->refs) {
1705                 size_t i = 0;
1706
1707                 do {
1708                         if (type == LINE_CURSOR)
1709                                 ;
1710                         else if (commit->refs[i]->tag)
1711                                 wattrset(view->win, get_line_attr(LINE_MAIN_TAG));
1712                         else
1713                                 wattrset(view->win, get_line_attr(LINE_MAIN_REF));
1714                         waddstr(view->win, "[");
1715                         waddstr(view->win, commit->refs[i]->name);
1716                         waddstr(view->win, "]");
1717                         if (type != LINE_CURSOR)
1718                                 wattrset(view->win, A_NORMAL);
1719                         waddstr(view->win, " ");
1720                         col += strlen(commit->refs[i]->name) + STRING_SIZE("[] ");
1721                 } while (commit->refs[i++]->next);
1722         }
1723
1724         if (type != LINE_CURSOR)
1725                 wattrset(view->win, get_line_attr(type));
1726
1727         {
1728                 int titlelen = strlen(commit->title);
1729
1730                 if (col + titlelen > view->width)
1731                         titlelen = view->width - col;
1732
1733                 waddnstr(view->win, commit->title, titlelen);
1734         }
1735
1736         return TRUE;
1737 }
1738
1739 /* Reads git log --pretty=raw output and parses it into the commit struct. */
1740 static bool
1741 main_read(struct view *view, struct line *prev, char *line)
1742 {
1743         enum line_type type = get_line_type(line);
1744         struct commit *commit;
1745
1746         switch (type) {
1747         case LINE_COMMIT:
1748                 commit = calloc(1, sizeof(struct commit));
1749                 if (!commit)
1750                         return FALSE;
1751
1752                 line += STRING_SIZE("commit ");
1753
1754                 view->line[view->lines++].data = commit;
1755                 string_copy(commit->id, line);
1756                 commit->refs = get_refs(commit->id);
1757                 break;
1758
1759         case LINE_AUTHOR:
1760         {
1761                 char *ident = line + STRING_SIZE("author ");
1762                 char *end = strchr(ident, '<');
1763
1764                 if (!prev)
1765                         break;
1766
1767                 commit = prev->data;
1768
1769                 if (end) {
1770                         for (; end > ident && isspace(end[-1]); end--) ;
1771                         *end = 0;
1772                 }
1773
1774                 string_copy(commit->author, ident);
1775
1776                 /* Parse epoch and timezone */
1777                 if (end) {
1778                         char *secs = strchr(end + 1, '>');
1779                         char *zone;
1780                         time_t time;
1781
1782                         if (!secs || secs[1] != ' ')
1783                                 break;
1784
1785                         secs += 2;
1786                         time = (time_t) atol(secs);
1787                         zone = strchr(secs, ' ');
1788                         if (zone && strlen(zone) == STRING_SIZE(" +0700")) {
1789                                 long tz;
1790
1791                                 zone++;
1792                                 tz  = ('0' - zone[1]) * 60 * 60 * 10;
1793                                 tz += ('0' - zone[2]) * 60 * 60;
1794                                 tz += ('0' - zone[3]) * 60;
1795                                 tz += ('0' - zone[4]) * 60;
1796
1797                                 if (zone[0] == '-')
1798                                         tz = -tz;
1799
1800                                 time -= tz;
1801                         }
1802                         gmtime_r(&time, &commit->time);
1803                 }
1804                 break;
1805         }
1806         default:
1807                 if (!prev)
1808                         break;
1809
1810                 commit = prev->data;
1811
1812                 /* Fill in the commit title if it has not already been set. */
1813                 if (commit->title[0])
1814                         break;
1815
1816                 /* Require titles to start with a non-space character at the
1817                  * offset used by git log. */
1818                 /* FIXME: More gracefull handling of titles; append "..." to
1819                  * shortened titles, etc. */
1820                 if (strncmp(line, "    ", 4) ||
1821                     isspace(line[4]))
1822                         break;
1823
1824                 string_copy(commit->title, line + 4);
1825         }
1826
1827         return TRUE;
1828 }
1829
1830 static bool
1831 main_enter(struct view *view, struct line *line)
1832 {
1833         enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT;
1834
1835         open_view(view, REQ_VIEW_DIFF, flags);
1836         return TRUE;
1837 }
1838
1839 static struct view_ops main_ops = {
1840         "commit",
1841         main_draw,
1842         main_read,
1843         main_enter,
1844 };
1845
1846
1847 /**
1848  * KEYS
1849  * ----
1850  * Below the default key bindings are shown.
1851  **/
1852
1853 struct keymap {
1854         int alias;
1855         int request;
1856 };
1857
1858 static struct keymap keymap[] = {
1859         /**
1860          * View switching
1861          * ~~~~~~~~~~~~~~
1862          * m::
1863          *      Switch to main view.
1864          * d::
1865          *      Switch to diff view.
1866          * l::
1867          *      Switch to log view.
1868          * p::
1869          *      Switch to pager view.
1870          * h::
1871          *      Show man page.
1872          **/
1873         { 'm',          REQ_VIEW_MAIN },
1874         { 'd',          REQ_VIEW_DIFF },
1875         { 'l',          REQ_VIEW_LOG },
1876         { 'p',          REQ_VIEW_PAGER },
1877         { 'h',          REQ_VIEW_HELP },
1878
1879         /**
1880          * View manipulation
1881          * ~~~~~~~~~~~~~~~~~
1882          * q::
1883          *      Close view, if multiple views are open it will jump back to the
1884          *      previous view in the view stack. If it is the last open view it
1885          *      will quit. Use 'Q' to quit all views at once.
1886          * Enter::
1887          *      This key is "context sensitive" depending on what view you are
1888          *      currently in. When in log view on a commit line or in the main
1889          *      view, split the view and show the commit diff. In the diff view
1890          *      pressing Enter will simply scroll the view one line down.
1891          * Tab::
1892          *      Switch to next view.
1893          * Up::
1894          *      This key is "context sensitive" and will move the cursor one
1895          *      line up. However, uf you opened a diff view from the main view
1896          *      (split- or full-screen) it will change the cursor to point to
1897          *      the previous commit in the main view and update the diff view
1898          *      to display it.
1899          * Down::
1900          *      Similar to 'Up' but will move down.
1901          **/
1902         { 'q',          REQ_VIEW_CLOSE },
1903         { KEY_TAB,      REQ_VIEW_NEXT },
1904         { KEY_RETURN,   REQ_ENTER },
1905         { KEY_UP,       REQ_PREVIOUS },
1906         { KEY_DOWN,     REQ_NEXT },
1907
1908         /**
1909          * Cursor navigation
1910          * ~~~~~~~~~~~~~~~~~
1911          * j::
1912          *      Move cursor one line up.
1913          * k::
1914          *      Move cursor one line down.
1915          * PgUp::
1916          * b::
1917          * -::
1918          *      Move cursor one page up.
1919          * PgDown::
1920          * Space::
1921          *      Move cursor one page down.
1922          * Home::
1923          *      Jump to first line.
1924          * End::
1925          *      Jump to last line.
1926          **/
1927         { 'k',          REQ_MOVE_UP },
1928         { 'j',          REQ_MOVE_DOWN },
1929         { KEY_HOME,     REQ_MOVE_FIRST_LINE },
1930         { KEY_END,      REQ_MOVE_LAST_LINE },
1931         { KEY_NPAGE,    REQ_MOVE_PAGE_DOWN },
1932         { ' ',          REQ_MOVE_PAGE_DOWN },
1933         { KEY_PPAGE,    REQ_MOVE_PAGE_UP },
1934         { 'b',          REQ_MOVE_PAGE_UP },
1935         { '-',          REQ_MOVE_PAGE_UP },
1936
1937         /**
1938          * Scrolling
1939          * ~~~~~~~~~
1940          * Insert::
1941          *      Scroll view one line up.
1942          * Delete::
1943          *      Scroll view one line down.
1944          * w::
1945          *      Scroll view one page up.
1946          * s::
1947          *      Scroll view one page down.
1948          **/
1949         { KEY_IC,       REQ_SCROLL_LINE_UP },
1950         { KEY_DC,       REQ_SCROLL_LINE_DOWN },
1951         { 'w',          REQ_SCROLL_PAGE_UP },
1952         { 's',          REQ_SCROLL_PAGE_DOWN },
1953
1954         /**
1955          * Misc
1956          * ~~~~
1957          * Q::
1958          *      Quit.
1959          * r::
1960          *      Redraw screen.
1961          * z::
1962          *      Stop all background loading. This can be useful if you use
1963          *      tig(1) in a repository with a long history without limiting
1964          *      the revision log.
1965          * v::
1966          *      Show version.
1967          * n::
1968          *      Toggle line numbers on/off.
1969          * ':'::
1970          *      Open prompt. This allows you to specify what git command
1971          *      to run. Example:
1972          *
1973          *      :log -p
1974          **/
1975         { 'Q',          REQ_QUIT },
1976         { 'z',          REQ_STOP_LOADING },
1977         { 'v',          REQ_SHOW_VERSION },
1978         { 'r',          REQ_SCREEN_REDRAW },
1979         { 'n',          REQ_TOGGLE_LINE_NUMBERS },
1980         { ':',          REQ_PROMPT },
1981
1982         /* wgetch() with nodelay() enabled returns ERR when there's no input. */
1983         { ERR,          REQ_SCREEN_UPDATE },
1984
1985         /* Use the ncurses SIGWINCH handler. */
1986         { KEY_RESIZE,   REQ_SCREEN_RESIZE },
1987 };
1988
1989 static enum request
1990 get_request(int key)
1991 {
1992         int i;
1993
1994         for (i = 0; i < ARRAY_SIZE(keymap); i++)
1995                 if (keymap[i].alias == key)
1996                         return keymap[i].request;
1997
1998         return (enum request) key;
1999 }
2000
2001
2002 /*
2003  * Unicode / UTF-8 handling
2004  *
2005  * NOTE: Much of the following code for dealing with unicode is derived from
2006  * ELinks' UTF-8 code developed by Scrool <scroolik@gmail.com>. Origin file is
2007  * src/intl/charset.c from the utf8 branch commit elinks-0.11.0-g31f2c28.
2008  */
2009
2010 /* I've (over)annotated a lot of code snippets because I am not entirely
2011  * confident that the approach taken by this small UTF-8 interface is correct.
2012  * --jonas */
2013
2014 static inline int
2015 unicode_width(unsigned long c)
2016 {
2017         if (c >= 0x1100 &&
2018            (c <= 0x115f                         /* Hangul Jamo */
2019             || c == 0x2329
2020             || c == 0x232a
2021             || (c >= 0x2e80  && c <= 0xa4cf && c != 0x303f)
2022                                                 /* CJK ... Yi */
2023             || (c >= 0xac00  && c <= 0xd7a3)    /* Hangul Syllables */
2024             || (c >= 0xf900  && c <= 0xfaff)    /* CJK Compatibility Ideographs */
2025             || (c >= 0xfe30  && c <= 0xfe6f)    /* CJK Compatibility Forms */
2026             || (c >= 0xff00  && c <= 0xff60)    /* Fullwidth Forms */
2027             || (c >= 0xffe0  && c <= 0xffe6)
2028             || (c >= 0x20000 && c <= 0x2fffd)
2029             || (c >= 0x30000 && c <= 0x3fffd)))
2030                 return 2;
2031
2032         return 1;
2033 }
2034
2035 /* Number of bytes used for encoding a UTF-8 character indexed by first byte.
2036  * Illegal bytes are set one. */
2037 static const unsigned char utf8_bytes[256] = {
2038         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
2039         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
2040         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
2041         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
2042         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
2043         1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,
2044         2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,
2045         3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3, 4,4,4,4,4,4,4,4, 5,5,5,5,6,6,1,1,
2046 };
2047
2048 /* Decode UTF-8 multi-byte representation into a unicode character. */
2049 static inline unsigned long
2050 utf8_to_unicode(const char *string, size_t length)
2051 {
2052         unsigned long unicode;
2053
2054         switch (length) {
2055         case 1:
2056                 unicode  =   string[0];
2057                 break;
2058         case 2:
2059                 unicode  =  (string[0] & 0x1f) << 6;
2060                 unicode +=  (string[1] & 0x3f);
2061                 break;
2062         case 3:
2063                 unicode  =  (string[0] & 0x0f) << 12;
2064                 unicode += ((string[1] & 0x3f) << 6);
2065                 unicode +=  (string[2] & 0x3f);
2066                 break;
2067         case 4:
2068                 unicode  =  (string[0] & 0x0f) << 18;
2069                 unicode += ((string[1] & 0x3f) << 12);
2070                 unicode += ((string[2] & 0x3f) << 6);
2071                 unicode +=  (string[3] & 0x3f);
2072                 break;
2073         case 5:
2074                 unicode  =  (string[0] & 0x0f) << 24;
2075                 unicode += ((string[1] & 0x3f) << 18);
2076                 unicode += ((string[2] & 0x3f) << 12);
2077                 unicode += ((string[3] & 0x3f) << 6);
2078                 unicode +=  (string[4] & 0x3f);
2079                 break;
2080         case 6:
2081                 unicode  =  (string[0] & 0x01) << 30;
2082                 unicode += ((string[1] & 0x3f) << 24);
2083                 unicode += ((string[2] & 0x3f) << 18);
2084                 unicode += ((string[3] & 0x3f) << 12);
2085                 unicode += ((string[4] & 0x3f) << 6);
2086                 unicode +=  (string[5] & 0x3f);
2087                 break;
2088         default:
2089                 die("Invalid unicode length");
2090         }
2091
2092         /* Invalid characters could return the special 0xfffd value but NUL
2093          * should be just as good. */
2094         return unicode > 0xffff ? 0 : unicode;
2095 }
2096
2097 /* Calculates how much of string can be shown within the given maximum width
2098  * and sets trimmed parameter to non-zero value if all of string could not be
2099  * shown.
2100  *
2101  * Additionally, adds to coloffset how many many columns to move to align with
2102  * the expected position. Takes into account how multi-byte and double-width
2103  * characters will effect the cursor position.
2104  *
2105  * Returns the number of bytes to output from string to satisfy max_width. */
2106 static size_t
2107 utf8_length(const char *string, size_t max_width, int *coloffset, int *trimmed)
2108 {
2109         const char *start = string;
2110         const char *end = strchr(string, '\0');
2111         size_t mbwidth = 0;
2112         size_t width = 0;
2113
2114         *trimmed = 0;
2115
2116         while (string < end) {
2117                 int c = *(unsigned char *) string;
2118                 unsigned char bytes = utf8_bytes[c];
2119                 size_t ucwidth;
2120                 unsigned long unicode;
2121
2122                 if (string + bytes > end)
2123                         break;
2124
2125                 /* Change representation to figure out whether
2126                  * it is a single- or double-width character. */
2127
2128                 unicode = utf8_to_unicode(string, bytes);
2129                 /* FIXME: Graceful handling of invalid unicode character. */
2130                 if (!unicode)
2131                         break;
2132
2133                 ucwidth = unicode_width(unicode);
2134                 width  += ucwidth;
2135                 if (width > max_width) {
2136                         *trimmed = 1;
2137                         break;
2138                 }
2139
2140                 /* The column offset collects the differences between the
2141                  * number of bytes encoding a character and the number of
2142                  * columns will be used for rendering said character.
2143                  *
2144                  * So if some character A is encoded in 2 bytes, but will be
2145                  * represented on the screen using only 1 byte this will and up
2146                  * adding 1 to the multi-byte column offset.
2147                  *
2148                  * Assumes that no double-width character can be encoding in
2149                  * less than two bytes. */
2150                 if (bytes > ucwidth)
2151                         mbwidth += bytes - ucwidth;
2152
2153                 string  += bytes;
2154         }
2155
2156         *coloffset += mbwidth;
2157
2158         return string - start;
2159 }
2160
2161
2162 /*
2163  * Status management
2164  */
2165
2166 /* Whether or not the curses interface has been initialized. */
2167 static bool cursed = FALSE;
2168
2169 /* The status window is used for polling keystrokes. */
2170 static WINDOW *status_win;
2171
2172 /* Update status and title window. */
2173 static void
2174 report(const char *msg, ...)
2175 {
2176         static bool empty = TRUE;
2177         struct view *view = display[current_view];
2178
2179         if (!empty || *msg) {
2180                 va_list args;
2181
2182                 va_start(args, msg);
2183
2184                 werase(status_win);
2185                 wmove(status_win, 0, 0);
2186                 if (*msg) {
2187                         vwprintw(status_win, msg, args);
2188                         empty = FALSE;
2189                 } else {
2190                         empty = TRUE;
2191                 }
2192                 wrefresh(status_win);
2193
2194                 va_end(args);
2195         }
2196
2197         update_view_title(view);
2198
2199         /* Move the cursor to the right-most column of the cursor line.
2200          *
2201          * XXX: This could turn out to be a bit expensive, but it ensures that
2202          * the cursor does not jump around. */
2203         if (view->lines) {
2204                 wmove(view->win, view->lineno - view->offset, view->width - 1);
2205                 wrefresh(view->win);
2206         }
2207 }
2208
2209 /* Controls when nodelay should be in effect when polling user input. */
2210 static void
2211 set_nonblocking_input(bool loading)
2212 {
2213         static unsigned int loading_views;
2214
2215         if ((loading == FALSE && loading_views-- == 1) ||
2216             (loading == TRUE  && loading_views++ == 0))
2217                 nodelay(status_win, loading);
2218 }
2219
2220 static void
2221 init_display(void)
2222 {
2223         int x, y;
2224
2225         /* Initialize the curses library */
2226         if (isatty(STDIN_FILENO)) {
2227                 cursed = !!initscr();
2228         } else {
2229                 /* Leave stdin and stdout alone when acting as a pager. */
2230                 FILE *io = fopen("/dev/tty", "r+");
2231
2232                 cursed = !!newterm(NULL, io, io);
2233         }
2234
2235         if (!cursed)
2236                 die("Failed to initialize curses");
2237
2238         nonl();         /* Tell curses not to do NL->CR/NL on output */
2239         cbreak();       /* Take input chars one at a time, no wait for \n */
2240         noecho();       /* Don't echo input */
2241         leaveok(stdscr, TRUE);
2242
2243         if (has_colors())
2244                 init_colors();
2245
2246         getmaxyx(stdscr, y, x);
2247         status_win = newwin(1, 0, y - 1, 0);
2248         if (!status_win)
2249                 die("Failed to create status window");
2250
2251         /* Enable keyboard mapping */
2252         keypad(status_win, TRUE);
2253         wbkgdset(status_win, get_line_attr(LINE_STATUS));
2254 }
2255
2256
2257 /*
2258  * Repository references
2259  */
2260
2261 static struct ref *refs;
2262 static size_t refs_size;
2263
2264 /* Id <-> ref store */
2265 static struct ref ***id_refs;
2266 static size_t id_refs_size;
2267
2268 static struct ref **
2269 get_refs(char *id)
2270 {
2271         struct ref ***tmp_id_refs;
2272         struct ref **ref_list = NULL;
2273         size_t ref_list_size = 0;
2274         size_t i;
2275
2276         for (i = 0; i < id_refs_size; i++)
2277                 if (!strcmp(id, id_refs[i][0]->id))
2278                         return id_refs[i];
2279
2280         tmp_id_refs = realloc(id_refs, (id_refs_size + 1) * sizeof(*id_refs));
2281         if (!tmp_id_refs)
2282                 return NULL;
2283
2284         id_refs = tmp_id_refs;
2285
2286         for (i = 0; i < refs_size; i++) {
2287                 struct ref **tmp;
2288
2289                 if (strcmp(id, refs[i].id))
2290                         continue;
2291
2292                 tmp = realloc(ref_list, (ref_list_size + 1) * sizeof(*ref_list));
2293                 if (!tmp) {
2294                         if (ref_list)
2295                                 free(ref_list);
2296                         return NULL;
2297                 }
2298
2299                 ref_list = tmp;
2300                 if (ref_list_size > 0)
2301                         ref_list[ref_list_size - 1]->next = 1;
2302                 ref_list[ref_list_size] = &refs[i];
2303
2304                 /* XXX: The properties of the commit chains ensures that we can
2305                  * safely modify the shared ref. The repo references will
2306                  * always be similar for the same id. */
2307                 ref_list[ref_list_size]->next = 0;
2308                 ref_list_size++;
2309         }
2310
2311         if (ref_list)
2312                 id_refs[id_refs_size++] = ref_list;
2313
2314         return ref_list;
2315 }
2316
2317 static int
2318 load_refs(void)
2319 {
2320         const char *cmd_env = getenv("TIG_LS_REMOTE");
2321         const char *cmd = cmd_env && *cmd_env ? cmd_env : TIG_LS_REMOTE;
2322         FILE *pipe = popen(cmd, "r");
2323         char buffer[BUFSIZ];
2324         char *line;
2325
2326         if (!pipe)
2327                 return ERR;
2328
2329         while ((line = fgets(buffer, sizeof(buffer), pipe))) {
2330                 char *name = strchr(line, '\t');
2331                 struct ref *ref;
2332                 int namelen;
2333                 bool tag = FALSE;
2334                 bool tag_commit = FALSE;
2335
2336                 if (!name)
2337                         continue;
2338
2339                 *name++ = 0;
2340                 namelen = strlen(name) - 1;
2341
2342                 /* Commits referenced by tags has "^{}" appended. */
2343                 if (name[namelen - 1] == '}') {
2344                         while (namelen > 0 && name[namelen] != '^')
2345                                 namelen--;
2346                         if (namelen > 0)
2347                                 tag_commit = TRUE;
2348                 }
2349                 name[namelen] = 0;
2350
2351                 if (!strncmp(name, "refs/tags/", STRING_SIZE("refs/tags/"))) {
2352                         if (!tag_commit)
2353                                 continue;
2354                         name += STRING_SIZE("refs/tags/");
2355                         tag = TRUE;
2356
2357                 } else if (!strncmp(name, "refs/heads/", STRING_SIZE("refs/heads/"))) {
2358                         name += STRING_SIZE("refs/heads/");
2359
2360                 } else if (!strcmp(name, "HEAD")) {
2361                         continue;
2362                 }
2363
2364                 refs = realloc(refs, sizeof(*refs) * (refs_size + 1));
2365                 if (!refs)
2366                         return ERR;
2367
2368                 ref = &refs[refs_size++];
2369                 ref->tag = tag;
2370                 ref->name = strdup(name);
2371                 if (!ref->name)
2372                         return ERR;
2373
2374                 string_copy(ref->id, line);
2375         }
2376
2377         if (ferror(pipe))
2378                 return ERR;
2379
2380         pclose(pipe);
2381
2382         return OK;
2383 }
2384
2385 static int
2386 load_config(void)
2387 {
2388         FILE *pipe = popen("git repo-config --list", "r");
2389         char buffer[BUFSIZ];
2390         char *name;
2391
2392         if (!pipe)
2393                 return ERR;
2394
2395         while ((name = fgets(buffer, sizeof(buffer), pipe))) {
2396                 char *value = strchr(name, '=');
2397                 int valuelen, namelen;
2398
2399                 /* No boolean options, yet */
2400                 if (!value)
2401                         continue;
2402
2403                 namelen  = value - name;
2404
2405                 *value++ = 0;
2406                 valuelen = strlen(value);
2407                 if (valuelen > 0)
2408                         value[valuelen - 1] = 0;
2409
2410                 if (!strcmp(name, "i18n.commitencoding")) {
2411                         string_copy(opt_encoding, value);
2412                 }
2413         }
2414
2415         if (ferror(pipe))
2416                 return ERR;
2417
2418         pclose(pipe);
2419
2420         return OK;
2421 }
2422
2423 /*
2424  * Main
2425  */
2426
2427 #if __GNUC__ >= 3
2428 #define __NORETURN __attribute__((__noreturn__))
2429 #else
2430 #define __NORETURN
2431 #endif
2432
2433 static void __NORETURN
2434 quit(int sig)
2435 {
2436         /* XXX: Restore tty modes and let the OS cleanup the rest! */
2437         if (cursed)
2438                 endwin();
2439         exit(0);
2440 }
2441
2442 static void __NORETURN
2443 die(const char *err, ...)
2444 {
2445         va_list args;
2446
2447         endwin();
2448
2449         va_start(args, err);
2450         fputs("tig: ", stderr);
2451         vfprintf(stderr, err, args);
2452         fputs("\n", stderr);
2453         va_end(args);
2454
2455         exit(1);
2456 }
2457
2458 int
2459 main(int argc, char *argv[])
2460 {
2461         struct view *view;
2462         enum request request;
2463         size_t i;
2464
2465         signal(SIGINT, quit);
2466
2467         if (!parse_options(argc, argv))
2468                 return 0;
2469
2470         if (load_refs() == ERR)
2471                 die("Failed to load refs.");
2472
2473         /* Require a git repository unless when running in pager mode. */
2474         if (refs_size == 0 && opt_request != REQ_VIEW_PAGER)
2475                 die("Not a git repository");
2476
2477         if (load_config() == ERR)
2478                 die("Failed to load repo config.");
2479
2480         for (i = 0; i < ARRAY_SIZE(views) && (view = &views[i]); i++)
2481                 view->cmd_env = getenv(view->cmd_env);
2482
2483         request = opt_request;
2484
2485         init_display();
2486
2487         while (view_driver(display[current_view], request)) {
2488                 int key;
2489                 int i;
2490
2491                 foreach_view (view, i)
2492                         update_view(view);
2493
2494                 /* Refresh, accept single keystroke of input */
2495                 key = wgetch(status_win);
2496                 request = get_request(key);
2497
2498                 /* Some low-level request handling. This keeps access to
2499                  * status_win restricted. */
2500                 switch (request) {
2501                 case REQ_PROMPT:
2502                         report(":");
2503                         /* Temporarily switch to line-oriented and echoed
2504                          * input. */
2505                         nocbreak();
2506                         echo();
2507
2508                         if (wgetnstr(status_win, opt_cmd + 4, sizeof(opt_cmd) - 4) == OK) {
2509                                 memcpy(opt_cmd, "git ", 4);
2510                                 opt_request = REQ_VIEW_PAGER;
2511                         } else {
2512                                 request = ERR;
2513                         }
2514
2515                         noecho();
2516                         cbreak();
2517                         break;
2518
2519                 case REQ_SCREEN_RESIZE:
2520                 {
2521                         int height, width;
2522
2523                         getmaxyx(stdscr, height, width);
2524
2525                         /* Resize the status view and let the view driver take
2526                          * care of resizing the displayed views. */
2527                         wresize(status_win, 1, width);
2528                         mvwin(status_win, height - 1, 0);
2529                         wrefresh(status_win);
2530                         break;
2531                 }
2532                 default:
2533                         break;
2534                 }
2535         }
2536
2537         quit(0);
2538
2539         return 0;
2540 }
2541
2542 /**
2543  * [[refspec]]
2544  * Revision specification
2545  * ----------------------
2546  * This section describes various ways to specify what revisions to display
2547  * or otherwise limit the view to. tig(1) does not itself parse the described
2548  * revision options so refer to the relevant git man pages for futher
2549  * information. Relevant man pages besides git-log(1) are git-diff(1) and
2550  * git-rev-list(1).
2551  *
2552  * You can tune the interaction with git by making use of the options
2553  * explained in this section. For example, by configuring the environment
2554  * variables described in the  <<view-commands, "View commands">> section.
2555  *
2556  * Limit by path name
2557  * ~~~~~~~~~~~~~~~~~~
2558  * If you are interested only in those revisions that made changes to a
2559  * specific file (or even several files) list the files like this:
2560  *
2561  *      $ tig log Makefile README
2562  *
2563  * To avoid ambiguity with repository references such as tag name, be sure
2564  * to separate file names from other git options using "\--". So if you
2565  * have a file named 'master' it will clash with the reference named
2566  * 'master', and thus you will have to use:
2567  *
2568  *      $ tig log -- master
2569  *
2570  * NOTE: For the main view, avoiding ambiguity will in some cases require
2571  * you to specify two "\--" options. The first will make tig(1) stop
2572  * option processing and the latter will be passed to git log.
2573  *
2574  * Limit by date or number
2575  * ~~~~~~~~~~~~~~~~~~~~~~~
2576  * To speed up interaction with git, you can limit the amount of commits
2577  * to show both for the log and main view. Either limit by date using
2578  * e.g. `--since=1.month` or limit by the number of commits using `-n400`.
2579  *
2580  * If you are only interested in changed that happened between two dates
2581  * you can use:
2582  *
2583  *      $ tig -- --after="May 5th" --before="2006-05-16 15:44"
2584  *
2585  * NOTE: If you want to avoid having to quote dates containing spaces you
2586  * can use "." instead, e.g. `--after=May.5th`.
2587  *
2588  * Limiting by commit ranges
2589  * ~~~~~~~~~~~~~~~~~~~~~~~~~
2590  * Alternatively, commits can be limited to a specific range, such as
2591  * "all commits between 'tag-1.0' and 'tag-2.0'". For example:
2592  *
2593  *      $ tig log tag-1.0..tag-2.0
2594  *
2595  * This way of commit limiting makes it trivial to only browse the commits
2596  * which haven't been pushed to a remote branch. Assuming 'origin' is your
2597  * upstream remote branch, using:
2598  *
2599  *      $ tig log origin..HEAD
2600  *
2601  * will list what will be pushed to the remote branch. Optionally, the ending
2602  * 'HEAD' can be left out since it is implied.
2603  *
2604  * Limiting by reachability
2605  * ~~~~~~~~~~~~~~~~~~~~~~~~
2606  * Git interprets the range specifier "tag-1.0..tag-2.0" as
2607  * "all commits reachable from 'tag-2.0' but not from 'tag-1.0'".
2608  * Where reachability refers to what commits are ancestors (or part of the
2609  * history) of the branch or tagged revision in question.
2610  *
2611  * If you prefer to specify which commit to preview in this way use the
2612  * following:
2613  *
2614  *      $ tig log tag-2.0 ^tag-1.0
2615  *
2616  * You can think of '^' as a negation operator. Using this alternate syntax,
2617  * it is possible to further prune commits by specifying multiple branch
2618  * cut offs.
2619  *
2620  * Combining revisions specification
2621  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2622  * Revisions options can to some degree be combined, which makes it possible
2623  * to say "show at most 20 commits from within the last month that changed
2624  * files under the Documentation/ directory."
2625  *
2626  *      $ tig -- --since=1.month -n20 -- Documentation/
2627  *
2628  * Examining all repository references
2629  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2630  * In some cases, it can be useful to query changes across all references
2631  * in a repository. An example is to ask "did any line of development in
2632  * this repository change a particular file within the last week". This
2633  * can be accomplished using:
2634  *
2635  *      $ tig -- --all --since=1.week -- Makefile
2636  *
2637  * BUGS
2638  * ----
2639  * Known bugs and problems:
2640  *
2641  * - In it's current state tig is pretty much UTF-8 only.
2642  *
2643  * - If the screen width is very small the main view can draw
2644  *   outside the current view causing bad wrapping. Same goes
2645  *   for title and status windows.
2646  *
2647  * - The cursor can wrap-around on the last line and cause the
2648  *   window to scroll.
2649  *
2650  * TODO
2651  * ----
2652  * Features that should be explored.
2653  *
2654  * - Searching.
2655  *
2656  * - Locale support.
2657  *
2658  * COPYRIGHT
2659  * ---------
2660  * Copyright (c) 2006 Jonas Fonseca <fonseca@diku.dk>
2661  *
2662  * This program is free software; you can redistribute it and/or modify
2663  * it under the terms of the GNU General Public License as published by
2664  * the Free Software Foundation; either version 2 of the License, or
2665  * (at your option) any later version.
2666  *
2667  * SEE ALSO
2668  * --------
2669  * - link:http://www.kernel.org/pub/software/scm/git/docs/[git(7)],
2670  * - link:http://www.kernel.org/pub/software/scm/cogito/docs/[cogito(7)]
2671  *
2672  * Other git repository browsers:
2673 *
2674  *  - gitk(1)
2675  *  - qgit(1)
2676  *  - gitview(1)
2677  **/