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