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