]> git.ipfire.org Git - thirdparty/git.git/blobdiff - trailer.c
Merge branch 'rj/add-i-leak-fix'
[thirdparty/git.git] / trailer.c
index 3a0710a4583a4a11fa5a87cda88ef6a3d0610870..c72ae687099f0d2d21d8dacddf553eabc6ddca8c 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -5,7 +5,6 @@
 #include "string-list.h"
 #include "run-command.h"
 #include "commit.h"
-#include "tempfile.h"
 #include "trailer.h"
 #include "list.h"
 /*
@@ -145,37 +144,6 @@ static char last_non_space_char(const char *s)
        return '\0';
 }
 
-static void print_tok_val(FILE *outfile, const char *tok, const char *val)
-{
-       char c;
-
-       if (!tok) {
-               fprintf(outfile, "%s\n", val);
-               return;
-       }
-
-       c = last_non_space_char(tok);
-       if (!c)
-               return;
-       if (strchr(separators, c))
-               fprintf(outfile, "%s%s\n", tok, val);
-       else
-               fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
-}
-
-static void print_all(FILE *outfile, struct list_head *head,
-                     const struct process_trailer_options *opts)
-{
-       struct list_head *pos;
-       struct trailer_item *item;
-       list_for_each(pos, head) {
-               item = list_entry(pos, struct trailer_item, list);
-               if ((!opts->trim_empty || strlen(item->value) > 0) &&
-                   (!opts->only_trailers || item->token))
-                       print_tok_val(outfile, item->token, item->value);
-       }
-}
-
 static struct trailer_item *trailer_from_arg(struct arg_item *arg_tok)
 {
        struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
@@ -366,8 +334,8 @@ static int find_same_and_apply_arg(struct list_head *head,
        return 0;
 }
 
-static void process_trailers_lists(struct list_head *head,
-                                  struct list_head *arg_head)
+void process_trailers_lists(struct list_head *head,
+                           struct list_head *arg_head)
 {
        struct list_head *pos, *p;
        struct arg_item *arg_tok;
@@ -589,7 +557,7 @@ static int git_trailer_config(const char *conf_key, const char *value,
        return 0;
 }
 
-static void ensure_configured(void)
+void trailer_config_init(void)
 {
        if (configured)
                return;
@@ -719,7 +687,7 @@ static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
        list_add_tail(&new_item->list, arg_head);
 }
 
-static void parse_trailers_from_config(struct list_head *config_head)
+void parse_trailers_from_config(struct list_head *config_head)
 {
        struct arg_item *item;
        struct list_head *pos;
@@ -735,8 +703,8 @@ static void parse_trailers_from_config(struct list_head *config_head)
        }
 }
 
-static void parse_trailers_from_command_line_args(struct list_head *arg_head,
-                                                 struct list_head *new_trailer_head)
+void parse_trailers_from_command_line_args(struct list_head *arg_head,
+                                          struct list_head *new_trailer_head)
 {
        struct strbuf tok = STRBUF_INIT;
        struct strbuf val = STRBUF_INIT;
@@ -775,17 +743,6 @@ static void parse_trailers_from_command_line_args(struct list_head *arg_head,
        free(cl_separators);
 }
 
-static void read_input_file(struct strbuf *sb, const char *file)
-{
-       if (file) {
-               if (strbuf_read_file(sb, file, 0) < 0)
-                       die_errno(_("could not read input file '%s'"), file);
-       } else {
-               if (strbuf_read(sb, fileno(stdin), 0) < 0)
-                       die_errno(_("could not read from stdin"));
-       }
-}
-
 static const char *next_line(const char *str)
 {
        const char *nl = strchrnul(str, '\n');
@@ -845,16 +802,15 @@ static size_t find_end_of_log_message(const char *input, int no_divider)
        /* Assume the naive end of the input is already what we want. */
        end = strlen(input);
 
-       if (no_divider)
-               return end;
-
        /* Optionally skip over any patch part ("---" line and below). */
-       for (s = input; *s; s = next_line(s)) {
-               const char *v;
+       if (!no_divider) {
+               for (s = input; *s; s = next_line(s)) {
+                       const char *v;
 
-               if (skip_prefix(s, "---", &v) && isspace(*v)) {
-                       end = s - input;
-                       break;
+                       if (skip_prefix(s, "---", &v) && isspace(*v)) {
+                               end = s - input;
+                               break;
+                       }
                }
        }
 
@@ -883,7 +839,7 @@ static size_t find_trailer_block_start(const char *buf, size_t len)
 
        /* The first paragraph is the title and cannot be trailers */
        for (s = buf; s < buf + len; s = next_line(s)) {
-               if (s[0] == comment_line_char)
+               if (starts_with_mem(s, buf + len - s, comment_line_str))
                        continue;
                if (is_blank_line(s))
                        break;
@@ -903,7 +859,7 @@ static size_t find_trailer_block_start(const char *buf, size_t len)
                const char **p;
                ssize_t separator_pos;
 
-               if (bol[0] == comment_line_char) {
+               if (starts_with_mem(bol, buf + len - bol, comment_line_str)) {
                        non_trailer_lines += possible_continuation_lines;
                        possible_continuation_lines = 0;
                        continue;
@@ -1000,21 +956,21 @@ static void unfold_value(struct strbuf *val)
  * Parse trailers in "str", populating the trailer info and "head"
  * linked list structure.
  */
-static void parse_trailers(struct trailer_info *info,
-                            const char *str,
-                            struct list_head *head,
-                            const struct process_trailer_options *opts)
+void parse_trailers(const struct process_trailer_options *opts,
+                   struct trailer_info *info,
+                   const char *str,
+                   struct list_head *head)
 {
        struct strbuf tok = STRBUF_INIT;
        struct strbuf val = STRBUF_INIT;
        size_t i;
 
-       trailer_info_get(info, str, opts);
+       trailer_info_get(opts, str, info);
 
        for (i = 0; i < info->trailer_nr; i++) {
                int separator_pos;
                char *trailer = info->trailers[i];
-               if (trailer[0] == comment_line_char)
+               if (starts_with(trailer, comment_line_str))
                        continue;
                separator_pos = find_separator(trailer, separators);
                if (separator_pos >= 1) {
@@ -1035,99 +991,18 @@ static void parse_trailers(struct trailer_info *info,
        }
 }
 
-static void free_all(struct list_head *head)
+void free_trailers(struct list_head *trailers)
 {
        struct list_head *pos, *p;
-       list_for_each_safe(pos, p, head) {
+       list_for_each_safe(pos, p, trailers) {
                list_del(pos);
                free_trailer_item(list_entry(pos, struct trailer_item, list));
        }
 }
 
-static struct tempfile *trailers_tempfile;
-
-static FILE *create_in_place_tempfile(const char *file)
-{
-       struct stat st;
-       struct strbuf filename_template = STRBUF_INIT;
-       const char *tail;
-       FILE *outfile;
-
-       if (stat(file, &st))
-               die_errno(_("could not stat %s"), file);
-       if (!S_ISREG(st.st_mode))
-               die(_("file %s is not a regular file"), file);
-       if (!(st.st_mode & S_IWUSR))
-               die(_("file %s is not writable by user"), file);
-
-       /* Create temporary file in the same directory as the original */
-       tail = strrchr(file, '/');
-       if (tail)
-               strbuf_add(&filename_template, file, tail - file + 1);
-       strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
-
-       trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
-       strbuf_release(&filename_template);
-       outfile = fdopen_tempfile(trailers_tempfile, "w");
-       if (!outfile)
-               die_errno(_("could not open temporary file"));
-
-       return outfile;
-}
-
-void process_trailers(const char *file,
-                     const struct process_trailer_options *opts,
-                     struct list_head *new_trailer_head)
-{
-       LIST_HEAD(head);
-       struct strbuf sb = STRBUF_INIT;
-       struct trailer_info info;
-       FILE *outfile = stdout;
-
-       ensure_configured();
-
-       read_input_file(&sb, file);
-
-       if (opts->in_place)
-               outfile = create_in_place_tempfile(file);
-
-       parse_trailers(&info, sb.buf, &head, opts);
-
-       /* Print the lines before the trailers */
-       if (!opts->only_trailers)
-               fwrite(sb.buf, 1, info.trailer_block_start, outfile);
-
-       if (!opts->only_trailers && !info.blank_line_before_trailer)
-               fprintf(outfile, "\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_all(outfile, &head, opts);
-
-       free_all(&head);
-       trailer_info_release(&info);
-
-       /* Print the lines after the trailers as is */
-       if (!opts->only_trailers)
-               fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
-
-       if (opts->in_place)
-               if (rename_tempfile(&trailers_tempfile, file))
-                       die_errno(_("could not rename temporary file to %s"), file);
-
-       strbuf_release(&sb);
-}
-
-void trailer_info_get(struct trailer_info *info, const char *str,
-                     const struct process_trailer_options *opts)
+void trailer_info_get(const struct process_trailer_options *opts,
+                     const char *str,
+                     struct trailer_info *info)
 {
        size_t end_of_log_message = 0, trailer_block_start = 0;
        struct strbuf **trailer_lines, **ptr;
@@ -1135,7 +1010,7 @@ void trailer_info_get(struct trailer_info *info, const char *str,
        size_t nr = 0, alloc = 0;
        char **last = NULL;
 
-       ensure_configured();
+       trailer_config_init();
 
        end_of_log_message = find_end_of_log_message(str, opts->no_divider);
        trailer_block_start = find_trailer_block_start(str, end_of_log_message);
@@ -1177,36 +1052,32 @@ void trailer_info_release(struct trailer_info *info)
        free(info->trailers);
 }
 
-static void format_trailer_info(struct strbuf *out,
-                               const struct trailer_info *info,
-                               const char *msg,
-                               const struct process_trailer_options *opts)
+void format_trailers(const struct process_trailer_options *opts,
+                    struct list_head *trailers,
+                    struct strbuf *out)
 {
        size_t origlen = out->len;
-       size_t i;
-
-       /* If we want the whole block untouched, we can take the fast path. */
-       if (!opts->only_trailers && !opts->unfold && !opts->filter &&
-           !opts->separator && !opts->key_only && !opts->value_only &&
-           !opts->key_value_separator) {
-               strbuf_add(out, msg + info->trailer_block_start,
-                          info->trailer_block_end - info->trailer_block_start);
-               return;
-       }
-
-       for (i = 0; i < info->trailer_nr; i++) {
-               char *trailer = info->trailers[i];
-               ssize_t separator_pos = find_separator(trailer, separators);
+       struct list_head *pos;
+       struct trailer_item *item;
 
-               if (separator_pos >= 1) {
+       list_for_each(pos, trailers) {
+               item = list_entry(pos, struct trailer_item, list);
+               if (item->token) {
                        struct strbuf tok = STRBUF_INIT;
                        struct strbuf val = STRBUF_INIT;
+                       strbuf_addstr(&tok, item->token);
+                       strbuf_addstr(&val, item->value);
+
+                       /*
+                        * Skip key/value pairs where the value was empty. This
+                        * can happen from trailers specified without a
+                        * separator, like `--trailer "Reviewed-by"` (no
+                        * corresponding value).
+                        */
+                       if (opts->trim_empty && !strlen(item->value))
+                               continue;
 
-                       parse_trailer(&tok, &val, NULL, trailer, separator_pos);
                        if (!opts->filter || opts->filter(&tok, opts->filter_data)) {
-                               if (opts->unfold)
-                                       unfold_value(&val);
-
                                if (opts->separator && out->len != origlen)
                                        strbuf_addbuf(out, opts->separator);
                                if (!opts->value_only)
@@ -1214,8 +1085,11 @@ static void format_trailer_info(struct strbuf *out,
                                if (!opts->key_only && !opts->value_only) {
                                        if (opts->key_value_separator)
                                                strbuf_addbuf(out, opts->key_value_separator);
-                                       else
-                                               strbuf_addstr(out, ": ");
+                                       else {
+                                               char c = last_non_space_char(tok.buf);
+                                               if (c && !strchr(separators, c))
+                                                       strbuf_addf(out, "%c ", separators[0]);
+                                       }
                                }
                                if (!opts->key_only)
                                        strbuf_addbuf(out, &val);
@@ -1229,22 +1103,34 @@ static void format_trailer_info(struct strbuf *out,
                        if (opts->separator && out->len != origlen) {
                                strbuf_addbuf(out, opts->separator);
                        }
-                       strbuf_addstr(out, trailer);
-                       if (opts->separator) {
+                       strbuf_addstr(out, item->value);
+                       if (opts->separator)
                                strbuf_rtrim(out);
-                       }
+                       else
+                               strbuf_addch(out, '\n');
                }
        }
-
 }
 
-void format_trailers_from_commit(struct strbuf *out, const char *msg,
-                                const struct process_trailer_options *opts)
+void format_trailers_from_commit(const struct process_trailer_options *opts,
+                                const char *msg,
+                                struct strbuf *out)
 {
+       LIST_HEAD(trailer_objects);
        struct trailer_info info;
 
-       trailer_info_get(&info, msg, opts);
-       format_trailer_info(out, &info, msg, opts);
+       parse_trailers(opts, &info, msg, &trailer_objects);
+
+       /* If we want the whole block untouched, we can take the fast path. */
+       if (!opts->only_trailers && !opts->unfold && !opts->filter &&
+           !opts->separator && !opts->key_only && !opts->value_only &&
+           !opts->key_value_separator) {
+               strbuf_add(out, msg + info.trailer_block_start,
+                          info.trailer_block_end - info.trailer_block_start);
+       } else
+               format_trailers(opts, &trailer_objects, out);
+
+       free_trailers(&trailer_objects);
        trailer_info_release(&info);
 }
 
@@ -1254,7 +1140,7 @@ void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
        strbuf_init(&iter->key, 0);
        strbuf_init(&iter->val, 0);
        opts.no_divider = 1;
-       trailer_info_get(&iter->internal.info, msg, &opts);
+       trailer_info_get(&opts, msg, &iter->internal.info);
        iter->internal.cur = 0;
 }
 
@@ -1271,6 +1157,7 @@ int trailer_iterator_advance(struct trailer_iterator *iter)
                strbuf_reset(&iter->val);
                parse_trailer(&iter->key, &iter->val, NULL,
                              trailer, separator_pos);
+               /* Always unfold values during iteration. */
                unfold_value(&iter->val);
                return 1;
        }