Imported Upstream version 2.10.2
[platform/upstream/git.git] / trailer.c
index a905f5c..aecaf92 100644 (file)
--- a/trailer.c
+++ b/trailer.c
@@ -1,7 +1,8 @@
 #include "cache.h"
 #include "string-list.h"
 #include "run-command.h"
-#include "string-list.h"
+#include "commit.h"
+#include "tempfile.h"
 #include "trailer.h"
 /*
  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
@@ -108,23 +109,23 @@ static char last_non_space_char(const char *s)
        return '\0';
 }
 
-static void print_tok_val(const char *tok, const char *val)
+static void print_tok_val(FILE *outfile, const char *tok, const char *val)
 {
        char c = last_non_space_char(tok);
        if (!c)
                return;
        if (strchr(separators, c))
-               printf("%s%s\n", tok, val);
+               fprintf(outfile, "%s%s\n", tok, val);
        else
-               printf("%s%c %s\n", tok, separators[0], val);
+               fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
 }
 
-static void print_all(struct trailer_item *first, int trim_empty)
+static void print_all(FILE *outfile, struct trailer_item *first, int trim_empty)
 {
        struct trailer_item *item;
        for (item = first; item; item = item->next) {
                if (!trim_empty || strlen(item->value) > 0)
-                       print_tok_val(item->token, item->value);
+                       print_tok_val(outfile, item->token, item->value);
        }
 }
 
@@ -214,16 +215,6 @@ static struct trailer_item *remove_first(struct trailer_item **first)
        return item;
 }
 
-static int read_from_command(struct child_process *cp, struct strbuf *buf)
-{
-       if (run_command(cp))
-               return error("running trailer command '%s' failed", cp->argv[0]);
-       if (strbuf_read(buf, cp->out, 1024) < 1)
-               return error("reading from trailer command '%s' failed", cp->argv[0]);
-       strbuf_trim(buf);
-       return 0;
-}
-
 static const char *apply_command(const char *command, const char *arg)
 {
        struct strbuf cmd = STRBUF_INIT;
@@ -240,14 +231,16 @@ static const char *apply_command(const char *command, const char *arg)
        cp.argv = argv;
        cp.env = local_repo_env;
        cp.no_stdin = 1;
-       cp.out = -1;
        cp.use_shell = 1;
 
-       if (read_from_command(&cp, &buf)) {
+       if (capture_command(&cp, &buf, 1024)) {
+               error(_("running trailer command '%s' failed"), cmd.buf);
                strbuf_release(&buf);
                result = xstrdup("");
-       } else
+       } else {
+               strbuf_trim(&buf);
                result = strbuf_detach(&buf, NULL);
+       }
 
        strbuf_release(&cmd);
        return result;
@@ -435,12 +428,9 @@ static int set_if_missing(struct conf_info *item, const char *value)
 static void duplicate_conf(struct conf_info *dst, struct conf_info *src)
 {
        *dst = *src;
-       if (src->name)
-               dst->name = xstrdup(src->name);
-       if (src->key)
-               dst->key = xstrdup(src->key);
-       if (src->command)
-               dst->command = xstrdup(src->command);
+       dst->name = xstrdup_or_null(src->name);
+       dst->key = xstrdup_or_null(src->key);
+       dst->command = xstrdup_or_null(src->command);
 }
 
 static struct trailer_item *get_conf_item(const char *name)
@@ -569,7 +559,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)
                        warning(_("unknown value '%s' for key '%s'"), value, conf_key);
                break;
        default:
-               die("internal bug in trailer.c");
+               die("BUG: trailer.c: unhandled type %d", type);
        }
        return 0;
 }
@@ -743,13 +733,22 @@ static int find_patch_start(struct strbuf **lines, int count)
  */
 static int find_trailer_start(struct strbuf **lines, int count)
 {
-       int start, only_spaces = 1;
+       int start, end_of_title, only_spaces = 1;
+
+       /* The first paragraph is the title and cannot be trailers */
+       for (start = 0; start < count; start++) {
+               if (lines[start]->buf[0] == comment_line_char)
+                       continue;
+               if (contains_only_spaces(lines[start]->buf))
+                       break;
+       }
+       end_of_title = start;
 
        /*
         * Get the start of the trailers by looking starting from the end
         * for a line with only spaces before lines with one separator.
         */
-       for (start = count - 1; start >= 0; start--) {
+       for (start = count - 1; start >= end_of_title; start--) {
                if (lines[start]->buf[0] == comment_line_char)
                        continue;
                if (contains_only_spaces(lines[start]->buf)) {
@@ -768,6 +767,22 @@ static int find_trailer_start(struct strbuf **lines, int count)
        return only_spaces ? count : 0;
 }
 
+/* Get the index of the end of the trailers */
+static int find_trailer_end(struct strbuf **lines, int patch_start)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int i, ignore_bytes;
+
+       for (i = 0; i < patch_start; i++)
+               strbuf_addbuf(&sb, lines[i]);
+       ignore_bytes = ignore_non_trailer(&sb);
+       strbuf_release(&sb);
+       for (i = patch_start - 1; i >= 0 && ignore_bytes > 0; i--)
+               ignore_bytes -= lines[i]->len;
+
+       return i + 1;
+}
+
 static int has_blank_line_before(struct strbuf **lines, int start)
 {
        for (;start >= 0; start--) {
@@ -778,42 +793,44 @@ static int has_blank_line_before(struct strbuf **lines, int start)
        return 0;
 }
 
-static void print_lines(struct strbuf **lines, int start, int end)
+static void print_lines(FILE *outfile, struct strbuf **lines, int start, int end)
 {
        int i;
        for (i = start; lines[i] && i < end; i++)
-               printf("%s", lines[i]->buf);
+               fprintf(outfile, "%s", lines[i]->buf);
 }
 
-static int process_input_file(struct strbuf **lines,
+static int process_input_file(FILE *outfile,
+                             struct strbuf **lines,
                              struct trailer_item **in_tok_first,
                              struct trailer_item **in_tok_last)
 {
        int count = 0;
-       int patch_start, trailer_start, i;
+       int patch_start, trailer_start, trailer_end, i;
 
        /* Get the line count */
        while (lines[count])
                count++;
 
        patch_start = find_patch_start(lines, count);
-       trailer_start = find_trailer_start(lines, patch_start);
+       trailer_end = find_trailer_end(lines, patch_start);
+       trailer_start = find_trailer_start(lines, trailer_end);
 
        /* Print lines before the trailers as is */
-       print_lines(lines, 0, trailer_start);
+       print_lines(outfile, lines, 0, trailer_start);
 
        if (!has_blank_line_before(lines, trailer_start - 1))
-               printf("\n");
+               fprintf(outfile, "\n");
 
        /* Parse trailer lines */
-       for (i = trailer_start; i < patch_start; i++) {
+       for (i = trailer_start; i < trailer_end; i++) {
                if (lines[i]->buf[0] != comment_line_char) {
                        struct trailer_item *new = create_trailer_item(lines[i]->buf);
                        add_trailer_item(in_tok_first, in_tok_last, new);
                }
        }
 
-       return patch_start;
+       return trailer_end;
 }
 
 static void free_all(struct trailer_item **first)
@@ -824,13 +841,45 @@ static void free_all(struct trailer_item **first)
        }
 }
 
-void process_trailers(const char *file, int trim_empty, struct string_list *trailers)
+static struct tempfile trailers_tempfile;
+
+static FILE *create_in_place_tempfile(const char *file)
+{
+       struct stat st;
+       struct strbuf 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 != NULL)
+               strbuf_add(&template, file, tail - file + 1);
+       strbuf_addstr(&template, "git-interpret-trailers-XXXXXX");
+
+       xmks_tempfile_m(&trailers_tempfile, template.buf, st.st_mode);
+       strbuf_release(&template);
+       outfile = fdopen_tempfile(&trailers_tempfile, "w");
+       if (!outfile)
+               die_errno(_("could not open temporary file"));
+
+       return outfile;
+}
+
+void process_trailers(const char *file, int in_place, int trim_empty, struct string_list *trailers)
 {
        struct trailer_item *in_tok_first = NULL;
        struct trailer_item *in_tok_last = NULL;
        struct trailer_item *arg_tok_first;
        struct strbuf **lines;
-       int patch_start;
+       int trailer_end;
+       FILE *outfile = stdout;
 
        /* Default config must be setup first */
        git_config(git_trailer_default_config, NULL);
@@ -838,19 +887,26 @@ void process_trailers(const char *file, int trim_empty, struct string_list *trai
 
        lines = read_input_file(file);
 
+       if (in_place)
+               outfile = create_in_place_tempfile(file);
+
        /* Print the lines before the trailers */
-       patch_start = process_input_file(lines, &in_tok_first, &in_tok_last);
+       trailer_end = process_input_file(outfile, lines, &in_tok_first, &in_tok_last);
 
        arg_tok_first = process_command_line_args(trailers);
 
        process_trailers_lists(&in_tok_first, &in_tok_last, &arg_tok_first);
 
-       print_all(in_tok_first, trim_empty);
+       print_all(outfile, in_tok_first, trim_empty);
 
        free_all(&in_tok_first);
 
        /* Print the lines after the trailers as is */
-       print_lines(lines, patch_start, INT_MAX);
+       print_lines(outfile, lines, trailer_end, INT_MAX);
+
+       if (in_place)
+               if (rename_tempfile(&trailers_tempfile, file))
+                       die_errno(_("could not rename temporary file to %s"), file);
 
        strbuf_list_free(lines);
 }