diff options
Diffstat (limited to 'trailer.c')
| -rw-r--r-- | trailer.c | 182 |
1 files changed, 173 insertions, 9 deletions
@@ -7,8 +7,11 @@ #include "string-list.h" #include "run-command.h" #include "commit.h" +#include "strvec.h" #include "trailer.h" #include "list.h" +#include "tempfile.h" + /* * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org> */ @@ -772,6 +775,35 @@ void parse_trailers_from_command_line_args(struct list_head *arg_head, free(cl_separators); } +int validate_trailer_args(const struct strvec *cli_args) +{ + char *cl_separators; + int ret = 0; + + trailer_config_init(); + + cl_separators = xstrfmt("=%s", separators); + + for (size_t i = 0; i < cli_args->nr; i++) { + const char *txt = cli_args->v[i]; + ssize_t separator_pos; + + if (!*txt) { + ret = error(_("empty --trailer argument")); + goto out; + } + separator_pos = find_separator(txt, cl_separators); + if (separator_pos == 0) { + ret = error(_("invalid trailer '%s': missing key before separator"), + txt); + goto out; + } + } +out: + free(cl_separators); + return ret; +} + static const char *next_line(const char *str) { const char *nl = strchrnul(str, '\n'); @@ -1009,7 +1041,7 @@ static struct trailer_block *trailer_block_get(const struct process_trailer_opti for (ptr = trailer_lines; *ptr; ptr++) { if (last && isspace((*ptr)->buf[0])) { struct strbuf sb = STRBUF_INIT; - strbuf_attach(&sb, *last, strlen(*last), strlen(*last)); + strbuf_attach(&sb, *last, strlen(*last), strlen(*last) + 1); strbuf_addbuf(&sb, *ptr); *last = strbuf_detach(&sb, NULL); continue; @@ -1224,14 +1256,146 @@ void trailer_iterator_release(struct trailer_iterator *iter) strbuf_release(&iter->key); } -int amend_file_with_trailers(const char *path, const struct strvec *trailer_args) +struct tempfile *trailer_create_in_place_tempfile(const char *file) +{ + struct tempfile *tempfile = NULL; + struct stat st; + struct strbuf filename_template = STRBUF_INIT; + const char *tail; + + if (stat(file, &st)) { + error_errno(_("could not stat %s"), file); + return NULL; + } + if (!S_ISREG(st.st_mode)) { + error(_("file %s is not a regular file"), file); + return NULL; + } + if (!(st.st_mode & S_IWUSR)) { + error(_("file %s is not writable by user"), file); + return NULL; + } + /* Create temporary file in the same directory as the original */ + tail = find_last_dir_sep(file); + if (tail) + strbuf_add(&filename_template, file, tail - file + 1); + strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX"); + + tempfile = mks_tempfile_m(filename_template.buf, st.st_mode); + + strbuf_release(&filename_template); + + return tempfile; +} + +int amend_strbuf_with_trailers(struct strbuf *buf, + const struct strvec *trailer_args) { - struct child_process run_trailer = CHILD_PROCESS_INIT; + struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT; + LIST_HEAD(new_trailer_head); + struct strbuf out = STRBUF_INIT; + size_t i; + int ret = 0; + + opts.no_divider = 1; + + for (i = 0; i < trailer_args->nr; i++) { + const char *text = trailer_args->v[i]; + struct new_trailer_item *item; + + if (!*text) { + ret = error(_("empty --trailer argument")); + goto out; + } + item = xcalloc(1, sizeof(*item)); + item->text = xstrdup(text); + list_add_tail(&item->list, &new_trailer_head); + } - run_trailer.git_cmd = 1; - strvec_pushl(&run_trailer.args, "interpret-trailers", - "--in-place", "--no-divider", - path, NULL); - strvec_pushv(&run_trailer.args, trailer_args->v); - return run_command(&run_trailer); + process_trailers(&opts, &new_trailer_head, buf, &out); + + strbuf_swap(buf, &out); +out: + strbuf_release(&out); + free_trailers(&new_trailer_head); + + return ret; +} + +static int write_file_in_place(const char *path, const struct strbuf *buf) +{ + struct tempfile *tempfile = trailer_create_in_place_tempfile(path); + if (!tempfile) + return -1; + + if (write_in_full(tempfile->fd, buf->buf, buf->len) < 0) + return error_errno(_("could not write to temporary file")); + + if (rename_tempfile(&tempfile, path)) + return error_errno(_("could not rename temporary file to %s"), path); + + return 0; +} + +int amend_file_with_trailers(const char *path, + const struct strvec *trailer_args) +{ + struct strbuf buf = STRBUF_INIT; + int ret = 0; + + if (!trailer_args) + BUG("amend_file_with_trailers called with NULL trailer_args"); + if (!trailer_args->nr) + return 0; + + if (validate_trailer_args(trailer_args)) { + ret = -1; + goto out; + } + if (strbuf_read_file(&buf, path, 0) < 0) + ret = error_errno(_("could not read '%s'"), path); + else + amend_strbuf_with_trailers(&buf, trailer_args); + + if (!ret) + ret = write_file_in_place(path, &buf); +out: + strbuf_release(&buf); + return ret; +} + +void process_trailers(const struct process_trailer_options *opts, + struct list_head *new_trailer_head, + struct strbuf *input, struct strbuf *out) +{ + LIST_HEAD(head); + struct trailer_block *trailer_block; + + trailer_block = parse_trailers(opts, input->buf, &head); + + /* Print the lines before the trailer block */ + if (!opts->only_trailers) + strbuf_add(out, input->buf, trailer_block_start(trailer_block)); + + if (!opts->only_trailers && !blank_line_before_trailer_block(trailer_block)) + strbuf_addch(out, '\n'); + + if (!opts->only_input) { + LIST_HEAD(config_head); + LIST_HEAD(arg_head); + parse_trailers_from_config(&config_head); + parse_trailers_from_command_line_args(&arg_head, new_trailer_head); + list_splice(&config_head, &arg_head); + process_trailers_lists(&head, &arg_head); + } + + /* Print trailer block. */ + format_trailers(opts, &head, out); + free_trailers(&head); + + /* Print the lines after the trailer block as is. */ + if (!opts->only_trailers) + strbuf_add(out, input->buf + trailer_block_end(trailer_block), + input->len - trailer_block_end(trailer_block)); + trailer_block_release(trailer_block); } |
