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