aboutsummaryrefslogtreecommitdiff
path: root/trailer.c
diff options
context:
space:
mode:
Diffstat (limited to 'trailer.c')
-rw-r--r--trailer.c182
1 files changed, 173 insertions, 9 deletions
diff --git a/trailer.c b/trailer.c
index 911a81ed99..470f86a4a2 100644
--- a/trailer.c
+++ b/trailer.c
@@ -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);
}