]> git.ipfire.org Git - thirdparty/git.git/blobdiff - trailer.c
format_trailer_info(): move "fast path" to caller
[thirdparty/git.git] / trailer.c
index b6de5d9cb2dab2c07c26775e62e5bfc40241c9fc..e92d0154d902f15d3054a862bacb82dbe6e186d8 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,12 +144,12 @@ static char last_non_space_char(const char *s)
        return '\0';
 }
 
-static void print_tok_val(FILE *outfile, const char *tok, const char *val)
+static void print_tok_val(struct strbuf *out, const char *tok, const char *val)
 {
        char c;
 
        if (!tok) {
-               fprintf(outfile, "%s\n", val);
+               strbuf_addf(out, "%s\n", val);
                return;
        }
 
@@ -158,21 +157,22 @@ static void print_tok_val(FILE *outfile, const char *tok, const char *val)
        if (!c)
                return;
        if (strchr(separators, c))
-               fprintf(outfile, "%s%s\n", tok, val);
+               strbuf_addf(out, "%s%s\n", tok, val);
        else
-               fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
+               strbuf_addf(out, "%s%c %s\n", tok, separators[0], val);
 }
 
-static void print_all(FILE *outfile, struct list_head *head,
-                     const struct process_trailer_options *opts)
+void format_trailers(const struct process_trailer_options *opts,
+                    struct list_head *trailers,
+                    struct strbuf *out)
 {
        struct list_head *pos;
        struct trailer_item *item;
-       list_for_each(pos, head) {
+       list_for_each(pos, trailers) {
                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);
+                       print_tok_val(out, item->token, item->value);
        }
 }
 
@@ -366,8 +366,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;
@@ -507,6 +507,8 @@ static int git_trailer_default_config(const char *conf_key, const char *value,
                                warning(_("unknown value '%s' for key '%s'"),
                                        value, conf_key);
                } else if (!strcmp(trailer_item, "separators")) {
+                       if (!value)
+                               return config_error_nonbool(conf_key);
                        separators = xstrdup(value);
                }
        }
@@ -551,16 +553,22 @@ static int git_trailer_config(const char *conf_key, const char *value,
        case TRAILER_KEY:
                if (conf->key)
                        warning(_("more than one %s"), conf_key);
+               if (!value)
+                       return config_error_nonbool(conf_key);
                conf->key = xstrdup(value);
                break;
        case TRAILER_COMMAND:
                if (conf->command)
                        warning(_("more than one %s"), conf_key);
+               if (!value)
+                       return config_error_nonbool(conf_key);
                conf->command = xstrdup(value);
                break;
        case TRAILER_CMD:
                if (conf->cmd)
                        warning(_("more than one %s"), conf_key);
+               if (!value)
+                       return config_error_nonbool(conf_key);
                conf->cmd = xstrdup(value);
                break;
        case TRAILER_WHERE:
@@ -581,7 +589,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;
@@ -711,7 +719,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;
@@ -727,8 +735,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;
@@ -767,17 +775,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');
@@ -809,28 +806,56 @@ static ssize_t last_line(const char *buf, size_t len)
 }
 
 /*
- * Return the position of the start of the patch or the length of str if there
- * is no patch in the message.
+ * Find the end of the log message as an offset from the start of the input
+ * (where callers of this function are interested in looking for a trailers
+ * block in the same input). We have to consider two categories of content that
+ * can come at the end of the input which we want to ignore (because they don't
+ * belong in the log message):
+ *
+ * (1) the "patch part" which begins with a "---" divider and has patch
+ * information (like the output of git-format-patch), and
+ *
+ * (2) any trailing comment lines, blank lines like in the output of "git
+ * commit -v", or stuff below the "cut" (scissor) line.
+ *
+ * As a formula, the situation looks like this:
+ *
+ *     INPUT = LOG MESSAGE + IGNORED
+ *
+ * where IGNORED can be either of the two categories described above. It may be
+ * that there is nothing to ignore. Now it may be the case that the LOG MESSAGE
+ * contains a trailer block, but that's not the concern of this function.
  */
-static size_t find_patch_start(const char *str)
+static size_t find_end_of_log_message(const char *input, int no_divider)
 {
+       size_t end;
        const char *s;
 
-       for (s = str; *s; s = next_line(s)) {
+       /* 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 (skip_prefix(s, "---", &v) && isspace(*v))
-                       return s - str;
+               if (skip_prefix(s, "---", &v) && isspace(*v)) {
+                       end = s - input;
+                       break;
+               }
        }
 
-       return s - str;
+       /* Skip over other ignorable bits. */
+       return end - ignored_log_message_bytes(input, end);
 }
 
 /*
  * Return the position of the first trailer line or len if there are no
  * trailers.
  */
-static size_t find_trailer_start(const char *buf, size_t len)
+static size_t find_trailer_block_start(const char *buf, size_t len)
 {
        const char *s;
        ssize_t end_of_title, l;
@@ -925,12 +950,6 @@ continue_outer_loop:
        return len;
 }
 
-/* Return the position of the end of the trailers. */
-static size_t find_trailer_end(const char *buf, size_t len)
-{
-       return len - ignore_non_trailer(buf, len);
-}
-
 static int ends_with_blank_line(const char *buf, size_t len)
 {
        ssize_t ll = last_line(buf, len);
@@ -970,16 +989,16 @@ 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;
@@ -1005,120 +1024,32 @@ 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;
-       size_t trailer_end;
-       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);
-       trailer_end = info.trailer_end - sb.buf;
-
-       /* Print the lines before the trailers */
-       if (!opts->only_trailers)
-               fwrite(sb.buf, 1, info.trailer_start - sb.buf, 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 + trailer_end, 1, sb.len - trailer_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)
 {
-       int patch_start, trailer_end, trailer_start;
+       size_t end_of_log_message = 0, trailer_block_start = 0;
        struct strbuf **trailer_lines, **ptr;
        char **trailer_strings = NULL;
        size_t nr = 0, alloc = 0;
        char **last = NULL;
 
-       ensure_configured();
+       trailer_config_init();
 
-       if (opts->no_divider)
-               patch_start = strlen(str);
-       else
-               patch_start = find_patch_start(str);
+       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);
 
-       trailer_end = find_trailer_end(str, patch_start);
-       trailer_start = find_trailer_start(str, trailer_end);
-
-       trailer_lines = strbuf_split_buf(str + trailer_start,
-                                        trailer_end - trailer_start,
+       trailer_lines = strbuf_split_buf(str + trailer_block_start,
+                                        end_of_log_message - trailer_block_start,
                                         '\n',
                                         0);
        for (ptr = trailer_lines; *ptr; ptr++) {
@@ -1139,9 +1070,9 @@ void trailer_info_get(struct trailer_info *info, const char *str,
        strbuf_list_free(trailer_lines);
 
        info->blank_line_before_trailer = ends_with_blank_line(str,
-                                                              trailer_start);
-       info->trailer_start = str + trailer_start;
-       info->trailer_end = str + trailer_end;
+                                                              trailer_block_start);
+       info->trailer_block_start = trailer_block_start;
+       info->trailer_block_end = end_of_log_message;
        info->trailers = trailer_strings;
        info->trailer_nr = nr;
 }
@@ -1154,22 +1085,13 @@ void trailer_info_release(struct trailer_info *info)
        free(info->trailers);
 }
 
-static void format_trailer_info(struct strbuf *out,
+static void format_trailer_info(const struct process_trailer_options *opts,
                                const struct trailer_info *info,
-                               const struct process_trailer_options *opts)
+                               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, info->trailer_start,
-                          info->trailer_end - info->trailer_start);
-               return;
-       }
-
        for (i = 0; i < info->trailer_nr; i++) {
                char *trailer = info->trailers[i];
                ssize_t separator_pos = find_separator(trailer, separators);
@@ -1214,13 +1136,22 @@ static void format_trailer_info(struct strbuf *out,
 
 }
 
-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)
 {
        struct trailer_info info;
 
-       trailer_info_get(&info, msg, opts);
-       format_trailer_info(out, &info, opts);
+       trailer_info_get(opts, msg, &info);
+       /* 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_trailer_info(opts, &info, out);
+
        trailer_info_release(&info);
 }
 
@@ -1230,7 +1161,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;
 }
 
@@ -1247,6 +1178,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;
        }