X-Git-Url: https://git.tokkee.org/?p=tig.git;a=blobdiff_plain;f=tig.c;h=fd19f5403e8351618b37e2e1a97bcc1af9a6906b;hp=0c57533fb7c9319315b5cd675083c31ea09c0bfc;hb=8869ebf96178755c9c88e011db35048b93647ae2;hpb=1e27747895fa12b047dcd5248a75e71cc3bf67fa diff --git a/tig.c b/tig.c index 0c57533..fd19f54 100644 --- a/tig.c +++ b/tig.c @@ -114,6 +114,7 @@ static void report(const char *msg, ...); #define S_ISGITLINK(mode) (((mode) & S_IFMT) == 0160000) /* Some ASCII-shorthands fitted into the ncurses namespace. */ +#define KEY_CTL(x) ((x) & 0x1f) /* KEY_CTL(A) == ^A == \1 */ #define KEY_TAB '\t' #define KEY_RETURN '\r' #define KEY_ESC 27 @@ -140,13 +141,6 @@ static struct ref_list *get_ref_list(const char *id); static void foreach_ref(bool (*visitor)(void *data, const struct ref *ref), void *data); static int load_refs(void); -enum format_flags { - FORMAT_ALL, /* Perform replacement in all arguments. */ - FORMAT_NONE /* No replacement should be performed. */ -}; - -static bool format_argv(const char *dst[], const char *src[], enum format_flags flags); - enum input_status { INPUT_OK, INPUT_SKIP, @@ -216,7 +210,7 @@ string_ncopy_do(char *dst, size_t dstlen, const char *src, size_t srclen) #define string_add(dst, from, src) \ string_ncopy_do(dst + (from), sizeof(dst) - (from), src, sizeof(src)) -static void +static size_t string_expand(char *dst, size_t dstlen, const char *src, int tabsize) { size_t size, pos; @@ -235,6 +229,7 @@ string_expand(char *dst, size_t dstlen, const char *src, int tabsize) } dst[size] = 0; + return pos; } static char * @@ -678,6 +673,66 @@ argv_from_env(const char **argv, const char *name) return !env || argv_from_string(argv, &argc, env); } +static void +argv_free(const char *argv[]) +{ + int argc; + + if (!argv) + return; + for (argc = 0; argv[argc]; argc++) + free((void *) argv[argc]); + argv[0] = NULL; +} + +static size_t +argv_size(const char **argv) +{ + int argc = 0; + + while (argv && argv[argc]) + argc++; + + return argc; +} + +DEFINE_ALLOCATOR(argv_realloc, const char *, SIZEOF_ARG) + +static bool +argv_append(const char ***argv, const char *arg) +{ + size_t argc = argv_size(*argv); + + if (!argv_realloc(argv, argc, 2)) + return FALSE; + + (*argv)[argc++] = strdup(arg); + (*argv)[argc] = NULL; + return TRUE; +} + +static bool +argv_append_array(const char ***dst_argv, const char *src_argv[]) +{ + int i; + + for (i = 0; src_argv && src_argv[i]; i++) + if (!argv_append(dst_argv, src_argv[i])) + return FALSE; + return TRUE; +} + +static bool +argv_copy(const char ***dst, const char *src[]) +{ + int argc; + + for (argc = 0; src[argc]; argc++) + if (!argv_append(dst, src[argc])) + return FALSE; + return TRUE; +} + /* * Executing external commands. @@ -693,12 +748,9 @@ enum io_type { }; struct io { - enum io_type type; /* The requested type of pipe. */ - const char *dir; /* Directory from which to execute. */ - pid_t pid; /* PID of spawned process. */ int pipe; /* Pipe end for reading or writing. */ + pid_t pid; /* PID of spawned process. */ int error; /* Error status. */ - const char *argv[SIZEOF_ARG]; /* Shell command arguments. */ char *buf; /* Read buffer. */ size_t bufalloc; /* Allocated buffer size. */ size_t bufsize; /* Buffer content size. */ @@ -707,30 +759,10 @@ struct io { }; static void -io_reset(struct io *io) +io_init(struct io *io) { + memset(io, 0, sizeof(*io)); io->pipe = -1; - io->pid = 0; - io->buf = io->bufpos = NULL; - io->bufalloc = io->bufsize = 0; - io->error = 0; - io->eof = 0; -} - -static void -io_init(struct io *io, const char *dir, enum io_type type) -{ - io_reset(io); - io->type = type; - io->dir = dir; -} - -static bool -io_format(struct io *io, const char *dir, enum io_type type, - const char *argv[], enum format_flags flags) -{ - io_init(io, dir, type); - return format_argv(io->argv, argv, flags); } static bool @@ -740,7 +772,7 @@ io_open(struct io *io, const char *fmt, ...) bool fits; va_list args; - io_init(io, NULL, IO_FD); + io_init(io); va_start(args, fmt); fits = vsnprintf(name, sizeof(name), fmt, args) < sizeof(name); @@ -770,7 +802,7 @@ io_done(struct io *io) if (io->pipe != -1) close(io->pipe); free(io->buf); - io_reset(io); + io_init(io); while (pid > 0) { int status; @@ -793,35 +825,37 @@ io_done(struct io *io) } static bool -io_start(struct io *io) +io_run(struct io *io, enum io_type type, const char *dir, const char *argv[], ...) { int pipefds[2] = { -1, -1 }; + va_list args; - if (io->type == IO_FD) - return TRUE; + io_init(io); - if ((io->type == IO_RD || io->type == IO_WR) && pipe(pipefds) < 0) { + if ((type == IO_RD || type == IO_WR) && pipe(pipefds) < 0) { io->error = errno; return FALSE; - } else if (io->type == IO_AP) { - pipefds[1] = io->pipe; + } else if (type == IO_AP) { + va_start(args, argv); + pipefds[1] = va_arg(args, int); + va_end(args); } if ((io->pid = fork())) { if (io->pid == -1) io->error = errno; - if (pipefds[!(io->type == IO_WR)] != -1) - close(pipefds[!(io->type == IO_WR)]); + if (pipefds[!(type == IO_WR)] != -1) + close(pipefds[!(type == IO_WR)]); if (io->pid != -1) { - io->pipe = pipefds[!!(io->type == IO_WR)]; + io->pipe = pipefds[!!(type == IO_WR)]; return TRUE; } } else { - if (io->type != IO_FG) { + if (type != IO_FG) { int devnull = open("/dev/null", O_RDWR); - int readfd = io->type == IO_WR ? pipefds[0] : devnull; - int writefd = (io->type == IO_RD || io->type == IO_AP) + int readfd = type == IO_WR ? pipefds[0] : devnull; + int writefd = (type == IO_RD || type == IO_AP) ? pipefds[1] : devnull; dup2(readfd, STDIN_FILENO); @@ -835,71 +869,42 @@ io_start(struct io *io) close(pipefds[1]); } - if (io->dir && *io->dir && chdir(io->dir) == -1) + if (dir && *dir && chdir(dir) == -1) exit(errno); - execvp(io->argv[0], (char *const*) io->argv); + execvp(argv[0], (char *const*) argv); exit(errno); } - if (pipefds[!!(io->type == IO_WR)] != -1) - close(pipefds[!!(io->type == IO_WR)]); + if (pipefds[!!(type == IO_WR)] != -1) + close(pipefds[!!(type == IO_WR)]); return FALSE; } static bool -io_run(struct io *io, const char **argv, const char *dir, enum io_type type) +io_complete(enum io_type type, const char **argv, const char *dir, int fd) { - io_init(io, dir, type); - if (!format_argv(io->argv, argv, FORMAT_NONE)) - return FALSE; - return io_start(io); -} + struct io io; -static int -io_complete(struct io *io) -{ - return io_start(io) && io_done(io); + return io_run(&io, type, dir, argv, fd) && io_done(&io); } -static int +static bool io_run_bg(const char **argv) { - struct io io = {}; - - if (!io_format(&io, NULL, IO_BG, argv, FORMAT_NONE)) - return FALSE; - return io_complete(&io); + return io_complete(IO_BG, argv, NULL, -1); } static bool io_run_fg(const char **argv, const char *dir) { - struct io io = {}; - - if (!io_format(&io, dir, IO_FG, argv, FORMAT_NONE)) - return FALSE; - return io_complete(&io); -} - -static bool -io_run_append(const char **argv, enum format_flags flags, int fd) -{ - struct io io = {}; - - if (!io_format(&io, NULL, IO_AP, argv, flags)) { - close(fd); - return FALSE; - } - - io.pipe = fd; - return io_complete(&io); + return io_complete(IO_FG, argv, dir, -1); } static bool -io_run_rd(struct io *io, const char **argv, const char *dir, enum format_flags flags) +io_run_append(const char **argv, int fd) { - return io_format(io, dir, IO_RD, argv, flags) && io_start(io); + return io_complete(IO_AP, argv, NULL, fd); } static bool @@ -1034,22 +1039,20 @@ io_read_buf(struct io *io, char buf[], size_t bufsize) static bool io_run_buf(const char **argv, char buf[], size_t bufsize) { - struct io io = {}; + struct io io; - return io_run_rd(&io, argv, NULL, FORMAT_NONE) - && io_read_buf(&io, buf, bufsize); + return io_run(&io, IO_RD, NULL, argv) && io_read_buf(&io, buf, bufsize); } +typedef int (*io_read_fn)(char *, size_t, char *, size_t, void *data); + static int io_load(struct io *io, const char *separators, - int (*read_property)(char *, size_t, char *, size_t)) + io_read_fn read_property, void *data) { char *name; int state = OK; - if (!io_start(io)) - return ERR; - while (state == OK && (name = io_get(io, '\n', TRUE))) { char *value; size_t namelen; @@ -1068,7 +1071,7 @@ io_load(struct io *io, const char *separators, valuelen = 0; } - state = read_property(name, namelen, value, valuelen); + state = read_property(name, namelen, value, valuelen, data); } if (state != ERR && io_error(io)) @@ -1080,12 +1083,13 @@ io_load(struct io *io, const char *separators, static int io_run_load(const char **argv, const char *separators, - int (*read_property)(char *, size_t, char *, size_t)) + io_read_fn read_property, void *data) { - struct io io = {}; + struct io io; - return io_format(&io, NULL, IO_RD, argv, FORMAT_NONE) - ? io_load(&io, separators, read_property) : ERR; + if (!io_run(&io, IO_RD, NULL, argv)) + return ERR; + return io_load(&io, separators, read_property, data); } @@ -1134,6 +1138,7 @@ io_run_load(const char **argv, const char *separators, REQ_(MOVE_LAST_LINE, "Move cursor to last line"), \ \ REQ_GROUP("Scrolling") \ + REQ_(SCROLL_FIRST_COL, "Scroll to the first line columns"), \ REQ_(SCROLL_LEFT, "Scroll two columns left"), \ REQ_(SCROLL_RIGHT, "Scroll two columns right"), \ REQ_(SCROLL_LINE_UP, "Scroll one line up"), \ @@ -1151,7 +1156,6 @@ io_run_load(const char **argv, const char *separators, REQ_(OPTIONS, "Open option menu"), \ REQ_(TOGGLE_LINENO, "Toggle line numbers"), \ REQ_(TOGGLE_DATE, "Toggle date display"), \ - REQ_(TOGGLE_DATE_SHORT, "Toggle short (date-only) dates"), \ REQ_(TOGGLE_AUTHOR, "Toggle author display"), \ REQ_(TOGGLE_REV_GRAPH, "Toggle revision graph visualization"), \ REQ_(TOGGLE_REFS, "Toggle reference display (tags/branches)"), \ @@ -1221,6 +1225,7 @@ static bool opt_line_number = FALSE; static bool opt_line_graphics = TRUE; static bool opt_rev_graph = FALSE; static bool opt_show_refs = TRUE; +static bool opt_untracked_dirs_content = TRUE; static int opt_num_interval = 5; static double opt_hscroll = 0.50; static double opt_scale_split_view = 2.0 / 3.0; @@ -1241,6 +1246,9 @@ static char opt_git_dir[SIZEOF_STR] = ""; static signed char opt_is_inside_work_tree = -1; /* set to TRUE or FALSE */ static char opt_editor[SIZEOF_STR] = ""; static FILE *opt_tty = NULL; +static const char **opt_diff_argv = NULL; +static const char **opt_rev_argv = NULL; +static const char **opt_file_argv = NULL; #define is_initial_commit() (!get_ref_head()) #define is_head_commit(rev) (!strcmp((rev), "HEAD") || (get_ref_head() && !strcmp(rev, get_ref_head()->id))) @@ -1279,6 +1287,8 @@ LINE(AUTHOR, "author ", COLOR_GREEN, COLOR_DEFAULT, 0), \ LINE(COMMITTER, "committer ", COLOR_MAGENTA, COLOR_DEFAULT, 0), \ LINE(SIGNOFF, " Signed-off-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \ LINE(ACKED, " Acked-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(TESTED, " Tested-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \ +LINE(REVIEWED, " Reviewed-by", COLOR_YELLOW, COLOR_DEFAULT, 0), \ LINE(DEFAULT, "", COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL), \ LINE(CURSOR, "", COLOR_WHITE, COLOR_GREEN, A_BOLD), \ LINE(STATUS, "", COLOR_GREEN, COLOR_DEFAULT, 0), \ @@ -1431,7 +1441,9 @@ static struct keybinding default_keybindings[] = { { KEY_TAB, REQ_VIEW_NEXT }, { KEY_RETURN, REQ_ENTER }, { KEY_UP, REQ_PREVIOUS }, + { KEY_CTL('P'), REQ_PREVIOUS }, { KEY_DOWN, REQ_NEXT }, + { KEY_CTL('N'), REQ_NEXT }, { 'R', REQ_REFRESH }, { KEY_F(5), REQ_REFRESH }, { 'O', REQ_MAXIMIZE }, @@ -1442,16 +1454,21 @@ static struct keybinding default_keybindings[] = { { KEY_HOME, REQ_MOVE_FIRST_LINE }, { KEY_END, REQ_MOVE_LAST_LINE }, { KEY_NPAGE, REQ_MOVE_PAGE_DOWN }, + { KEY_CTL('D'), REQ_MOVE_PAGE_DOWN }, { ' ', REQ_MOVE_PAGE_DOWN }, { KEY_PPAGE, REQ_MOVE_PAGE_UP }, + { KEY_CTL('U'), REQ_MOVE_PAGE_UP }, { 'b', REQ_MOVE_PAGE_UP }, { '-', REQ_MOVE_PAGE_UP }, /* Scrolling */ + { '|', REQ_SCROLL_FIRST_COL }, { KEY_LEFT, REQ_SCROLL_LEFT }, { KEY_RIGHT, REQ_SCROLL_RIGHT }, { KEY_IC, REQ_SCROLL_LINE_UP }, + { KEY_CTL('Y'), REQ_SCROLL_LINE_UP }, { KEY_DC, REQ_SCROLL_LINE_DOWN }, + { KEY_CTL('E'), REQ_SCROLL_LINE_DOWN }, { 'w', REQ_SCROLL_PAGE_UP }, { 's', REQ_SCROLL_PAGE_DOWN }, @@ -1466,6 +1483,7 @@ static struct keybinding default_keybindings[] = { { 'z', REQ_STOP_LOADING }, { 'v', REQ_SHOW_VERSION }, { 'r', REQ_SCREEN_REDRAW }, + { KEY_CTL('L'), REQ_SCREEN_REDRAW }, { 'o', REQ_OPTIONS }, { '.', REQ_TOGGLE_LINENO }, { 'D', REQ_TOGGLE_DATE }, @@ -1614,16 +1632,17 @@ get_key_value(const char *name) if (!strcasecmp(key_table[i].name, name)) return key_table[i].value; + if (strlen(name) == 2 && name[0] == '^' && isprint(*name)) + return (int)name[1] & 0x1f; if (strlen(name) == 1 && isprint(*name)) return (int) *name; - return ERR; } static const char * get_key_name(int key_value) { - static char key_char[] = "'X'"; + static char key_char[] = "'X'\0"; const char *seq = NULL; int key; @@ -1631,10 +1650,17 @@ get_key_name(int key_value) if (key_table[key].value == key_value) seq = key_table[key].name; - if (seq == NULL && - key_value < 127 && - isprint(key_value)) { - key_char[1] = (char) key_value; + if (seq == NULL && key_value < 0x7f) { + char *s = key_char + 1; + + if (key_value >= 0x20) { + *s++ = key_value; + } else { + *s++ = '^'; + *s++ = 0x40 | (key_value & 0x1f); + } + *s++ = '\''; + *s++ = '\0'; seq = key_char; } @@ -1711,7 +1737,7 @@ get_keys(enum keymap keymap, enum request request, bool all) struct run_request { enum keymap keymap; int key; - const char *argv[SIZEOF_ARG]; + const char **argv; }; static struct run_request *run_request; @@ -1720,22 +1746,19 @@ static size_t run_requests; DEFINE_ALLOCATOR(realloc_run_requests, struct run_request, 8) static enum request -add_run_request(enum keymap keymap, int key, int argc, const char **argv) +add_run_request(enum keymap keymap, int key, const char **argv) { struct run_request *req; - if (argc >= ARRAY_SIZE(req->argv) - 1) - return REQ_NONE; - if (!realloc_run_requests(&run_request, run_requests, 1)) return REQ_NONE; req = &run_request[run_requests]; req->keymap = keymap; req->key = key; - req->argv[0] = NULL; + req->argv = NULL; - if (!format_argv(req->argv, argv, FORMAT_NONE)) + if (!argv_copy(&req->argv, argv)) return REQ_NONE; return REQ_NONE + ++run_requests; @@ -1756,16 +1779,11 @@ add_builtin_run_requests(void) const char *checkout[] = { "git", "checkout", "%(branch)", NULL }; const char *commit[] = { "git", "commit", NULL }; const char *gc[] = { "git", "gc", NULL }; - struct { - enum keymap keymap; - int key; - int argc; - const char **argv; - } reqs[] = { - { KEYMAP_MAIN, 'C', ARRAY_SIZE(cherry_pick) - 1, cherry_pick }, - { KEYMAP_STATUS, 'C', ARRAY_SIZE(commit) - 1, commit }, - { KEYMAP_BRANCH, 'C', ARRAY_SIZE(checkout) - 1, checkout }, - { KEYMAP_GENERIC, 'G', ARRAY_SIZE(gc) - 1, gc }, + struct run_request reqs[] = { + { KEYMAP_MAIN, 'C', cherry_pick }, + { KEYMAP_STATUS, 'C', commit }, + { KEYMAP_BRANCH, 'C', checkout }, + { KEYMAP_GENERIC, 'G', gc }, }; int i; @@ -1774,7 +1792,7 @@ add_builtin_run_requests(void) if (req != reqs[i].key) continue; - req = add_run_request(reqs[i].keymap, reqs[i].key, reqs[i].argc, reqs[i].argv); + req = add_run_request(reqs[i].keymap, reqs[i].key, reqs[i].argv); if (req != REQ_NONE) add_keybinding(reqs[i].keymap, req, reqs[i].key); } @@ -1784,9 +1802,36 @@ add_builtin_run_requests(void) * User config file handling. */ -static int config_lineno; -static bool config_errors; -static const char *config_msg; +#define OPT_ERR_INFO \ + OPT_ERR_(INTEGER_VALUE_OUT_OF_BOUND, "Integer value out of bound"), \ + OPT_ERR_(INVALID_STEP_VALUE, "Invalid step value"), \ + OPT_ERR_(NO_OPTION_VALUE, "No option value"), \ + OPT_ERR_(NO_VALUE_ASSIGNED, "No value assigned"), \ + OPT_ERR_(OBSOLETE_REQUEST_NAME, "Obsolete request name"), \ + OPT_ERR_(TOO_MANY_OPTION_ARGUMENTS, "Too many option arguments"), \ + OPT_ERR_(UNKNOWN_ATTRIBUTE, "Unknown attribute"), \ + OPT_ERR_(UNKNOWN_COLOR, "Unknown color"), \ + OPT_ERR_(UNKNOWN_COLOR_NAME, "Unknown color name"), \ + OPT_ERR_(UNKNOWN_KEY, "Unknown key"), \ + OPT_ERR_(UNKNOWN_KEY_MAP, "Unknown key map"), \ + OPT_ERR_(UNKNOWN_OPTION_COMMAND, "Unknown option command"), \ + OPT_ERR_(UNKNOWN_REQUEST_NAME, "Unknown request name"), \ + OPT_ERR_(UNKNOWN_VARIABLE_NAME, "Unknown variable name"), \ + OPT_ERR_(UNMATCHED_QUOTATION, "Unmatched quotation"), \ + OPT_ERR_(WRONG_NUMBER_OF_ARGUMENTS, "Wrong number of arguments"), + +enum option_code { +#define OPT_ERR_(name, msg) OPT_ERR_ ## name + OPT_ERR_INFO +#undef OPT_ERR_ + OPT_OK +}; + +static const char *option_errors[] = { +#define OPT_ERR_(name, msg) msg + OPT_ERR_INFO +#undef OPT_ERR_ +}; static const struct enum_map color_map[] = { #define COLOR_MAP(name) ENUM_MAP(#name, COLOR_##name) @@ -1814,39 +1859,37 @@ static const struct enum_map attr_map[] = { #define set_attribute(attr, name) map_enum(attr, attr_map, name) -static int parse_step(double *opt, const char *arg) +static enum option_code +parse_step(double *opt, const char *arg) { *opt = atoi(arg); if (!strchr(arg, '%')) - return OK; + return OPT_OK; /* "Shift down" so 100% and 1 does not conflict. */ *opt = (*opt - 1) / 100; if (*opt >= 1.0) { *opt = 0.99; - config_msg = "Step value larger than 100%"; - return ERR; + return OPT_ERR_INVALID_STEP_VALUE; } if (*opt < 0.0) { *opt = 1; - config_msg = "Invalid step value"; - return ERR; + return OPT_ERR_INVALID_STEP_VALUE; } - return OK; + return OPT_OK; } -static int +static enum option_code parse_int(int *opt, const char *arg, int min, int max) { int value = atoi(arg); if (min <= value && value <= max) { *opt = value; - return OK; + return OPT_OK; } - config_msg = "Integer value out of bound"; - return ERR; + return OPT_ERR_INTEGER_VALUE_OUT_OF_BOUND; } static bool @@ -1860,15 +1903,13 @@ set_color(int *color, const char *name) } /* Wants: object fgcolor bgcolor [attribute] */ -static int +static enum option_code option_color_command(int argc, const char *argv[]) { struct line_info *info; - if (argc < 3) { - config_msg = "Wrong number of arguments given to color command"; - return ERR; - } + if (argc < 3) + return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS; info = get_line_info(argv[0]); if (!info) { @@ -1880,16 +1921,14 @@ option_color_command(int argc, const char *argv[]) int index; if (!map_enum(&index, obsolete, argv[0])) { - config_msg = "Unknown color name"; - return ERR; + return OPT_ERR_UNKNOWN_COLOR_NAME; } info = &line_info[index]; } if (!set_color(&info->fg, argv[1]) || !set_color(&info->bg, argv[2])) { - config_msg = "Unknown color"; - return ERR; + return OPT_ERR_UNKNOWN_COLOR; } info->attr = 0; @@ -1897,43 +1936,42 @@ option_color_command(int argc, const char *argv[]) int attr; if (!set_attribute(&attr, argv[argc])) { - config_msg = "Unknown attribute"; - return ERR; + return OPT_ERR_UNKNOWN_ATTRIBUTE; } info->attr |= attr; } - return OK; + return OPT_OK; } -static int parse_bool(bool *opt, const char *arg) +static enum option_code +parse_bool(bool *opt, const char *arg) { *opt = (!strcmp(arg, "1") || !strcmp(arg, "true") || !strcmp(arg, "yes")) ? TRUE : FALSE; - return OK; + return OPT_OK; } -static int parse_enum_do(unsigned int *opt, const char *arg, - const struct enum_map *map, size_t map_size) +static enum option_code +parse_enum_do(unsigned int *opt, const char *arg, + const struct enum_map *map, size_t map_size) { bool is_true; assert(map_size > 1); if (map_enum_do(map, map_size, (int *) opt, arg)) - return OK; - - if (parse_bool(&is_true, arg) != OK) - return ERR; + return OPT_OK; + parse_bool(&is_true, arg); *opt = is_true ? map[1].value : map[0].value; - return OK; + return OPT_OK; } #define parse_enum(opt, arg, map) \ parse_enum_do(opt, arg, map, ARRAY_SIZE(map)) -static int +static enum option_code parse_string(char *opt, const char *arg, size_t optsize) { int arglen = strlen(arg); @@ -1941,30 +1979,24 @@ parse_string(char *opt, const char *arg, size_t optsize) switch (arg[0]) { case '\"': case '\'': - if (arglen == 1 || arg[arglen - 1] != arg[0]) { - config_msg = "Unmatched quotation"; - return ERR; - } + if (arglen == 1 || arg[arglen - 1] != arg[0]) + return OPT_ERR_UNMATCHED_QUOTATION; arg += 1; arglen -= 2; default: string_ncopy_do(opt, optsize, arg, arglen); - return OK; + return OPT_OK; } } /* Wants: name = value */ -static int +static enum option_code option_set_command(int argc, const char *argv[]) { - if (argc != 3) { - config_msg = "Wrong number of arguments given to set command"; - return ERR; - } + if (argc != 3) + return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS; - if (strcmp(argv[1], "=")) { - config_msg = "No value assigned"; - return ERR; - } + if (strcmp(argv[1], "=")) + return OPT_ERR_NO_VALUE_ASSIGNED; if (!strcmp(argv[0], "show-author")) return parse_enum(&opt_author, argv[2], author_map); @@ -2002,33 +2034,29 @@ option_set_command(int argc, const char *argv[]) if (!strcmp(argv[0], "commit-encoding")) return parse_string(opt_encoding, argv[2], sizeof(opt_encoding)); - config_msg = "Unknown variable name"; - return ERR; + if (!strcmp(argv[0], "status-untracked-dirs")) + return parse_bool(&opt_untracked_dirs_content, argv[2]); + + return OPT_ERR_UNKNOWN_VARIABLE_NAME; } /* Wants: mode request key */ -static int +static enum option_code option_bind_command(int argc, const char *argv[]) { enum request request; int keymap = -1; int key; - if (argc < 3) { - config_msg = "Wrong number of arguments given to bind command"; - return ERR; - } + if (argc < 3) + return OPT_ERR_WRONG_NUMBER_OF_ARGUMENTS; - if (!set_keymap(&keymap, argv[0])) { - config_msg = "Unknown key map"; - return ERR; - } + if (!set_keymap(&keymap, argv[0])) + return OPT_ERR_UNKNOWN_KEY_MAP; key = get_key_value(argv[1]); - if (key == ERR) { - config_msg = "Unknown key"; - return ERR; - } + if (key == ERR) + return OPT_ERR_UNKNOWN_KEY; request = get_request(argv[2]); if (request == REQ_UNKNOWN) { @@ -2042,32 +2070,27 @@ option_bind_command(int argc, const char *argv[]) if (map_enum(&alias, obsolete, argv[2])) { if (alias != REQ_NONE) add_keybinding(keymap, alias, key); - config_msg = "Obsolete request name"; - return ERR; + return OPT_ERR_OBSOLETE_REQUEST_NAME; } } if (request == REQ_UNKNOWN && *argv[2]++ == '!') - request = add_run_request(keymap, key, argc - 2, argv + 2); - if (request == REQ_UNKNOWN) { - config_msg = "Unknown request name"; - return ERR; - } + request = add_run_request(keymap, key, argv + 2); + if (request == REQ_UNKNOWN) + return OPT_ERR_UNKNOWN_REQUEST_NAME; add_keybinding(keymap, request, key); - return OK; + return OPT_OK; } -static int +static enum option_code set_option(const char *opt, char *value) { const char *argv[SIZEOF_ARG]; int argc = 0; - if (!argv_from_string(argv, &argc, value)) { - config_msg = "Too many option arguments"; - return ERR; - } + if (!argv_from_string(argv, &argc, value)) + return OPT_ERR_TOO_MANY_OPTION_ARGUMENTS; if (!strcmp(opt, "color")) return option_color_command(argc, argv); @@ -2078,17 +2101,21 @@ set_option(const char *opt, char *value) if (!strcmp(opt, "bind")) return option_bind_command(argc, argv); - config_msg = "Unknown option command"; - return ERR; + return OPT_ERR_UNKNOWN_OPTION_COMMAND; } +struct config_state { + int lineno; + bool errors; +}; + static int -read_option(char *opt, size_t optlen, char *value, size_t valuelen) +read_option(char *opt, size_t optlen, char *value, size_t valuelen, void *data) { - int status = OK; + struct config_state *config = data; + enum option_code status = OPT_ERR_NO_OPTION_VALUE; - config_lineno++; - config_msg = "Internal error"; + config->lineno++; /* Check for comment markers, since read_properties() will * only ensure opt and value are split at first " \t". */ @@ -2096,11 +2123,7 @@ read_option(char *opt, size_t optlen, char *value, size_t valuelen) if (optlen == 0) return OK; - if (opt[optlen] != 0) { - config_msg = "No option value"; - status = ERR; - - } else { + if (opt[optlen] == 0) { /* Look for comment endings in the value. */ size_t len = strcspn(value, "#"); @@ -2112,10 +2135,10 @@ read_option(char *opt, size_t optlen, char *value, size_t valuelen) status = set_option(opt, value); } - if (status == ERR) { + if (status != OPT_OK) { warn("Error on line %d, near '%.*s': %s", - config_lineno, (int) optlen, opt, config_msg); - config_errors = TRUE; + config->lineno, (int) optlen, opt, option_errors[status]); + config->errors = TRUE; } /* Always keep going if errors are encountered. */ @@ -2125,17 +2148,15 @@ read_option(char *opt, size_t optlen, char *value, size_t valuelen) static void load_option_file(const char *path) { - struct io io = {}; + struct config_state config = { 0, FALSE }; + struct io io; /* It's OK that the file doesn't exist. */ if (!io_open(&io, "%s", path)) return; - config_lineno = 0; - config_errors = FALSE; - - if (io_load(&io, " \t", read_option) == ERR || - config_errors == TRUE) + if (io_load(&io, " \t", read_option, &config) == ERR || + config.errors == TRUE) warn("Errors while loading %s.", path); } @@ -2145,6 +2166,7 @@ load_options(void) const char *home = getenv("HOME"); const char *tigrc_user = getenv("TIGRC_USER"); const char *tigrc_system = getenv("TIGRC_SYSTEM"); + const char *tig_diff_opts = getenv("TIG_DIFF_OPTS"); char buf[SIZEOF_STR]; if (!tigrc_system) @@ -2162,6 +2184,17 @@ load_options(void) * that conflict with keybindings. */ add_builtin_run_requests(); + if (!opt_diff_argv && tig_diff_opts && *tig_diff_opts) { + static const char *diff_opts[SIZEOF_ARG] = { NULL }; + int argc = 0; + + if (!string_format(buf, "%s", tig_diff_opts) || + !argv_from_string(diff_opts, &argc, buf)) + die("TIG_DIFF_OPTS contains too many arguments"); + else if (!argv_copy(&opt_diff_argv, diff_opts)) + die("Failed to format TIG_DIFF_OPTS arguments"); + } + return OK; } @@ -2212,7 +2245,6 @@ struct view { enum keymap keymap; /* What keymap does this view have */ bool git_dir; /* Whether the view requires a git directory. */ - bool refresh; /* Whether the view supports refreshing. */ char ref[SIZEOF_REF]; /* Hovered commit reference */ char vid[SIZEOF_REF]; /* View ID. Set to id member when updating. */ @@ -2237,6 +2269,7 @@ struct view { /* If non-NULL, points to the view that opened this view. If this view * is closed tig will switch back to the parent view. */ struct view *parent; + struct view *prev; /* Buffering */ size_t lines; /* Total number of lines */ @@ -2250,6 +2283,8 @@ struct view { bool has_scrolled; /* View was scrolled. */ /* Loading */ + const char **argv; /* Shell command arguments. */ + const char *dir; /* Directory from which to execute. */ struct io io; struct io *pipe; time_t start_time; @@ -2289,24 +2324,24 @@ static struct view_ops status_ops; static struct view_ops tree_ops; static struct view_ops branch_ops; -#define VIEW_STR(type, name, env, ref, ops, map, git, refresh) \ - { type, name, #env, ref, ops, map, git, refresh } +#define VIEW_STR(type, name, env, ref, ops, map, git) \ + { type, name, #env, ref, ops, map, git } -#define VIEW_(id, name, ops, git, refresh, ref) \ - VIEW_STR(VIEW_##id, name, TIG_##id##_CMD, ref, ops, KEYMAP_##id, git, refresh) +#define VIEW_(id, name, ops, git, ref) \ + VIEW_STR(VIEW_##id, name, TIG_##id##_CMD, ref, ops, KEYMAP_##id, git) static struct view views[] = { - VIEW_(MAIN, "main", &main_ops, TRUE, TRUE, ref_head), - VIEW_(DIFF, "diff", &diff_ops, TRUE, FALSE, ref_commit), - VIEW_(LOG, "log", &log_ops, TRUE, TRUE, ref_head), - VIEW_(TREE, "tree", &tree_ops, TRUE, FALSE, ref_commit), - VIEW_(BLOB, "blob", &blob_ops, TRUE, FALSE, ref_blob), - VIEW_(BLAME, "blame", &blame_ops, TRUE, FALSE, ref_commit), - VIEW_(BRANCH, "branch", &branch_ops, TRUE, TRUE, ref_head), - VIEW_(HELP, "help", &help_ops, FALSE, FALSE, ""), - VIEW_(PAGER, "pager", &pager_ops, FALSE, FALSE, "stdin"), - VIEW_(STATUS, "status", &status_ops, TRUE, TRUE, ""), - VIEW_(STAGE, "stage", &stage_ops, TRUE, TRUE, ""), + VIEW_(MAIN, "main", &main_ops, TRUE, ref_head), + VIEW_(DIFF, "diff", &diff_ops, TRUE, ref_commit), + VIEW_(LOG, "log", &log_ops, TRUE, ref_head), + VIEW_(TREE, "tree", &tree_ops, TRUE, ref_commit), + VIEW_(BLOB, "blob", &blob_ops, TRUE, ref_blob), + VIEW_(BLAME, "blame", &blame_ops, TRUE, ref_commit), + VIEW_(BRANCH, "branch", &branch_ops, TRUE, ref_head), + VIEW_(HELP, "help", &help_ops, FALSE, ""), + VIEW_(PAGER, "pager", &pager_ops, FALSE, ""), + VIEW_(STATUS, "status", &status_ops, TRUE, ""), + VIEW_(STAGE, "stage", &stage_ops, TRUE, ""), }; #define VIEW(req) (&views[(req) - REQ_OFFSET - 1]) @@ -2317,8 +2352,18 @@ static struct view views[] = { #define view_is_displayed(view) \ (view == display[0] || view == display[1]) -#define view_has_parent(view, child_type, parent_type) \ - (view->type == child_type && view->parent && view->parent->type == parent_type) +static enum request +view_request(struct view *view, enum request request) +{ + if (!view || !view->lines) + return request; + return view->ops->request(view, request, &view->line[view->lineno]); +} + + +/* + * View drawing. + */ static inline void set_view_attr(struct view *view, enum line_type type) @@ -2364,11 +2409,12 @@ draw_chars(struct view *view, enum line_type type, const char *string, } waddnstr(view->win, string, len); - } - if (trimmed && use_tilde) { - set_view_attr(view, LINE_DELIMITER); - waddch(view->win, '~'); - col++; + + if (trimmed && use_tilde) { + set_view_attr(view, LINE_DELIMITER); + waddch(view->win, '~'); + col++; + } } return col; @@ -2395,7 +2441,15 @@ draw_space(struct view *view, enum line_type type, int max, int spaces) static bool draw_text(struct view *view, enum line_type type, const char *string, bool trim) { - view->col += draw_chars(view, type, string, view->width + view->yoffset - view->col, trim); + char text[SIZEOF_STR]; + + do { + size_t pos = string_expand(text, sizeof(text), string, opt_tab_size); + + view->col += draw_chars(view, type, text, view->width + view->yoffset - view->col, trim); + string += pos; + } while (*string && view->width + view->yoffset > view->col); + return view->width + view->yoffset <= view->col; } @@ -2709,49 +2763,61 @@ redraw_display(bool clear) } } -static void -toggle_enum_option_do(unsigned int *opt, const char *help, - const struct enum_map *map, size_t size) -{ - *opt = (*opt + 1) % size; - redraw_display(FALSE); - report("Displaying %s %s", enum_name(map[*opt]), help); -} -#define toggle_enum_option(opt, help, map) \ - toggle_enum_option_do(opt, help, map, ARRAY_SIZE(map)) +/* + * Option management + */ -#define toggle_date() toggle_enum_option(&opt_date, "dates", date_map) -#define toggle_author() toggle_enum_option(&opt_author, "author names", author_map) +#define TOGGLE_MENU \ + TOGGLE_(LINENO, '.', "line numbers", &opt_line_number, NULL) \ + TOGGLE_(DATE, 'D', "dates", &opt_date, date_map) \ + TOGGLE_(AUTHOR, 'A', "author names", &opt_author, author_map) \ + TOGGLE_(REV_GRAPH, 'g', "revision graph", &opt_rev_graph, NULL) \ + TOGGLE_(REFS, 'F', "reference display", &opt_show_refs, NULL) static void -toggle_view_option(bool *option, const char *help) -{ - *option = !*option; - redraw_display(FALSE); - report("%sabling %s", *option ? "En" : "Dis", help); -} - -static void -open_option_menu(void) -{ +toggle_option(enum request request) +{ + const struct { + enum request request; + const struct enum_map *map; + size_t map_size; + } data[] = { +#define TOGGLE_(id, key, help, value, map) { REQ_TOGGLE_ ## id, map, ARRAY_SIZE(map) }, + TOGGLE_MENU +#undef TOGGLE_ + }; const struct menu_item menu[] = { - { '.', "line numbers", &opt_line_number }, - { 'D', "date display", &opt_date }, - { 'A', "author display", &opt_author }, - { 'g', "revision graph display", &opt_rev_graph }, - { 'F', "reference display", &opt_show_refs }, +#define TOGGLE_(id, key, help, value, map) { key, help, value }, + TOGGLE_MENU +#undef TOGGLE_ { 0 } }; - int selected = 0; + int i = 0; - if (prompt_menu("Toggle option", menu, &selected)) { - if (menu[selected].data == &opt_date) - toggle_date(); - else if (menu[selected].data == &opt_author) - toggle_author(); - else - toggle_view_option(menu[selected].data, menu[selected].text); + if (request == REQ_OPTIONS) { + if (!prompt_menu("Toggle option", menu, &i)) + return; + } else { + while (i < ARRAY_SIZE(data) && data[i].request != request) + i++; + if (i >= ARRAY_SIZE(data)) + die("Invalid request (%d)", request); + } + + if (data[i].map != NULL) { + unsigned int *opt = menu[i].data; + + *opt = (*opt + 1) % data[i].map_size; + redraw_display(FALSE); + report("Displaying %s %s", enum_name(data[i].map[*opt]), menu[i].text); + + } else { + bool *option = menu[i].data; + + *option = !*option; + redraw_display(FALSE); + report("%sabling %s", *option ? "En" : "Dis", menu[i].text); } } @@ -2851,6 +2917,11 @@ scroll_view(struct view *view, enum request request) assert(view_is_displayed(view)); switch (request) { + case REQ_SCROLL_FIRST_COL: + view->yoffset = 0; + redraw_view_from(view, 0); + report(""); + return; case REQ_SCROLL_LEFT: if (view->yoffset == 0) { report("Cannot scroll beyond the first column"); @@ -3132,15 +3203,6 @@ reset_view(struct view *view) view->update_secs = 0; } -static void -free_argv(const char *argv[]) -{ - int argc; - - for (argc = 0; argv[argc]; argc++) - free((void *) argv[argc]); -} - static const char * format_arg(const char *name) { @@ -3171,24 +3233,40 @@ format_arg(const char *name) } static bool -format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags flags) +format_argv(const char ***dst_argv, const char *src_argv[], bool replace, bool first) { char buf[SIZEOF_STR]; int argc; - bool noreplace = flags == FORMAT_NONE; - free_argv(dst_argv); + argv_free(*dst_argv); for (argc = 0; src_argv[argc]; argc++) { const char *arg = src_argv[argc]; size_t bufpos = 0; + if (!strcmp(arg, "%(fileargs)")) { + if (!argv_append_array(dst_argv, opt_file_argv)) + break; + continue; + + } else if (!strcmp(arg, "%(diffargs)")) { + if (!argv_append_array(dst_argv, opt_diff_argv)) + break; + continue; + + } else if (!strcmp(arg, "%(revargs)") || + (first && !strcmp(arg, "%(commit)"))) { + if (!argv_append_array(dst_argv, opt_rev_argv)) + break; + continue; + } + while (arg) { char *next = strstr(arg, "%("); int len = next - arg; const char *value; - if (!next || noreplace) { + if (!next || !replace) { len = strlen(arg); value = ""; @@ -3203,16 +3281,13 @@ format_argv(const char *dst_argv[], const char *src_argv[], enum format_flags fl if (!string_format_from(buf, &bufpos, "%.*s%s", len, arg, value)) return FALSE; - arg = next && !noreplace ? strchr(next, ')') + 1 : NULL; + arg = next && replace ? strchr(next, ')') + 1 : NULL; } - dst_argv[argc] = strdup(buf); - if (!dst_argv[argc]) + if (!argv_append(dst_argv, buf)) break; } - dst_argv[argc] = NULL; - return src_argv[argc] == NULL; } @@ -3262,12 +3337,28 @@ setup_update(struct view *view, const char *vid) view->start_time = time(NULL); } +static bool +prepare_io(struct view *view, const char *dir, const char *argv[], bool replace) +{ + view->dir = dir; + return format_argv(&view->argv, argv, replace, !view->prev); +} + static bool prepare_update(struct view *view, const char *argv[], const char *dir) { if (view->pipe) end_update(view, TRUE); - return io_format(&view->io, dir, IO_RD, argv, FORMAT_NONE); + return prepare_io(view, dir, argv, FALSE); +} + +static bool +start_update(struct view *view, const char **argv, const char *dir) +{ + if (view->pipe) + io_done(view->pipe); + return prepare_io(view, dir, argv, FALSE) && + io_run(&view->io, IO_RD, dir, view->argv); } static bool @@ -3275,6 +3366,7 @@ prepare_update_file(struct view *view, const char *name) { if (view->pipe) end_update(view, TRUE); + argv_free(view->argv); return io_open(&view->io, "%s/%s", opt_cdup[0] ? opt_cdup : ".", name); } @@ -3288,7 +3380,7 @@ begin_update(struct view *view, bool refresh) if (view->ops->prepare) { if (!view->ops->prepare(view)) return FALSE; - } else if (!io_format(&view->io, NULL, IO_RD, view->ops->argv, FORMAT_ALL)) { + } else if (!prepare_io(view, NULL, view->ops->argv, TRUE)) { return FALSE; } @@ -3299,7 +3391,8 @@ begin_update(struct view *view, bool refresh) string_copy_rev(view->ref, view->id); } - if (!io_start(&view->io)) + if (view->argv && view->argv[0] && + !io_run(&view->io, IO_RD, view->dir, view->argv)) return FALSE; setup_update(view, view->id); @@ -3376,7 +3469,8 @@ update_view(struct view *view) end_update(view, TRUE); } else if (io_eof(view->pipe)) { - report(""); + if (view_is_displayed(view)) + report(""); end_update(view, FALSE); } @@ -3473,6 +3567,7 @@ open_view(struct view *prev, enum request request, enum open_flags flags) if (split) { display[1] = view; current_view = 1; + view->parent = prev; } else if (!nomaximize) { /* Maximize the current view. */ memset(display, 0, sizeof(display)); @@ -3480,9 +3575,9 @@ open_view(struct view *prev, enum request request, enum open_flags flags) display[current_view] = view; } - /* No parent signals that this is the first loaded view. */ + /* No prev signals that this is the first loaded view. */ if (prev && view != prev) { - view->parent = prev; + view->prev = prev; } /* Resize the view when switching between split- and full-screen, @@ -3576,16 +3671,18 @@ static void open_run_request(enum request request) { struct run_request *req = get_run_request(request); - const char *argv[ARRAY_SIZE(req->argv)] = { NULL }; + const char **argv = NULL; if (!req) { report("Unknown run request"); return; } - if (format_argv(argv, req->argv, FORMAT_ALL)) + if (format_argv(&argv, req->argv, TRUE, FALSE)) open_external_viewer(argv, NULL); - free_argv(argv); + if (argv) + argv_free(argv); + free(argv); } /* @@ -3602,18 +3699,13 @@ view_driver(struct view *view, enum request request) if (request > REQ_NONE) { open_run_request(request); - /* FIXME: When all views can refresh always do this. */ - if (view->refresh) - request = REQ_REFRESH; - else - return TRUE; + view_request(view, REQ_REFRESH); + return TRUE; } - if (view && view->lines) { - request = view->ops->request(view, request, &view->line[view->lineno]); - if (request == REQ_NONE) - return TRUE; - } + request = view_request(view, request); + if (request == REQ_NONE) + return TRUE; switch (request) { case REQ_MOVE_UP: @@ -3625,6 +3717,7 @@ view_driver(struct view *view, enum request request) move_view(view, request); break; + case REQ_SCROLL_FIRST_COL: case REQ_SCROLL_LEFT: case REQ_SCROLL_RIGHT: case REQ_SCROLL_LINE_DOWN: @@ -3653,6 +3746,13 @@ view_driver(struct view *view, enum request request) break; case REQ_VIEW_PAGER: + if (view == NULL) { + if (!io_open(&VIEW(REQ_VIEW_PAGER)->io, "")) + die("Failed to open stdin"); + open_view(view, request, OPEN_PREPARED); + break; + } + if (!VIEW(REQ_VIEW_PAGER)->pipe && !VIEW(REQ_VIEW_PAGER)->lines) { report("No pager content, press %s to run command from prompt", get_key(view->keymap, REQ_PROMPT)); @@ -3691,11 +3791,7 @@ view_driver(struct view *view, enum request request) case REQ_PREVIOUS: request = request == REQ_NEXT ? REQ_MOVE_DOWN : REQ_MOVE_UP; - if (view_has_parent(view, VIEW_DIFF, VIEW_MAIN) || - view_has_parent(view, VIEW_DIFF, VIEW_BLAME) || - view_has_parent(view, VIEW_STAGE, VIEW_STATUS) || - view_has_parent(view, VIEW_BLOB, VIEW_TREE) || - view_has_parent(view, VIEW_MAIN, VIEW_BRANCH)) { + if (view->parent) { int line; view = view->parent; @@ -3704,9 +3800,7 @@ view_driver(struct view *view, enum request request) if (view_is_displayed(view)) update_view_title(view); if (line != view->lineno) - view->ops->request(view, REQ_ENTER, - &view->line[view->lineno]); - + view_request(view, REQ_ENTER); } else { move_view(view, request); } @@ -3738,27 +3832,12 @@ view_driver(struct view *view, enum request request) break; case REQ_OPTIONS: - open_option_menu(); - break; - case REQ_TOGGLE_LINENO: - toggle_view_option(&opt_line_number, "line numbers"); - break; - case REQ_TOGGLE_DATE: - toggle_date(); - break; - case REQ_TOGGLE_AUTHOR: - toggle_author(); - break; - case REQ_TOGGLE_REV_GRAPH: - toggle_view_option(&opt_rev_graph, "revision graph display"); - break; - case REQ_TOGGLE_REFS: - toggle_view_option(&opt_show_refs, "reference display"); + toggle_option(request); break; case REQ_TOGGLE_SORT_FIELD: @@ -3801,13 +3880,12 @@ view_driver(struct view *view, enum request request) break; case REQ_VIEW_CLOSE: - /* XXX: Mark closed views by letting view->parent point to the + /* XXX: Mark closed views by letting view->prev point to the * view itself. Parents to closed view should never be * followed. */ - if (view->parent && - view->parent->parent != view->parent) { - maximize_view(view->parent); - view->parent = view; + if (view->prev && view->prev != view) { + maximize_view(view->prev); + view->prev = view; break; } /* Fall-through */ @@ -3958,71 +4036,6 @@ parse_author_line(char *ident, const char **author, struct time *time) } } -static bool -open_commit_parent_menu(char buf[SIZEOF_STR], int *parents) -{ - char rev[SIZEOF_REV]; - const char *revlist_argv[] = { - "git", "log", "--no-color", "-1", "--pretty=format:%s", rev, NULL - }; - struct menu_item *items; - char text[SIZEOF_STR]; - bool ok = TRUE; - int i; - - items = calloc(*parents + 1, sizeof(*items)); - if (!items) - return FALSE; - - for (i = 0; i < *parents; i++) { - string_copy_rev(rev, &buf[SIZEOF_REV * i]); - if (!io_run_buf(revlist_argv, text, sizeof(text)) || - !(items[i].text = strdup(text))) { - ok = FALSE; - break; - } - } - - if (ok) { - *parents = 0; - ok = prompt_menu("Select parent", items, parents); - } - for (i = 0; items[i].text; i++) - free((char *) items[i].text); - free(items); - return ok; -} - -static bool -select_commit_parent(const char *id, char rev[SIZEOF_REV], const char *path) -{ - char buf[SIZEOF_STR * 4]; - const char *revlist_argv[] = { - "git", "log", "--no-color", "-1", - "--pretty=format:%P", id, "--", path, NULL - }; - int parents; - - if (!io_run_buf(revlist_argv, buf, sizeof(buf)) || - (parents = strlen(buf) / 40) < 0) { - report("Failed to get parent information"); - return FALSE; - - } else if (parents == 0) { - if (path) - report("Path '%s' does not exist in the parent", path); - else - report("The selected commit has no parents"); - return FALSE; - } - - if (parents > 1 && !open_commit_parent_menu(buf, &parents)) - return FALSE; - - string_copy_rev(rev, &buf[41 * parents]); - return TRUE; -} - /* * Pager backend */ @@ -4030,13 +4043,10 @@ select_commit_parent(const char *id, char rev[SIZEOF_REV], const char *path) static bool pager_draw(struct view *view, struct line *line, unsigned int lineno) { - char text[SIZEOF_STR]; - if (opt_line_number && draw_lineno(view, lineno)) return TRUE; - string_expand(text, sizeof(text), line->data, opt_tab_size); - draw_text(view, line->type, text, TRUE); + draw_text(view, line->type, line->data, TRUE); return TRUE; } @@ -4210,14 +4220,42 @@ static struct view_ops log_ops = { static const char *diff_argv[SIZEOF_ARG] = { "git", "show", "--pretty=fuller", "--no-color", "--root", - "--patch-with-stat", "--find-copies-harder", "-C", "%(commit)", NULL + "--patch-with-stat", "--find-copies-harder", "-C", + "%(diffargs)", "%(commit)", "--", "%(fileargs)", NULL }; +static bool +diff_read(struct view *view, char *data) +{ + if (!data) { + /* Fall back to retry if no diff will be shown. */ + if (view->lines == 0 && opt_file_argv) { + int pos = argv_size(view->argv) + - argv_size(opt_file_argv) - 1; + + if (pos > 0 && !strcmp(view->argv[pos], "--")) { + for (; view->argv[pos]; pos++) { + free((void *) view->argv[pos]); + view->argv[pos] = NULL; + } + + if (view->pipe) + io_done(view->pipe); + if (io_run(&view->io, IO_RD, view->dir, view->argv)) + return FALSE; + } + } + return TRUE; + } + + return pager_read(view, data); +} + static struct view_ops diff_ops = { "line", diff_argv, NULL, - pager_read, + diff_read, pager_draw, pager_request, pager_grep, @@ -4510,7 +4548,6 @@ tree_read_date(struct view *view, char *text, bool *read_date) "git", "log", "--no-color", "--pretty=raw", "--cc", "--raw", view->id, "--", path, NULL }; - struct io io = {}; if (!view->lines) { tree_entry(view, LINE_TREE_HEAD, opt_path, NULL, NULL); @@ -4518,13 +4555,11 @@ tree_read_date(struct view *view, char *text, bool *read_date) return TRUE; } - if (!io_run_rd(&io, log_file, opt_cdup, FORMAT_NONE)) { + if (!start_update(view, log_file, opt_cdup)) { report("Failed to load tree data"); return TRUE; } - io_done(view->pipe); - view->io = io; *read_date = TRUE; return FALSE; @@ -4653,14 +4688,15 @@ tree_draw(struct view *view, struct line *line, unsigned int lineno) } static void -open_blob_editor() +open_blob_editor(const char *id) { + const char *blob_argv[] = { "git", "cat-file", "blob", id, NULL }; char file[SIZEOF_STR] = "/tmp/tigblob.XXXXXX"; int fd = mkstemp(file); if (fd == -1) report("Failed to create temporary file"); - else if (!io_run_append(blob_ops.argv, FORMAT_ALL, fd)) + else if (!io_run_append(blob_argv, fd)) report("Failed to save blob data to file"); else open_editor(file); @@ -4672,6 +4708,7 @@ static enum request tree_request(struct view *view, enum request request, struct line *line) { enum open_flags flags; + struct tree_entry *entry = line->data; switch (request) { case REQ_VIEW_BLAME: @@ -4687,7 +4724,7 @@ tree_request(struct view *view, enum request request, struct line *line) if (line->type != LINE_TREE_FILE) { report("Edit only supported for files"); } else if (!is_head_commit(view->vid)) { - open_blob_editor(); + open_blob_editor(entry->id); } else { open_editor(opt_file); } @@ -4738,7 +4775,7 @@ tree_request(struct view *view, enum request request, struct line *line) break; case LINE_TREE_FILE: - flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT; + flags = view_is_displayed(view) ? OPEN_SPLIT : OPEN_DEFAULT; request = REQ_VIEW_BLOB; break; @@ -4806,7 +4843,7 @@ tree_prepare(struct view *view) opt_path[0] = 0; } - return io_format(&view->io, opt_cdup, IO_RD, view->ops->argv, FORMAT_ALL); + return prepare_io(view, opt_cdup, view->ops->argv, TRUE); } static const char *tree_argv[SIZEOF_ARG] = { @@ -4838,7 +4875,7 @@ blob_request(struct view *view, enum request request, struct line *line) { switch (request) { case REQ_EDIT: - open_blob_editor(); + open_blob_editor(view->vid); return REQ_NONE; default: return pager_request(view, request, line); @@ -4871,25 +4908,14 @@ static struct view_ops blob_ops = { * reading output from git-blame. */ -static const char *blame_head_argv[] = { - "git", "blame", "--incremental", "--", "%(file)", NULL -}; - -static const char *blame_ref_argv[] = { - "git", "blame", "--incremental", "%(ref)", "--", "%(file)", NULL -}; - -static const char *blame_cat_file_argv[] = { - "git", "cat-file", "blob", "%(ref):%(file)", NULL -}; - struct blame_commit { char id[SIZEOF_REV]; /* SHA1 ID. */ char title[128]; /* First line of the commit message. */ const char *author; /* Author of the commit. */ struct time time; /* Date from the author ident. */ char filename[128]; /* Name of file. */ - bool has_previous; /* Was a "previous" line detected. */ + char parent_id[SIZEOF_REV]; /* Parent/previous SHA1 ID. */ + char parent_filename[128]; /* Parent/previous name of file. */ }; struct blame { @@ -4902,18 +4928,42 @@ static bool blame_open(struct view *view) { char path[SIZEOF_STR]; + size_t i; - if (!view->parent && *opt_prefix) { + if (!view->prev && *opt_prefix) { string_copy(path, opt_file); if (!string_format(opt_file, "%s%s", opt_prefix, path)) return FALSE; } if (*opt_ref || !io_open(&view->io, "%s%s", opt_cdup, opt_file)) { - if (!io_run_rd(&view->io, blame_cat_file_argv, opt_cdup, FORMAT_ALL)) + const char *blame_cat_file_argv[] = { + "git", "cat-file", "blob", path, NULL + }; + + if (!string_format(path, "%s:%s", opt_ref, opt_file) || + !start_update(view, blame_cat_file_argv, opt_cdup)) return FALSE; } + /* First pass: remove multiple references to the same commit. */ + for (i = 0; i < view->lines; i++) { + struct blame *blame = view->line[i].data; + + if (blame->commit && blame->commit->id[0]) + blame->commit->id[0] = 0; + else + blame->commit = NULL; + } + + /* Second pass: free existing references. */ + for (i = 0; i < view->lines; i++) { + struct blame *blame = view->line[i].data; + + if (blame->commit) + free(blame->commit); + } + setup_update(view, opt_file); string_format(view->ref, "%s ...", opt_file); @@ -5000,19 +5050,19 @@ static bool blame_read_file(struct view *view, const char *line, bool *read_file) { if (!line) { - const char **argv = *opt_ref ? blame_ref_argv : blame_head_argv; - struct io io = {}; + const char *blame_argv[] = { + "git", "blame", "--incremental", + *opt_ref ? opt_ref : "--incremental", "--", opt_file, NULL + }; - if (view->lines == 0 && !view->parent) + if (view->lines == 0 && !view->prev) die("No blame exist for %s", view->vid); - if (view->lines == 0 || !io_run_rd(&io, argv, opt_cdup, FORMAT_ALL)) { + if (view->lines == 0 || !start_update(view, blame_argv, opt_cdup)) { report("Failed to load blame data"); return TRUE; } - io_done(view->pipe); - view->io = io; *read_file = FALSE; return FALSE; @@ -5083,7 +5133,11 @@ blame_read(struct view *view, char *line) string_ncopy(commit->title, line, strlen(line)); } else if (match_blame_header("previous ", &line)) { - commit->has_previous = TRUE; + if (strlen(line) <= SIZEOF_REV) + return FALSE; + string_copy_rev(commit->parent_id, line); + line += SIZEOF_REV; + string_ncopy(commit->parent_filename, line, strlen(line)); } else if (match_blame_header("filename ", &line)) { string_ncopy(commit->filename, line, strlen(line)); @@ -5099,7 +5153,6 @@ blame_draw(struct view *view, struct line *line, unsigned int lineno) struct blame *blame = line->data; struct time *time = NULL; const char *id = NULL, *author = NULL; - char text[SIZEOF_STR]; if (blame->commit && *blame->commit->filename) { id = blame->commit->id; @@ -5119,8 +5172,7 @@ blame_draw(struct view *view, struct line *line, unsigned int lineno) if (draw_lineno(view, lineno)) return TRUE; - string_expand(text, sizeof(text), blame->text, opt_tab_size); - draw_text(view, LINE_DEFAULT, text, TRUE); + draw_text(view, LINE_DEFAULT, blame->text, TRUE); return TRUE; } @@ -5139,16 +5191,20 @@ check_blame_commit(struct blame *blame, bool check_null_id) static void setup_blame_parent_line(struct view *view, struct blame *blame) { + char from[SIZEOF_REF + SIZEOF_STR]; + char to[SIZEOF_REF + SIZEOF_STR]; const char *diff_tree_argv[] = { - "git", "diff-tree", "-U0", blame->commit->id, - "--", blame->commit->filename, NULL + "git", "diff", "--no-textconv", "--no-extdiff", "--no-color", + "-U0", from, to, "--", NULL }; - struct io io = {}; + struct io io; int parent_lineno = -1; int blamed_lineno = -1; char *line; - if (!io_run(&io, diff_tree_argv, NULL, IO_RD)) + if (!string_format(from, "%s:%s", opt_ref, opt_file) || + !string_format(to, "%s:%s", blame->commit->id, blame->commit->filename) || + !io_run(&io, IO_RD, NULL, diff_tree_argv)) return; while ((line = io_get(&io, '\n', TRUE))) { @@ -5175,7 +5231,7 @@ setup_blame_parent_line(struct view *view, struct blame *blame) static enum request blame_request(struct view *view, enum request request, struct line *line) { - enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT; + enum open_flags flags = view_is_displayed(view) ? OPEN_SPLIT : OPEN_DEFAULT; struct blame *blame = line->data; switch (request) { @@ -5190,10 +5246,13 @@ blame_request(struct view *view, enum request request, struct line *line) break; case REQ_PARENT: - if (check_blame_commit(blame, TRUE) && - select_commit_parent(blame->commit->id, opt_ref, - blame->commit->filename)) { - string_copy(opt_file, blame->commit->filename); + if (!check_blame_commit(blame, TRUE)) + break; + if (!*blame->commit->parent_id) { + report("The selected commit has no parents"); + } else { + string_copy_rev(opt_ref, blame->commit->parent_id); + string_copy(opt_file, blame->commit->parent_filename); setup_blame_parent_line(view, blame); open_view(view, REQ_VIEW_BLAME, OPEN_REFRESH); } @@ -5214,7 +5273,7 @@ blame_request(struct view *view, enum request request, struct line *line) "-C", "-M", "HEAD", "--", view->vid, NULL }; - if (!blame->commit->has_previous) { + if (!*blame->commit->parent_id) { diff_index_argv[1] = "diff"; diff_index_argv[2] = "--no-color"; diff_index_argv[6] = "--"; @@ -5352,23 +5411,21 @@ branch_request(struct view *view, enum request request, struct line *line) return REQ_NONE; case REQ_ENTER: - if (branch->ref == &branch_all) { - const char *all_branches_argv[] = { - "git", "log", "--no-color", "--pretty=raw", "--parents", - "--topo-order", "--all", NULL - }; - struct view *main_view = VIEW(REQ_VIEW_MAIN); + { + const struct ref *ref = branch->ref; + const char *all_branches_argv[] = { + "git", "log", "--no-color", "--pretty=raw", "--parents", + "--topo-order", + ref == &branch_all ? "--all" : ref->name, NULL + }; + struct view *main_view = VIEW(REQ_VIEW_MAIN); - if (!prepare_update(main_view, all_branches_argv, NULL)) { - report("Failed to load view of all branches"); - return REQ_NONE; - } + if (!prepare_update(main_view, all_branches_argv, NULL)) + report("Failed to load view of all branches"); + else open_view(view, REQ_VIEW_MAIN, OPEN_PREPARED | OPEN_SPLIT); - } else { - open_view(view, REQ_VIEW_MAIN, OPEN_SPLIT); - } return REQ_NONE; - + } default: return request; } @@ -5440,7 +5497,7 @@ branch_open(struct view *view) "--simplify-by-decoration", "--all", NULL }; - if (!io_run_rd(&view->io, branch_log, NULL, FORMAT_NONE)) { + if (!start_update(view, branch_log, NULL)) { report("Failed to load branch data"); return TRUE; } @@ -5559,9 +5616,9 @@ status_run(struct view *view, const char *argv[], char status, enum line_type ty { struct status *unmerged = NULL; char *buf; - struct io io = {}; + struct io io; - if (!io_run(&io, argv, opt_cdup, IO_RD)) + if (!io_run(&io, IO_RD, opt_cdup, argv)) return FALSE; add_line_data(view, NULL, type); @@ -5642,7 +5699,7 @@ static const char *status_diff_files_argv[] = { }; static const char *status_list_other_argv[] = { - "git", "ls-files", "-z", "--others", "--exclude-standard", opt_prefix, NULL + "git", "ls-files", "-z", "--others", "--exclude-standard", opt_prefix, NULL, NULL, }; static const char *status_list_no_head_argv[] = { @@ -5709,7 +5766,7 @@ status_update_onbranch(void) continue; if (!*opt_head) { - struct io io = {}; + struct io io; if (io_open(&io, "%s/rebase-merge/head-name", opt_git_dir) && io_read_buf(&io, buf, sizeof(buf))) { @@ -5747,6 +5804,9 @@ status_open(struct view *view) return FALSE; } + if (!opt_untracked_dirs_content) + status_list_other_argv[ARRAY_SIZE(status_list_other_argv) - 2] = "--directory"; + if (!status_run(view, status_diff_files_argv, 0, LINE_STAT_UNSTAGED) || !status_run(view, status_list_other_argv, '?', LINE_STAT_UNTRACKED)) return FALSE; @@ -5901,7 +5961,7 @@ status_enter(struct view *view, struct line *line) die("line type %d not handled in switch", line->type); } - split = view_is_displayed(view) ? OPEN_SPLIT : 0; + split = view_is_displayed(view) ? OPEN_SPLIT : OPEN_DEFAULT; open_view(view, REQ_VIEW_STAGE, OPEN_PREPARED | split); if (view_is_displayed(VIEW(REQ_VIEW_STAGE))) { if (status) { @@ -5956,11 +6016,11 @@ status_update_prepare(struct io *io, enum line_type type) switch (type) { case LINE_STAT_STAGED: - return io_run(io, staged_argv, opt_cdup, IO_WR); + return io_run(io, IO_WR, opt_cdup, staged_argv); case LINE_STAT_UNSTAGED: case LINE_STAT_UNTRACKED: - return io_run(io, others_argv, opt_cdup, IO_WR); + return io_run(io, IO_WR, opt_cdup, others_argv); default: die("line type %d not handled in switch", type); @@ -5999,7 +6059,7 @@ status_update_write(struct io *io, struct status *status, enum line_type type) static bool status_update_file(struct status *status, enum line_type type) { - struct io io = {}; + struct io io; bool result; if (!status_update_prepare(&io, type)) @@ -6013,7 +6073,7 @@ static bool status_update_files(struct view *view, struct line *line) { char buf[sizeof(view->ref)]; - struct io io = {}; + struct io io; bool result = TRUE; struct line *pos = view->line + view->lines; int files = 0; @@ -6286,7 +6346,7 @@ stage_apply_chunk(struct view *view, struct line *chunk, bool revert) "git", "apply", "--whitespace=nowarn", NULL }; struct line *diff_hdr; - struct io io = {}; + struct io io; int argc = 3; diff_hdr = stage_diff_find(view, chunk, LINE_DIFF_HEADER); @@ -6299,7 +6359,7 @@ stage_apply_chunk(struct view *view, struct line *chunk, bool revert) apply_argv[argc++] = "-R"; apply_argv[argc++] = "-"; apply_argv[argc++] = NULL; - if (!io_run(&io, apply_argv, opt_cdup, IO_WR)) + if (!io_run(&io, IO_WR, opt_cdup, apply_argv)) return FALSE; if (!stage_diff_write(&io, diff_hdr, chunk) || @@ -6715,7 +6775,8 @@ update_rev_graph(struct view *view, struct rev_graph *graph) static const char *main_argv[SIZEOF_ARG] = { "git", "log", "--no-color", "--pretty=raw", "--parents", - "--topo-order", "%(head)", NULL + "--topo-order", "%(diffargs)", "%(revargs)", + "--", "%(fileargs)", NULL }; static bool @@ -6781,7 +6842,7 @@ main_read(struct view *view, char *line) if (!line) { int i; - if (!view->lines && !view->parent) + if (!view->lines && !view->prev) die("No revisions match the given arguments."); if (view->lines > 0) { commit = view->line[view->lines - 1].data; @@ -6871,10 +6932,12 @@ main_read(struct view *view, char *line) static enum request main_request(struct view *view, enum request request, struct line *line) { - enum open_flags flags = display[0] == view ? OPEN_SPLIT : OPEN_DEFAULT; + enum open_flags flags = view_is_displayed(view) ? OPEN_SPLIT : OPEN_DEFAULT; switch (request) { case REQ_ENTER: + if (view_is_displayed(view) && display[0] != view) + maximize_view(view); open_view(view, REQ_VIEW_DIFF, flags); break; case REQ_REFRESH: @@ -7044,7 +7107,11 @@ init_display(void) keypad(status_win, TRUE); wbkgdset(status_win, get_line_attr(LINE_STATUS)); +#if defined(NCURSES_VERSION_PATCH) && (NCURSES_VERSION_PATCH >= 20080119) + set_tabsize(opt_tab_size); +#else TABSIZE = opt_tab_size; +#endif term = getenv("XTERM_VERSION") ? NULL : getenv("COLORTERM"); if (term && !strcmp(term, "gnome-terminal")) { @@ -7073,12 +7140,13 @@ get_input(int prompt_position) { struct view *view; int i, key, cursor_y, cursor_x; - bool loading = FALSE; if (prompt_position) input_mode = TRUE; while (TRUE) { + bool loading = FALSE; + foreach_view (view, i) { update_view(view); if (view_is_displayed(view) && view->has_scrolled && @@ -7363,7 +7431,7 @@ get_ref_list(const char *id) } static int -read_ref(char *id, size_t idlen, char *name, size_t namelen) +read_ref(char *id, size_t idlen, char *name, size_t namelen, void *data) { struct ref *ref = NULL; bool tag = FALSE; @@ -7481,7 +7549,7 @@ load_refs(void) for (i = 0; i < refs_size; i++) refs[i]->id[0] = 0; - if (io_run_load(ls_remote_argv, "\t", read_ref) == ERR) + if (io_run_load(ls_remote_argv, "\t", read_ref, NULL) == ERR) return ERR; /* Update the ref lists to reflect changes. */ @@ -7516,19 +7584,19 @@ set_remote_branch(const char *name, const char *value, size_t valuelen) } static void -set_repo_config_option(char *name, char *value, int (*cmd)(int, const char **)) +set_repo_config_option(char *name, char *value, enum option_code (*cmd)(int, const char **)) { const char *argv[SIZEOF_ARG] = { name, "=" }; int argc = 1 + (cmd == option_set_command); - int error = ERR; + enum option_code error; if (!argv_from_string(argv, &argc, value)) - config_msg = "Too many option arguments"; + error = OPT_ERR_TOO_MANY_OPTION_ARGUMENTS; else error = cmd(argc, argv); - if (error == ERR) - warn("Option 'tig.%s': %s", name, config_msg); + if (error != OPT_OK) + warn("Option 'tig.%s': %s", name, option_errors[error]); } static bool @@ -7570,7 +7638,7 @@ set_work_tree(const char *value) } static int -read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen) +read_repo_config_option(char *name, size_t namelen, char *value, size_t valuelen, void *data) { if (!strcmp(name, "i18n.commitencoding")) string_ncopy(opt_encoding, value, valuelen); @@ -7602,11 +7670,11 @@ load_git_config(void) { const char *config_list_argv[] = { "git", "config", "--list", NULL }; - return io_run_load(config_list_argv, "=", read_repo_config_option); + return io_run_load(config_list_argv, "=", read_repo_config_option, NULL); } static int -read_repo_info(char *name, size_t namelen, char *value, size_t valuelen) +read_repo_info(char *name, size_t namelen, char *value, size_t valuelen, void *data) { if (!opt_git_dir[0]) { string_ncopy(opt_git_dir, name, namelen); @@ -7637,7 +7705,7 @@ load_repo_info(void) "--show-cdup", "--show-prefix", NULL }; - return io_run_load(rev_parse_argv, "=", read_repo_info); + return io_run_load(rev_parse_argv, "=", read_repo_info, NULL); } @@ -7695,27 +7763,50 @@ warn(const char *msg, ...) va_end(args); } +static int +read_filter_args(char *name, size_t namelen, char *value, size_t valuelen, void *data) +{ + const char ***filter_args = data; + + return argv_append(filter_args, name) ? OK : ERR; +} + +static void +filter_rev_parse(const char ***args, const char *arg1, const char *arg2, const char *argv[]) +{ + const char *rev_parse_argv[SIZEOF_ARG] = { "git", "rev-parse", arg1, arg2 }; + const char **all_argv = NULL; + + if (!argv_append_array(&all_argv, rev_parse_argv) || + !argv_append_array(&all_argv, argv) || + !io_run_load(all_argv, "\n", read_filter_args, args) == ERR) + die("Failed to split arguments"); + argv_free(all_argv); + free(all_argv); +} + +static void +filter_options(const char *argv[]) +{ + filter_rev_parse(&opt_file_argv, "--no-revs", "--no-flags", argv); + filter_rev_parse(&opt_diff_argv, "--no-revs", "--flags", argv); + filter_rev_parse(&opt_rev_argv, "--symbolic", "--revs-only", argv); +} + static enum request parse_options(int argc, const char *argv[]) { enum request request = REQ_VIEW_MAIN; const char *subcommand; bool seen_dashdash = FALSE; - /* XXX: This is vulnerable to the user overriding options - * required for the main view parser. */ - const char *custom_argv[SIZEOF_ARG] = { - "git", "log", "--no-color", "--pretty=raw", "--parents", - "--topo-order", NULL - }; - int i, j = 6; + const char **filter_argv = NULL; + int i; - if (!isatty(STDIN_FILENO)) { - io_open(&VIEW(REQ_VIEW_PAGER)->io, ""); + if (!isatty(STDIN_FILENO)) return REQ_VIEW_PAGER; - } if (argc <= 1) - return REQ_NONE; + return REQ_VIEW_MAIN; subcommand = argv[1]; if (!strcmp(subcommand, "status")) { @@ -7743,16 +7834,16 @@ parse_options(int argc, const char *argv[]) subcommand = NULL; } - if (subcommand) { - custom_argv[1] = subcommand; - j = 2; - } - for (i = 1 + !!subcommand; i < argc; i++) { const char *opt = argv[i]; - if (seen_dashdash || !strcmp(opt, "--")) { + if (seen_dashdash) { + argv_append(&opt_file_argv, opt); + continue; + + } else if (!strcmp(opt, "--")) { seen_dashdash = TRUE; + continue; } else if (!strcmp(opt, "-v") || !strcmp(opt, "--version")) { printf("tig version %s\n", TIG_VERSION); @@ -7761,15 +7852,18 @@ parse_options(int argc, const char *argv[]) } else if (!strcmp(opt, "-h") || !strcmp(opt, "--help")) { printf("%s\n", usage); quit(0); + + } else if (!strcmp(opt, "--all")) { + argv_append(&opt_rev_argv, opt); + continue; } - custom_argv[j++] = opt; - if (j >= ARRAY_SIZE(custom_argv)) + if (!argv_append(&filter_argv, opt)) die("command too long"); } - if (!prepare_update(VIEW(request), custom_argv, NULL)) - die("Failed to format arguments"); + if (filter_argv) + filter_options(filter_argv); return request; } @@ -7817,17 +7911,18 @@ main(int argc, const char *argv[]) if (load_refs() == ERR) die("Failed to load refs."); - foreach_view (view, i) + foreach_view (view, i) { + if (getenv(view->cmd_env)) + warn("Use of the %s environment variable is deprecated," + " use options or TIG_DIFF_ARGS instead", + view->cmd_env); if (!argv_from_env(view->ops->argv, view->cmd_env)) die("Too many arguments in the `%s` environment variable", view->cmd_env); + } init_display(); - if (request != REQ_NONE) - open_view(NULL, request, OPEN_PREPARED); - request = request == REQ_NONE ? REQ_VIEW_MAIN : REQ_NONE; - while (view_driver(display[current_view], request)) { int key = get_input(0);