X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=tig.c;h=a2fe255bfda8599b94c1d05c4d079cc01043ec35;hb=53c089443cd9885fa677becee4bf7ffd56c3c357;hp=f364930235546fc7c22bcc7e9a596097a94ad67e;hpb=117e270f1ff02b56302627331e8d97645012a2a0;p=tig.git diff --git a/tig.c b/tig.c index f364930..a2fe255 100644 --- a/tig.c +++ b/tig.c @@ -676,18 +676,49 @@ argv_free(const char *argv[]) { int argc; + if (!argv) + return; for (argc = 0; argv[argc]; argc++) free((void *) argv[argc]); argv[0] = NULL; } +DEFINE_ALLOCATOR(argv_realloc, const char *, SIZEOF_ARG) + static bool -argv_copy(const char *dst[], const char *src[]) +argv_append(const char ***argv, const char *arg) +{ + int argc = 0; + + while (*argv && (*argv)[argc]) + argc++; + + 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 (!(dst[argc] = strdup(src[argc]))) + if (!argv_append(dst, src[argc])) return FALSE; return TRUE; } @@ -707,8 +738,8 @@ enum io_type { }; struct io { - 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. */ char *buf; /* Read buffer. */ size_t bufalloc; /* Allocated buffer size. */ @@ -717,21 +748,11 @@ struct io { unsigned int eof:1; /* Has end of file been reached. */ }; -static void -io_reset(struct io *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) { - io_reset(io); + memset(io, 0, sizeof(*io)); + io->pipe = -1; } static bool @@ -771,7 +792,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; @@ -794,15 +815,20 @@ io_done(struct io *io) } static bool -io_start(struct io *io, enum io_type type, const char *dir, const char *argv[]) +io_run(struct io *io, enum io_type type, const char *dir, const char *argv[], ...) { int pipefds[2] = { -1, -1 }; + va_list args; + + io_init(io); if ((type == IO_RD || type == IO_WR) && pipe(pipefds) < 0) { io->error = errno; return FALSE; } else if (type == IO_AP) { - pipefds[1] = io->pipe; + va_start(args, argv); + pipefds[1] = va_arg(args, int); + va_end(args); } if ((io->pid = fork())) { @@ -845,21 +871,12 @@ io_start(struct io *io, enum io_type type, const char *dir, const char *argv[]) return FALSE; } -static bool -io_run(struct io *io, const char **argv, const char *dir, enum io_type type) -{ - io_init(io); - return io_start(io, type, dir, argv); -} - static bool io_complete(enum io_type type, const char **argv, const char *dir, int fd) { - struct io io = {}; + struct io io; - io_init(&io); - io.pipe = fd; - return io_start(&io, type, dir, argv) && io_done(&io); + return io_run(&io, type, dir, argv, fd) && io_done(&io); } static bool @@ -877,7 +894,7 @@ io_run_fg(const char **argv, const char *dir) static bool io_run_append(const char **argv, int fd) { - return io_complete(IO_AP, argv, NULL, -1); + return io_complete(IO_AP, argv, NULL, fd); } static bool @@ -1012,10 +1029,9 @@ 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; - io_init(&io); - return io_start(&io, IO_RD, NULL, argv) && io_read_buf(&io, buf, bufsize); + return io_run(&io, IO_RD, NULL, argv) && io_read_buf(&io, buf, bufsize); } static int @@ -1057,10 +1073,9 @@ static int io_run_load(const char **argv, const char *separators, int (*read_property)(char *, size_t, char *, size_t)) { - struct io io = {}; + struct io io; - io_init(&io); - if (!io_start(&io, IO_RD, NULL, argv)) + if (!io_run(&io, IO_RD, NULL, argv)) return ERR; return io_load(&io, separators, read_property); } @@ -1218,6 +1233,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_args = NULL; +static const char **opt_rev_args = NULL; +static const char **opt_file_args = 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))) @@ -1690,7 +1708,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; @@ -1699,22 +1717,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 (!argv_copy(req->argv, argv)) + if (!argv_copy(&req->argv, argv)) return REQ_NONE; return REQ_NONE + ++run_requests; @@ -1735,16 +1750,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; @@ -1753,7 +1763,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); } @@ -2026,7 +2036,7 @@ option_bind_command(int argc, const char *argv[]) } } if (request == REQ_UNKNOWN && *argv[2]++ == '!') - request = add_run_request(keymap, key, argc - 2, argv + 2); + request = add_run_request(keymap, key, argv + 2); if (request == REQ_UNKNOWN) { config_msg = "Unknown request name"; return ERR; @@ -2104,7 +2114,7 @@ read_option(char *opt, size_t optlen, char *value, size_t valuelen) static void load_option_file(const char *path) { - struct io io = {}; + struct io io; /* It's OK that the file doesn't exist. */ if (!io_open(&io, "%s", path)) @@ -2229,7 +2239,7 @@ struct view { bool has_scrolled; /* View was scrolled. */ /* Loading */ - const char *argv[SIZEOF_ARG]; /* Shell command arguments. */ + const char **argv; /* Shell command arguments. */ const char *dir; /* Directory from which to execute. */ struct io io; struct io *pipe; @@ -3158,17 +3168,33 @@ format_arg(const char *name) } static bool -format_argv(const char *dst_argv[], const char *src_argv[], bool replace) +format_argv(const char ***dst_argv, const char *src_argv[], bool replace) { char buf[SIZEOF_STR]; int argc; - argv_free(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, "%(file-args)")) { + if (!argv_append_array(dst_argv, opt_file_args)) + break; + continue; + + } else if (!strcmp(arg, "%(diff-args)")) { + if (!argv_append_array(dst_argv, opt_diff_args)) + break; + continue; + + } else if (!strcmp(arg, "%(rev-args)")) { + if (!argv_append_array(dst_argv, opt_rev_args)) + break; + continue; + } + while (arg) { char *next = strstr(arg, "%("); int len = next - arg; @@ -3192,13 +3218,10 @@ format_argv(const char *dst_argv[], const char *src_argv[], bool replace) 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; } @@ -3251,9 +3274,8 @@ setup_update(struct view *view, const char *vid) static bool prepare_io(struct view *view, const char *dir, const char *argv[], bool replace) { - io_init(&view->io); view->dir = dir; - return format_argv(view->argv, argv, replace); + return format_argv(&view->argv, argv, replace); } static bool @@ -3270,7 +3292,7 @@ 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_start(&view->io, IO_RD, dir, view->argv); + io_run(&view->io, IO_RD, dir, view->argv); } static bool @@ -3303,7 +3325,8 @@ begin_update(struct view *view, bool refresh) string_copy_rev(view->ref, view->id); } - if (view->argv[0] && !io_start(&view->io, IO_RD, view->dir, view->argv)) + if (view->argv && view->argv[0] && + !io_run(&view->io, IO_RD, view->dir, view->argv)) return FALSE; setup_update(view, view->id); @@ -3582,16 +3605,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, TRUE)) + if (format_argv(&argv, req->argv, TRUE)) open_external_viewer(argv, NULL); - argv_free(argv); + if (argv) + argv_free(argv); + free(argv); } /* @@ -4206,7 +4231,8 @@ 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", + "%(diff-args)", "%(commit)", "--", "%(file-args)", NULL }; static struct view_ops diff_ops = { @@ -5131,12 +5157,12 @@ setup_blame_parent_line(struct view *view, struct blame *blame) "git", "diff-tree", "-U0", blame->commit->id, "--", blame->commit->filename, 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 (!io_run(&io, IO_RD, NULL, diff_tree_argv)) return; while ((line = io_get(&io, '\n', TRUE))) { @@ -5340,23 +5366,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; } @@ -5547,9 +5571,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); @@ -5697,7 +5721,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))) { @@ -5944,11 +5968,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); @@ -5987,7 +6011,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)) @@ -6001,7 +6025,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; @@ -6274,7 +6298,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); @@ -6287,7 +6311,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) || @@ -6703,7 +6727,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", "%(diff-args)", "%(rev-args)", + "--", "%(file-args)", NULL }; static bool @@ -7683,19 +7708,45 @@ warn(const char *msg, ...) va_end(args); } +static const char ***filter_args; + +static int +read_filter_args(char *name, size_t namelen, char *value, size_t valuelen) +{ + 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; + + filter_args = args; + if (!argv_append_array(&all_argv, rev_parse_argv) || + !argv_append_array(&all_argv, argv) || + !io_run_load(all_argv, "\n", read_filter_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_args, "--no-revs", "--no-flags", argv); + filter_rev_parse(&opt_diff_args, "--no-revs", "--flags", argv); + filter_rev_parse(&opt_rev_args, "--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, ""); @@ -7703,7 +7754,7 @@ parse_options(int argc, const char *argv[]) } if (argc <= 1) - return REQ_NONE; + return REQ_VIEW_MAIN; subcommand = argv[1]; if (!strcmp(subcommand, "status")) { @@ -7731,16 +7782,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_args, 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); @@ -7749,15 +7800,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_args, 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; } @@ -7812,10 +7866,6 @@ main(int argc, const char *argv[]) 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);