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