2023-03-21 09:26:04 +03:00
|
|
|
#include "git-compat-util.h"
|
2017-06-14 21:07:36 +03:00
|
|
|
#include "config.h"
|
2023-03-21 09:25:57 +03:00
|
|
|
#include "environment.h"
|
2023-03-21 09:25:54 +03:00
|
|
|
#include "gettext.h"
|
2014-10-13 22:16:26 +04:00
|
|
|
#include "string-list.h"
|
2014-10-13 22:16:31 +04:00
|
|
|
#include "run-command.h"
|
2014-11-09 12:23:42 +03:00
|
|
|
#include "commit.h"
|
2016-01-14 19:57:55 +03:00
|
|
|
#include "tempfile.h"
|
2014-10-13 22:16:28 +04:00
|
|
|
#include "trailer.h"
|
2016-10-21 00:39:47 +03:00
|
|
|
#include "list.h"
|
2014-10-13 22:16:23 +04:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct conf_info {
|
|
|
|
char *name;
|
|
|
|
char *key;
|
|
|
|
char *command;
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
char *cmd;
|
2017-07-24 11:22:43 +03:00
|
|
|
enum trailer_where where;
|
|
|
|
enum trailer_if_exists if_exists;
|
|
|
|
enum trailer_if_missing if_missing;
|
2014-10-13 22:16:23 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct conf_info default_conf_info;
|
|
|
|
|
|
|
|
struct trailer_item {
|
2016-10-21 00:39:47 +03:00
|
|
|
struct list_head list;
|
2016-10-21 20:55:01 +03:00
|
|
|
/*
|
|
|
|
* If this is not a trailer line, the line is stored in value
|
|
|
|
* (excluding the terminating newline) and token is NULL.
|
|
|
|
*/
|
2016-10-14 20:37:58 +03:00
|
|
|
char *token;
|
|
|
|
char *value;
|
2016-10-21 00:39:49 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
struct arg_item {
|
|
|
|
struct list_head list;
|
|
|
|
char *token;
|
|
|
|
char *value;
|
2014-10-13 22:16:23 +04:00
|
|
|
struct conf_info conf;
|
|
|
|
};
|
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
static LIST_HEAD(conf_head);
|
2014-10-13 22:16:23 +04:00
|
|
|
|
|
|
|
static char *separators = ":";
|
|
|
|
|
2016-11-02 20:29:19 +03:00
|
|
|
static int configured;
|
|
|
|
|
2014-10-13 22:16:31 +04:00
|
|
|
#define TRAILER_ARG_STRING "$ARG"
|
|
|
|
|
2016-10-21 20:55:01 +03:00
|
|
|
static const char *git_generated_prefixes[] = {
|
|
|
|
"Signed-off-by: ",
|
|
|
|
"(cherry picked from commit ",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
/* Iterate over the elements of the list. */
|
|
|
|
#define list_for_each_dir(pos, head, is_reverse) \
|
|
|
|
for (pos = is_reverse ? (head)->prev : (head)->next; \
|
|
|
|
pos != (head); \
|
|
|
|
pos = is_reverse ? pos->prev : pos->next)
|
|
|
|
|
2017-07-24 11:22:43 +03:00
|
|
|
static int after_or_end(enum trailer_where where)
|
2014-10-13 22:16:23 +04:00
|
|
|
{
|
|
|
|
return (where == WHERE_AFTER) || (where == WHERE_END);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the length of the string not including any final
|
|
|
|
* punctuation. E.g., the input "Signed-off-by:" would return
|
|
|
|
* 13, stripping the trailing punctuation but retaining
|
|
|
|
* internal punctuation.
|
|
|
|
*/
|
|
|
|
static size_t token_len_without_separator(const char *token, size_t len)
|
|
|
|
{
|
|
|
|
while (len > 0 && !isalnum(token[len - 1]))
|
|
|
|
len--;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
static int same_token(struct trailer_item *a, struct arg_item *b)
|
2014-10-13 22:16:23 +04:00
|
|
|
{
|
2016-10-21 20:55:01 +03:00
|
|
|
size_t a_len, b_len, min_len;
|
|
|
|
|
|
|
|
if (!a->token)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
a_len = token_len_without_separator(a->token, strlen(a->token));
|
|
|
|
b_len = token_len_without_separator(b->token, strlen(b->token));
|
|
|
|
min_len = (a_len > b_len) ? b_len : a_len;
|
2014-10-13 22:16:23 +04:00
|
|
|
|
|
|
|
return !strncasecmp(a->token, b->token, min_len);
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
static int same_value(struct trailer_item *a, struct arg_item *b)
|
2014-10-13 22:16:23 +04:00
|
|
|
{
|
|
|
|
return !strcasecmp(a->value, b->value);
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
static int same_trailer(struct trailer_item *a, struct arg_item *b)
|
2014-10-13 22:16:23 +04:00
|
|
|
{
|
|
|
|
return same_token(a, b) && same_value(a, b);
|
|
|
|
}
|
2014-10-13 22:16:24 +04:00
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
static inline int is_blank_line(const char *str)
|
2014-10-13 22:16:27 +04:00
|
|
|
{
|
|
|
|
const char *s = str;
|
2016-11-02 20:29:18 +03:00
|
|
|
while (*s && *s != '\n' && isspace(*s))
|
2014-10-13 22:16:27 +04:00
|
|
|
s++;
|
2016-11-02 20:29:18 +03:00
|
|
|
return !*s || *s == '\n';
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
|
|
|
|
2014-10-13 22:16:31 +04:00
|
|
|
static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)
|
|
|
|
{
|
|
|
|
const char *ptr = strstr(sb->buf, a);
|
|
|
|
if (ptr)
|
|
|
|
strbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));
|
|
|
|
}
|
|
|
|
|
2014-10-13 22:16:24 +04:00
|
|
|
static void free_trailer_item(struct trailer_item *item)
|
2016-10-21 00:39:49 +03:00
|
|
|
{
|
|
|
|
free(item->token);
|
|
|
|
free(item->value);
|
|
|
|
free(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_arg_item(struct arg_item *item)
|
2014-10-13 22:16:24 +04:00
|
|
|
{
|
|
|
|
free(item->conf.name);
|
|
|
|
free(item->conf.key);
|
|
|
|
free(item->conf.command);
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
free(item->conf.cmd);
|
2016-10-14 20:37:58 +03:00
|
|
|
free(item->token);
|
|
|
|
free(item->value);
|
2014-10-13 22:16:24 +04:00
|
|
|
free(item);
|
|
|
|
}
|
|
|
|
|
2014-10-13 22:16:28 +04:00
|
|
|
static char last_non_space_char(const char *s)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = strlen(s) - 1; i >= 0; i--)
|
|
|
|
if (!isspace(s[i]))
|
|
|
|
return s[i];
|
|
|
|
return '\0';
|
|
|
|
}
|
|
|
|
|
2016-01-14 19:57:54 +03:00
|
|
|
static void print_tok_val(FILE *outfile, const char *tok, const char *val)
|
2014-10-13 22:16:28 +04:00
|
|
|
{
|
2016-10-21 20:55:01 +03:00
|
|
|
char c;
|
|
|
|
|
|
|
|
if (!tok) {
|
|
|
|
fprintf(outfile, "%s\n", val);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
c = last_non_space_char(tok);
|
2014-10-13 22:16:28 +04:00
|
|
|
if (!c)
|
|
|
|
return;
|
|
|
|
if (strchr(separators, c))
|
2016-01-14 19:57:54 +03:00
|
|
|
fprintf(outfile, "%s%s\n", tok, val);
|
2014-10-13 22:16:28 +04:00
|
|
|
else
|
2016-01-14 19:57:54 +03:00
|
|
|
fprintf(outfile, "%s%c %s\n", tok, separators[0], val);
|
2014-10-13 22:16:28 +04:00
|
|
|
}
|
|
|
|
|
2017-08-15 13:23:21 +03:00
|
|
|
static void print_all(FILE *outfile, struct list_head *head,
|
|
|
|
const struct process_trailer_options *opts)
|
2014-10-13 22:16:28 +04:00
|
|
|
{
|
2016-10-21 00:39:47 +03:00
|
|
|
struct list_head *pos;
|
2014-10-13 22:16:28 +04:00
|
|
|
struct trailer_item *item;
|
2016-10-21 00:39:47 +03:00
|
|
|
list_for_each(pos, head) {
|
|
|
|
item = list_entry(pos, struct trailer_item, list);
|
2017-08-15 13:23:21 +03:00
|
|
|
if ((!opts->trim_empty || strlen(item->value) > 0) &&
|
|
|
|
(!opts->only_trailers || item->token))
|
2016-01-14 19:57:54 +03:00
|
|
|
print_tok_val(outfile, item->token, item->value);
|
2014-10-13 22:16:28 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
static struct trailer_item *trailer_from_arg(struct arg_item *arg_tok)
|
2014-10-13 22:16:24 +04:00
|
|
|
{
|
2021-03-06 14:26:19 +03:00
|
|
|
struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
|
2018-02-14 21:59:50 +03:00
|
|
|
new_item->token = arg_tok->token;
|
|
|
|
new_item->value = arg_tok->value;
|
2016-10-21 00:39:49 +03:00
|
|
|
arg_tok->token = arg_tok->value = NULL;
|
|
|
|
free_arg_item(arg_tok);
|
2018-02-14 21:59:50 +03:00
|
|
|
return new_item;
|
2014-10-13 22:16:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void add_arg_to_input_list(struct trailer_item *on_tok,
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *arg_tok)
|
2016-10-21 00:39:47 +03:00
|
|
|
{
|
2016-10-21 00:39:49 +03:00
|
|
|
int aoe = after_or_end(arg_tok->conf.where);
|
|
|
|
struct trailer_item *to_add = trailer_from_arg(arg_tok);
|
|
|
|
if (aoe)
|
|
|
|
list_add(&to_add->list, &on_tok->list);
|
2016-10-21 00:39:47 +03:00
|
|
|
else
|
2016-10-21 00:39:49 +03:00
|
|
|
list_add_tail(&to_add->list, &on_tok->list);
|
2014-10-13 22:16:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int check_if_different(struct trailer_item *in_tok,
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *arg_tok,
|
2016-10-21 00:39:47 +03:00
|
|
|
int check_all,
|
|
|
|
struct list_head *head)
|
2014-10-13 22:16:24 +04:00
|
|
|
{
|
2017-07-24 11:22:43 +03:00
|
|
|
enum trailer_where where = arg_tok->conf.where;
|
2016-10-21 00:39:47 +03:00
|
|
|
struct list_head *next_head;
|
2014-10-13 22:16:24 +04:00
|
|
|
do {
|
|
|
|
if (same_trailer(in_tok, arg_tok))
|
|
|
|
return 0;
|
|
|
|
/*
|
|
|
|
* if we want to add a trailer after another one,
|
|
|
|
* we have to check those before this one
|
|
|
|
*/
|
2016-10-21 00:39:47 +03:00
|
|
|
next_head = after_or_end(where) ? in_tok->list.prev
|
|
|
|
: in_tok->list.next;
|
|
|
|
if (next_head == head)
|
|
|
|
break;
|
|
|
|
in_tok = list_entry(next_head, struct trailer_item, list);
|
2014-10-13 22:16:24 +04:00
|
|
|
} while (check_all);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
static char *apply_command(struct conf_info *conf, const char *arg)
|
2014-10-13 22:16:31 +04:00
|
|
|
{
|
|
|
|
struct strbuf cmd = STRBUF_INIT;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2014-11-09 16:49:58 +03:00
|
|
|
struct child_process cp = CHILD_PROCESS_INIT;
|
2016-10-14 20:37:58 +03:00
|
|
|
char *result;
|
2014-10-13 22:16:31 +04:00
|
|
|
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
if (conf->cmd) {
|
|
|
|
strbuf_addstr(&cmd, conf->cmd);
|
|
|
|
strvec_push(&cp.args, cmd.buf);
|
|
|
|
if (arg)
|
|
|
|
strvec_push(&cp.args, arg);
|
|
|
|
} else if (conf->command) {
|
|
|
|
strbuf_addstr(&cmd, conf->command);
|
|
|
|
if (arg)
|
|
|
|
strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
|
|
|
|
strvec_push(&cp.args, cmd.buf);
|
|
|
|
}
|
2022-06-02 12:09:50 +03:00
|
|
|
strvec_pushv(&cp.env, (const char **)local_repo_env);
|
2014-10-13 22:16:31 +04:00
|
|
|
cp.no_stdin = 1;
|
|
|
|
cp.use_shell = 1;
|
|
|
|
|
2015-03-23 06:54:00 +03:00
|
|
|
if (capture_command(&cp, &buf, 1024)) {
|
2016-02-27 09:42:10 +03:00
|
|
|
error(_("running trailer command '%s' failed"), cmd.buf);
|
2014-10-13 22:16:31 +04:00
|
|
|
strbuf_release(&buf);
|
|
|
|
result = xstrdup("");
|
2015-03-23 06:54:00 +03:00
|
|
|
} else {
|
|
|
|
strbuf_trim(&buf);
|
2014-10-13 22:16:31 +04:00
|
|
|
result = strbuf_detach(&buf, NULL);
|
2015-03-23 06:54:00 +03:00
|
|
|
}
|
2014-10-13 22:16:31 +04:00
|
|
|
|
|
|
|
strbuf_release(&cmd);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
static void apply_item_command(struct trailer_item *in_tok, struct arg_item *arg_tok)
|
2014-10-13 22:16:31 +04:00
|
|
|
{
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
if (arg_tok->conf.command || arg_tok->conf.cmd) {
|
2014-10-13 22:16:31 +04:00
|
|
|
const char *arg;
|
|
|
|
if (arg_tok->value && arg_tok->value[0]) {
|
|
|
|
arg = arg_tok->value;
|
|
|
|
} else {
|
|
|
|
if (in_tok && in_tok->value)
|
|
|
|
arg = xstrdup(in_tok->value);
|
|
|
|
else
|
|
|
|
arg = xstrdup("");
|
|
|
|
}
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
arg_tok->value = apply_command(&arg_tok->conf, arg);
|
2014-10-13 22:16:31 +04:00
|
|
|
free((char *)arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-13 22:16:24 +04:00
|
|
|
static void apply_arg_if_exists(struct trailer_item *in_tok,
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *arg_tok,
|
2014-10-13 22:16:24 +04:00
|
|
|
struct trailer_item *on_tok,
|
2016-10-21 00:39:47 +03:00
|
|
|
struct list_head *head)
|
2014-10-13 22:16:24 +04:00
|
|
|
{
|
|
|
|
switch (arg_tok->conf.if_exists) {
|
|
|
|
case EXISTS_DO_NOTHING:
|
2016-10-21 00:39:49 +03:00
|
|
|
free_arg_item(arg_tok);
|
2014-10-13 22:16:24 +04:00
|
|
|
break;
|
|
|
|
case EXISTS_REPLACE:
|
2014-10-13 22:16:31 +04:00
|
|
|
apply_item_command(in_tok, arg_tok);
|
2016-10-21 00:39:47 +03:00
|
|
|
add_arg_to_input_list(on_tok, arg_tok);
|
|
|
|
list_del(&in_tok->list);
|
2014-10-13 22:16:24 +04:00
|
|
|
free_trailer_item(in_tok);
|
|
|
|
break;
|
|
|
|
case EXISTS_ADD:
|
2014-10-13 22:16:31 +04:00
|
|
|
apply_item_command(in_tok, arg_tok);
|
2016-10-21 00:39:47 +03:00
|
|
|
add_arg_to_input_list(on_tok, arg_tok);
|
2014-10-13 22:16:24 +04:00
|
|
|
break;
|
|
|
|
case EXISTS_ADD_IF_DIFFERENT:
|
2014-10-13 22:16:31 +04:00
|
|
|
apply_item_command(in_tok, arg_tok);
|
2016-10-21 00:39:47 +03:00
|
|
|
if (check_if_different(in_tok, arg_tok, 1, head))
|
|
|
|
add_arg_to_input_list(on_tok, arg_tok);
|
2014-10-13 22:16:24 +04:00
|
|
|
else
|
2016-10-21 00:39:49 +03:00
|
|
|
free_arg_item(arg_tok);
|
2014-10-13 22:16:24 +04:00
|
|
|
break;
|
|
|
|
case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
|
2014-10-13 22:16:31 +04:00
|
|
|
apply_item_command(in_tok, arg_tok);
|
2016-10-21 00:39:47 +03:00
|
|
|
if (check_if_different(on_tok, arg_tok, 0, head))
|
|
|
|
add_arg_to_input_list(on_tok, arg_tok);
|
2014-10-13 22:16:24 +04:00
|
|
|
else
|
2016-10-21 00:39:49 +03:00
|
|
|
free_arg_item(arg_tok);
|
2014-10-13 22:16:24 +04:00
|
|
|
break;
|
2017-08-01 12:03:32 +03:00
|
|
|
default:
|
2018-05-02 12:38:39 +03:00
|
|
|
BUG("trailer.c: unhandled value %d",
|
2017-08-01 12:03:32 +03:00
|
|
|
arg_tok->conf.if_exists);
|
2014-10-13 22:16:24 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
static void apply_arg_if_missing(struct list_head *head,
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *arg_tok)
|
2014-10-13 22:16:24 +04:00
|
|
|
{
|
2017-07-24 11:22:43 +03:00
|
|
|
enum trailer_where where;
|
2016-10-21 00:39:49 +03:00
|
|
|
struct trailer_item *to_add;
|
2014-10-13 22:16:24 +04:00
|
|
|
|
|
|
|
switch (arg_tok->conf.if_missing) {
|
|
|
|
case MISSING_DO_NOTHING:
|
2016-10-21 00:39:49 +03:00
|
|
|
free_arg_item(arg_tok);
|
2014-10-13 22:16:24 +04:00
|
|
|
break;
|
|
|
|
case MISSING_ADD:
|
|
|
|
where = arg_tok->conf.where;
|
2014-10-13 22:16:31 +04:00
|
|
|
apply_item_command(NULL, arg_tok);
|
2016-10-21 00:39:49 +03:00
|
|
|
to_add = trailer_from_arg(arg_tok);
|
2016-10-21 00:39:47 +03:00
|
|
|
if (after_or_end(where))
|
2016-10-21 00:39:49 +03:00
|
|
|
list_add_tail(&to_add->list, head);
|
2016-10-21 00:39:47 +03:00
|
|
|
else
|
2016-10-21 00:39:49 +03:00
|
|
|
list_add(&to_add->list, head);
|
2017-08-01 12:03:32 +03:00
|
|
|
break;
|
|
|
|
default:
|
2018-05-02 12:38:39 +03:00
|
|
|
BUG("trailer.c: unhandled value %d",
|
2017-08-01 12:03:32 +03:00
|
|
|
arg_tok->conf.if_missing);
|
2014-10-13 22:16:24 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
static int find_same_and_apply_arg(struct list_head *head,
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *arg_tok)
|
2014-10-13 22:16:24 +04:00
|
|
|
{
|
2016-10-21 00:39:47 +03:00
|
|
|
struct list_head *pos;
|
2014-10-13 22:16:24 +04:00
|
|
|
struct trailer_item *in_tok;
|
|
|
|
struct trailer_item *on_tok;
|
|
|
|
|
2017-07-24 11:22:43 +03:00
|
|
|
enum trailer_where where = arg_tok->conf.where;
|
2014-10-13 22:16:24 +04:00
|
|
|
int middle = (where == WHERE_AFTER) || (where == WHERE_BEFORE);
|
|
|
|
int backwards = after_or_end(where);
|
2016-10-21 00:39:47 +03:00
|
|
|
struct trailer_item *start_tok;
|
2014-10-13 22:16:24 +04:00
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
if (list_empty(head))
|
|
|
|
return 0;
|
2014-10-13 22:16:24 +04:00
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
start_tok = list_entry(backwards ? head->prev : head->next,
|
|
|
|
struct trailer_item,
|
|
|
|
list);
|
|
|
|
|
|
|
|
list_for_each_dir(pos, head, backwards) {
|
|
|
|
in_tok = list_entry(pos, struct trailer_item, list);
|
2014-10-13 22:16:24 +04:00
|
|
|
if (!same_token(in_tok, arg_tok))
|
|
|
|
continue;
|
|
|
|
on_tok = middle ? in_tok : start_tok;
|
2016-10-21 00:39:47 +03:00
|
|
|
apply_arg_if_exists(in_tok, arg_tok, on_tok, head);
|
2014-10-13 22:16:24 +04:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
static void process_trailers_lists(struct list_head *head,
|
|
|
|
struct list_head *arg_head)
|
2014-10-13 22:16:24 +04:00
|
|
|
{
|
2016-10-21 00:39:47 +03:00
|
|
|
struct list_head *pos, *p;
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *arg_tok;
|
2014-10-13 22:16:24 +04:00
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
list_for_each_safe(pos, p, arg_head) {
|
2014-10-13 22:16:24 +04:00
|
|
|
int applied = 0;
|
2016-10-21 00:39:49 +03:00
|
|
|
arg_tok = list_entry(pos, struct arg_item, list);
|
2014-10-13 22:16:24 +04:00
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
list_del(pos);
|
2014-10-13 22:16:24 +04:00
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
applied = find_same_and_apply_arg(head, arg_tok);
|
2014-10-13 22:16:24 +04:00
|
|
|
|
|
|
|
if (!applied)
|
2016-10-21 00:39:47 +03:00
|
|
|
apply_arg_if_missing(head, arg_tok);
|
2014-10-13 22:16:24 +04:00
|
|
|
}
|
|
|
|
}
|
2014-10-13 22:16:25 +04:00
|
|
|
|
2017-07-24 11:22:43 +03:00
|
|
|
int trailer_set_where(enum trailer_where *item, const char *value)
|
2014-10-13 22:16:25 +04:00
|
|
|
{
|
2017-08-01 12:03:32 +03:00
|
|
|
if (!value)
|
|
|
|
*item = WHERE_DEFAULT;
|
|
|
|
else if (!strcasecmp("after", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = WHERE_AFTER;
|
2014-10-13 22:16:25 +04:00
|
|
|
else if (!strcasecmp("before", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = WHERE_BEFORE;
|
2014-10-13 22:16:25 +04:00
|
|
|
else if (!strcasecmp("end", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = WHERE_END;
|
2014-10-13 22:16:25 +04:00
|
|
|
else if (!strcasecmp("start", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = WHERE_START;
|
2014-10-13 22:16:25 +04:00
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-24 11:22:43 +03:00
|
|
|
int trailer_set_if_exists(enum trailer_if_exists *item, const char *value)
|
2014-10-13 22:16:25 +04:00
|
|
|
{
|
2017-08-01 12:03:32 +03:00
|
|
|
if (!value)
|
|
|
|
*item = EXISTS_DEFAULT;
|
|
|
|
else if (!strcasecmp("addIfDifferent", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = EXISTS_ADD_IF_DIFFERENT;
|
2014-10-13 22:16:25 +04:00
|
|
|
else if (!strcasecmp("addIfDifferentNeighbor", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
|
2014-10-13 22:16:25 +04:00
|
|
|
else if (!strcasecmp("add", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = EXISTS_ADD;
|
2014-10-13 22:16:25 +04:00
|
|
|
else if (!strcasecmp("replace", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = EXISTS_REPLACE;
|
2014-10-13 22:16:25 +04:00
|
|
|
else if (!strcasecmp("doNothing", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = EXISTS_DO_NOTHING;
|
2014-10-13 22:16:25 +04:00
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-24 11:22:43 +03:00
|
|
|
int trailer_set_if_missing(enum trailer_if_missing *item, const char *value)
|
2014-10-13 22:16:25 +04:00
|
|
|
{
|
2017-08-01 12:03:32 +03:00
|
|
|
if (!value)
|
|
|
|
*item = MISSING_DEFAULT;
|
|
|
|
else if (!strcasecmp("doNothing", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = MISSING_DO_NOTHING;
|
2014-10-13 22:16:25 +04:00
|
|
|
else if (!strcasecmp("add", value))
|
2017-07-24 11:22:43 +03:00
|
|
|
*item = MISSING_ADD;
|
2014-10-13 22:16:25 +04:00
|
|
|
else
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-14 20:37:58 +03:00
|
|
|
static void duplicate_conf(struct conf_info *dst, const struct conf_info *src)
|
2014-10-13 22:16:25 +04:00
|
|
|
{
|
|
|
|
*dst = *src;
|
2016-10-12 21:20:23 +03:00
|
|
|
dst->name = xstrdup_or_null(src->name);
|
|
|
|
dst->key = xstrdup_or_null(src->key);
|
|
|
|
dst->command = xstrdup_or_null(src->command);
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
dst->cmd = xstrdup_or_null(src->cmd);
|
2014-10-13 22:16:25 +04:00
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
static struct arg_item *get_conf_item(const char *name)
|
2014-10-13 22:16:25 +04:00
|
|
|
{
|
2016-10-21 00:39:47 +03:00
|
|
|
struct list_head *pos;
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *item;
|
2014-10-13 22:16:25 +04:00
|
|
|
|
|
|
|
/* Look up item with same name */
|
2016-10-21 00:39:47 +03:00
|
|
|
list_for_each(pos, &conf_head) {
|
2016-10-21 00:39:49 +03:00
|
|
|
item = list_entry(pos, struct arg_item, list);
|
2014-10-13 22:16:25 +04:00
|
|
|
if (!strcasecmp(item->conf.name, name))
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Item does not already exists, create it */
|
2021-03-13 19:17:22 +03:00
|
|
|
CALLOC_ARRAY(item, 1);
|
2014-10-13 22:16:25 +04:00
|
|
|
duplicate_conf(&item->conf, &default_conf_info);
|
|
|
|
item->conf.name = xstrdup(name);
|
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
list_add_tail(&item->list, &conf_head);
|
2014-10-13 22:16:25 +04:00
|
|
|
|
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
enum trailer_info_type { TRAILER_KEY, TRAILER_COMMAND, TRAILER_CMD,
|
|
|
|
TRAILER_WHERE, TRAILER_IF_EXISTS, TRAILER_IF_MISSING };
|
2014-10-13 22:16:25 +04:00
|
|
|
|
|
|
|
static struct {
|
|
|
|
const char *name;
|
|
|
|
enum trailer_info_type type;
|
|
|
|
} trailer_config_items[] = {
|
|
|
|
{ "key", TRAILER_KEY },
|
|
|
|
{ "command", TRAILER_COMMAND },
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
{ "cmd", TRAILER_CMD },
|
2014-10-13 22:16:25 +04:00
|
|
|
{ "where", TRAILER_WHERE },
|
|
|
|
{ "ifexists", TRAILER_IF_EXISTS },
|
|
|
|
{ "ifmissing", TRAILER_IF_MISSING }
|
|
|
|
};
|
|
|
|
|
2022-08-19 13:08:44 +03:00
|
|
|
static int git_trailer_default_config(const char *conf_key, const char *value,
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-28 22:26:22 +03:00
|
|
|
const struct config_context *ctx UNUSED,
|
2022-08-25 20:09:48 +03:00
|
|
|
void *cb UNUSED)
|
2014-10-13 22:16:25 +04:00
|
|
|
{
|
|
|
|
const char *trailer_item, *variable_name;
|
|
|
|
|
|
|
|
if (!skip_prefix(conf_key, "trailer.", &trailer_item))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
variable_name = strrchr(trailer_item, '.');
|
|
|
|
if (!variable_name) {
|
|
|
|
if (!strcmp(trailer_item, "where")) {
|
2017-07-24 11:22:43 +03:00
|
|
|
if (trailer_set_where(&default_conf_info.where,
|
|
|
|
value) < 0)
|
2014-10-13 22:16:25 +04:00
|
|
|
warning(_("unknown value '%s' for key '%s'"),
|
|
|
|
value, conf_key);
|
|
|
|
} else if (!strcmp(trailer_item, "ifexists")) {
|
2017-07-24 11:22:43 +03:00
|
|
|
if (trailer_set_if_exists(&default_conf_info.if_exists,
|
|
|
|
value) < 0)
|
2014-10-13 22:16:25 +04:00
|
|
|
warning(_("unknown value '%s' for key '%s'"),
|
|
|
|
value, conf_key);
|
|
|
|
} else if (!strcmp(trailer_item, "ifmissing")) {
|
2017-07-24 11:22:43 +03:00
|
|
|
if (trailer_set_if_missing(&default_conf_info.if_missing,
|
|
|
|
value) < 0)
|
2014-10-13 22:16:25 +04:00
|
|
|
warning(_("unknown value '%s' for key '%s'"),
|
|
|
|
value, conf_key);
|
|
|
|
} else if (!strcmp(trailer_item, "separators")) {
|
2023-12-07 10:11:14 +03:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(conf_key);
|
2014-10-13 22:16:25 +04:00
|
|
|
separators = xstrdup(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-08-19 13:08:44 +03:00
|
|
|
static int git_trailer_config(const char *conf_key, const char *value,
|
config: add ctx arg to config_fn_t
Add a new "const struct config_context *ctx" arg to config_fn_t to hold
additional information about the config iteration operation.
config_context has a "struct key_value_info kvi" member that holds
metadata about the config source being read (e.g. what kind of config
source it is, the filename, etc). In this series, we're only interested
in .kvi, so we could have just used "struct key_value_info" as an arg,
but config_context makes it possible to add/adjust members in the future
without changing the config_fn_t signature. We could also consider other
ways of organizing the args (e.g. moving the config name and value into
config_context or key_value_info), but in my experiments, the
incremental benefit doesn't justify the added complexity (e.g. a
config_fn_t will sometimes invoke another config_fn_t but with a
different config value).
In subsequent commits, the .kvi member will replace the global "struct
config_reader" in config.c, making config iteration a global-free
operation. It requires much more work for the machinery to provide
meaningful values of .kvi, so for now, merely change the signature and
call sites, pass NULL as a placeholder value, and don't rely on the arg
in any meaningful way.
Most of the changes are performed by
contrib/coccinelle/config_fn_ctx.pending.cocci, which, for every
config_fn_t:
- Modifies the signature to accept "const struct config_context *ctx"
- Passes "ctx" to any inner config_fn_t, if needed
- Adds UNUSED attributes to "ctx", if needed
Most config_fn_t instances are easily identified by seeing if they are
called by the various config functions. Most of the remaining ones are
manually named in the .cocci patch. Manual cleanups are still needed,
but the majority of it is trivial; it's either adjusting config_fn_t
that the .cocci patch didn't catch, or adding forward declarations of
"struct config_context ctx" to make the signatures make sense.
The non-trivial changes are in cases where we are invoking a config_fn_t
outside of config machinery, and we now need to decide what value of
"ctx" to pass. These cases are:
- trace2/tr2_cfg.c:tr2_cfg_set_fl()
This is indirectly called by git_config_set() so that the trace2
machinery can notice the new config values and update its settings
using the tr2 config parsing function, i.e. tr2_cfg_cb().
- builtin/checkout.c:checkout_main()
This calls git_xmerge_config() as a shorthand for parsing a CLI arg.
This might be worth refactoring away in the future, since
git_xmerge_config() can call git_default_config(), which can do much
more than just parsing.
Handle them by creating a KVI_INIT macro that initializes "struct
key_value_info" to a reasonable default, and use that to construct the
"ctx" arg.
Signed-off-by: Glen Choo <chooglen@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-06-28 22:26:22 +03:00
|
|
|
const struct config_context *ctx UNUSED,
|
2022-08-25 20:09:48 +03:00
|
|
|
void *cb UNUSED)
|
2014-10-13 22:16:25 +04:00
|
|
|
{
|
|
|
|
const char *trailer_item, *variable_name;
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *item;
|
2014-10-13 22:16:25 +04:00
|
|
|
struct conf_info *conf;
|
|
|
|
char *name = NULL;
|
|
|
|
enum trailer_info_type type;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!skip_prefix(conf_key, "trailer.", &trailer_item))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
variable_name = strrchr(trailer_item, '.');
|
|
|
|
if (!variable_name)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
variable_name++;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(trailer_config_items); i++) {
|
|
|
|
if (strcmp(trailer_config_items[i].name, variable_name))
|
|
|
|
continue;
|
|
|
|
name = xstrndup(trailer_item, variable_name - trailer_item - 1);
|
|
|
|
type = trailer_config_items[i].type;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!name)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
item = get_conf_item(name);
|
|
|
|
conf = &item->conf;
|
|
|
|
free(name);
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case TRAILER_KEY:
|
|
|
|
if (conf->key)
|
|
|
|
warning(_("more than one %s"), conf_key);
|
2023-12-07 10:11:32 +03:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(conf_key);
|
2014-10-13 22:16:25 +04:00
|
|
|
conf->key = xstrdup(value);
|
|
|
|
break;
|
|
|
|
case TRAILER_COMMAND:
|
|
|
|
if (conf->command)
|
|
|
|
warning(_("more than one %s"), conf_key);
|
2023-12-07 10:11:32 +03:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(conf_key);
|
2014-10-13 22:16:25 +04:00
|
|
|
conf->command = xstrdup(value);
|
|
|
|
break;
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
case TRAILER_CMD:
|
|
|
|
if (conf->cmd)
|
|
|
|
warning(_("more than one %s"), conf_key);
|
2023-12-07 10:11:32 +03:00
|
|
|
if (!value)
|
|
|
|
return config_error_nonbool(conf_key);
|
trailer: add new .cmd config option
The `trailer.<token>.command` configuration variable
specifies a command (run via the shell, so it does not have
to be a single name or path to the command, but can be a
shell script), and the first occurrence of substring $ARG is
replaced with the value given to the `interpret-trailer`
command for the token in a '--trailer <token>=<value>' argument.
This has three downsides:
* The use of $ARG in the mechanism misleads the users that
the value is passed in the shell variable, and tempt them
to use $ARG more than once, but that would not work, as
the second and subsequent $ARG are not replaced.
* Because $ARG is textually replaced without regard to the
shell language syntax, even '$ARG' (inside a single-quote
pair), which a user would expect to stay intact, would be
replaced, and worse, if the value had an unmatched single
quote (imagine a name like "O'Connor", substituted into
NAME='$ARG' to make it NAME='O'Connor'), it would result in
a broken command that is not syntactically correct (or
worse).
* The first occurrence of substring `$ARG` will be replaced
with the empty string, in the command when the command is
first called to add a trailer with the specified <token>.
This is a bad design, the nature of automatic execution
causes it to add a trailer that we don't expect.
Introduce a new `trailer.<token>.cmd` configuration that
takes higher precedence to deprecate and eventually remove
`trailer.<token>.command`, which passes the value as an
argument to the command. Instead of "$ARG", users can
refer to the value as positional argument, $1, in their
scripts. At the same time, in order to allow
`git interpret-trailers` to better simulate the behavior
of `git command -s`, 'trailer.<token>.cmd' will not
automatically execute.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Christian Couder <christian.couder@gmail.com>
Signed-off-by: ZheNing Hu <adlternative@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2021-05-03 18:41:05 +03:00
|
|
|
conf->cmd = xstrdup(value);
|
|
|
|
break;
|
2014-10-13 22:16:25 +04:00
|
|
|
case TRAILER_WHERE:
|
2017-07-24 11:22:43 +03:00
|
|
|
if (trailer_set_where(&conf->where, value))
|
2014-10-13 22:16:25 +04:00
|
|
|
warning(_("unknown value '%s' for key '%s'"), value, conf_key);
|
|
|
|
break;
|
|
|
|
case TRAILER_IF_EXISTS:
|
2017-07-24 11:22:43 +03:00
|
|
|
if (trailer_set_if_exists(&conf->if_exists, value))
|
2014-10-13 22:16:25 +04:00
|
|
|
warning(_("unknown value '%s' for key '%s'"), value, conf_key);
|
|
|
|
break;
|
|
|
|
case TRAILER_IF_MISSING:
|
2017-07-24 11:22:43 +03:00
|
|
|
if (trailer_set_if_missing(&conf->if_missing, value))
|
2014-10-13 22:16:25 +04:00
|
|
|
warning(_("unknown value '%s' for key '%s'"), value, conf_key);
|
|
|
|
break;
|
|
|
|
default:
|
2018-05-02 12:38:39 +03:00
|
|
|
BUG("trailer.c: unhandled type %d", type);
|
2014-10-13 22:16:25 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2014-10-13 22:16:26 +04:00
|
|
|
|
2016-11-02 20:29:19 +03:00
|
|
|
static void ensure_configured(void)
|
|
|
|
{
|
|
|
|
if (configured)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Default config must be setup first */
|
2017-07-24 11:22:43 +03:00
|
|
|
default_conf_info.where = WHERE_END;
|
|
|
|
default_conf_info.if_exists = EXISTS_ADD_IF_DIFFERENT_NEIGHBOR;
|
|
|
|
default_conf_info.if_missing = MISSING_ADD;
|
2016-11-02 20:29:19 +03:00
|
|
|
git_config(git_trailer_default_config, NULL);
|
|
|
|
git_config(git_trailer_config, NULL);
|
|
|
|
configured = 1;
|
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
static const char *token_from_item(struct arg_item *item, char *tok)
|
2014-10-13 22:16:26 +04:00
|
|
|
{
|
|
|
|
if (item->conf.key)
|
|
|
|
return item->conf.key;
|
|
|
|
if (tok)
|
|
|
|
return tok;
|
|
|
|
return item->conf.name;
|
|
|
|
}
|
|
|
|
|
2018-08-23 03:44:38 +03:00
|
|
|
static int token_matches_item(const char *tok, struct arg_item *item, size_t tok_len)
|
2014-10-13 22:16:26 +04:00
|
|
|
{
|
|
|
|
if (!strncasecmp(tok, item->conf.name, tok_len))
|
|
|
|
return 1;
|
|
|
|
return item->conf.key ? !strncasecmp(tok, item->conf.key, tok_len) : 0;
|
|
|
|
}
|
|
|
|
|
2016-10-21 20:55:00 +03:00
|
|
|
/*
|
2016-11-02 20:29:16 +03:00
|
|
|
* If the given line is of the form
|
|
|
|
* "<token><optional whitespace><separator>..." or "<separator>...", return the
|
|
|
|
* location of the separator. Otherwise, return -1. The optional whitespace
|
|
|
|
* is allowed there primarily to allow things like "Bug #43" where <token> is
|
|
|
|
* "Bug" and <separator> is "#".
|
|
|
|
*
|
|
|
|
* The separator-starts-line case (in which this function returns 0) is
|
|
|
|
* distinguished from the non-well-formed-line case (in which this function
|
|
|
|
* returns -1) because some callers of this function need such a distinction.
|
2016-10-21 20:55:00 +03:00
|
|
|
*/
|
2018-08-23 03:44:38 +03:00
|
|
|
static ssize_t find_separator(const char *line, const char *separators)
|
2014-10-13 22:16:26 +04:00
|
|
|
{
|
2016-11-02 20:29:16 +03:00
|
|
|
int whitespace_found = 0;
|
|
|
|
const char *c;
|
|
|
|
for (c = line; *c; c++) {
|
|
|
|
if (strchr(separators, *c))
|
|
|
|
return c - line;
|
|
|
|
if (!whitespace_found && (isalnum(*c) || *c == '-'))
|
|
|
|
continue;
|
|
|
|
if (c != line && (*c == ' ' || *c == '\t')) {
|
|
|
|
whitespace_found = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return -1;
|
2016-10-21 20:55:00 +03:00
|
|
|
}
|
2014-10-13 22:16:26 +04:00
|
|
|
|
2016-10-21 20:55:00 +03:00
|
|
|
/*
|
|
|
|
* Obtain the token, value, and conf from the given trailer.
|
|
|
|
*
|
|
|
|
* separator_pos must not be 0, since the token cannot be an empty string.
|
|
|
|
*
|
|
|
|
* If separator_pos is -1, interpret the whole trailer as a token.
|
|
|
|
*/
|
|
|
|
static void parse_trailer(struct strbuf *tok, struct strbuf *val,
|
|
|
|
const struct conf_info **conf, const char *trailer,
|
2018-08-23 03:44:38 +03:00
|
|
|
ssize_t separator_pos)
|
2014-10-13 22:16:26 +04:00
|
|
|
{
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *item;
|
2018-08-23 03:44:38 +03:00
|
|
|
size_t tok_len;
|
2016-10-21 00:39:48 +03:00
|
|
|
struct list_head *pos;
|
2014-10-13 22:16:26 +04:00
|
|
|
|
2016-10-21 20:55:00 +03:00
|
|
|
if (separator_pos != -1) {
|
|
|
|
strbuf_add(tok, trailer, separator_pos);
|
2014-10-13 22:16:26 +04:00
|
|
|
strbuf_trim(tok);
|
2016-10-21 20:55:00 +03:00
|
|
|
strbuf_addstr(val, trailer + separator_pos + 1);
|
2014-10-13 22:16:26 +04:00
|
|
|
strbuf_trim(val);
|
|
|
|
} else {
|
|
|
|
strbuf_addstr(tok, trailer);
|
|
|
|
strbuf_trim(tok);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lookup if the token matches something in the config */
|
2016-10-21 00:39:48 +03:00
|
|
|
tok_len = token_len_without_separator(tok->buf, tok->len);
|
2016-10-21 00:39:49 +03:00
|
|
|
if (conf)
|
|
|
|
*conf = &default_conf_info;
|
2016-10-21 00:39:47 +03:00
|
|
|
list_for_each(pos, &conf_head) {
|
2016-10-21 00:39:49 +03:00
|
|
|
item = list_entry(pos, struct arg_item, list);
|
2016-10-21 00:39:48 +03:00
|
|
|
if (token_matches_item(tok->buf, item, tok_len)) {
|
|
|
|
char *tok_buf = strbuf_detach(tok, NULL);
|
2016-10-21 00:39:49 +03:00
|
|
|
if (conf)
|
|
|
|
*conf = &item->conf;
|
2016-10-21 00:39:48 +03:00
|
|
|
strbuf_addstr(tok, token_from_item(item, tok_buf));
|
|
|
|
free(tok_buf);
|
|
|
|
break;
|
|
|
|
}
|
2014-10-13 22:16:26 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-21 20:55:03 +03:00
|
|
|
static struct trailer_item *add_trailer_item(struct list_head *head, char *tok,
|
|
|
|
char *val)
|
2014-10-13 22:16:26 +04:00
|
|
|
{
|
2021-03-06 14:26:19 +03:00
|
|
|
struct trailer_item *new_item = xcalloc(1, sizeof(*new_item));
|
2018-02-14 21:59:50 +03:00
|
|
|
new_item->token = tok;
|
|
|
|
new_item->value = val;
|
|
|
|
list_add_tail(&new_item->list, head);
|
|
|
|
return new_item;
|
2014-10-13 22:16:26 +04:00
|
|
|
}
|
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
static void add_arg_item(struct list_head *arg_head, char *tok, char *val,
|
2017-08-01 12:03:32 +03:00
|
|
|
const struct conf_info *conf,
|
|
|
|
const struct new_trailer_item *new_trailer_item)
|
2014-10-13 22:16:26 +04:00
|
|
|
{
|
2021-03-06 14:26:19 +03:00
|
|
|
struct arg_item *new_item = xcalloc(1, sizeof(*new_item));
|
2018-02-14 21:59:50 +03:00
|
|
|
new_item->token = tok;
|
|
|
|
new_item->value = val;
|
|
|
|
duplicate_conf(&new_item->conf, conf);
|
2017-08-01 12:03:32 +03:00
|
|
|
if (new_trailer_item) {
|
|
|
|
if (new_trailer_item->where != WHERE_DEFAULT)
|
2018-02-14 21:59:50 +03:00
|
|
|
new_item->conf.where = new_trailer_item->where;
|
2017-08-01 12:03:32 +03:00
|
|
|
if (new_trailer_item->if_exists != EXISTS_DEFAULT)
|
2018-02-14 21:59:50 +03:00
|
|
|
new_item->conf.if_exists = new_trailer_item->if_exists;
|
2017-08-01 12:03:32 +03:00
|
|
|
if (new_trailer_item->if_missing != MISSING_DEFAULT)
|
2018-02-14 21:59:50 +03:00
|
|
|
new_item->conf.if_missing = new_trailer_item->if_missing;
|
2017-08-01 12:03:32 +03:00
|
|
|
}
|
2018-02-14 21:59:50 +03:00
|
|
|
list_add_tail(&new_item->list, arg_head);
|
2014-10-13 22:16:26 +04:00
|
|
|
}
|
|
|
|
|
2023-09-09 09:16:14 +03:00
|
|
|
static void parse_trailers_from_config(struct list_head *config_head)
|
2014-10-13 22:16:26 +04:00
|
|
|
{
|
2016-10-21 00:39:49 +03:00
|
|
|
struct arg_item *item;
|
2016-10-21 00:39:47 +03:00
|
|
|
struct list_head *pos;
|
2014-10-13 22:16:31 +04:00
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
/* Add an arg item for each configured trailer with a command */
|
2016-10-21 00:39:47 +03:00
|
|
|
list_for_each(pos, &conf_head) {
|
2016-10-21 00:39:49 +03:00
|
|
|
item = list_entry(pos, struct arg_item, list);
|
2016-10-21 00:39:48 +03:00
|
|
|
if (item->conf.command)
|
2023-09-09 09:16:14 +03:00
|
|
|
add_arg_item(config_head,
|
2016-10-21 00:39:49 +03:00
|
|
|
xstrdup(token_from_item(item, NULL)),
|
|
|
|
xstrdup(""),
|
2017-08-01 12:03:32 +03:00
|
|
|
&item->conf, NULL);
|
2014-10-13 22:16:31 +04:00
|
|
|
}
|
2023-09-09 09:16:14 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static 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;
|
|
|
|
const struct conf_info *conf;
|
|
|
|
struct list_head *pos;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In command-line arguments, '=' is accepted (in addition to the
|
|
|
|
* separators that are defined).
|
|
|
|
*/
|
|
|
|
char *cl_separators = xstrfmt("=%s", separators);
|
2014-10-13 22:16:26 +04:00
|
|
|
|
2016-10-21 00:39:49 +03:00
|
|
|
/* Add an arg item for each trailer on the command line */
|
2017-08-01 12:03:31 +03:00
|
|
|
list_for_each(pos, new_trailer_head) {
|
|
|
|
struct new_trailer_item *tr =
|
|
|
|
list_entry(pos, struct new_trailer_item, list);
|
2018-08-23 03:44:38 +03:00
|
|
|
ssize_t separator_pos = find_separator(tr->text, cl_separators);
|
2017-08-01 12:03:31 +03:00
|
|
|
|
2016-10-21 20:55:00 +03:00
|
|
|
if (separator_pos == 0) {
|
|
|
|
struct strbuf sb = STRBUF_INIT;
|
2017-08-01 12:03:31 +03:00
|
|
|
strbuf_addstr(&sb, tr->text);
|
2016-10-21 20:55:00 +03:00
|
|
|
strbuf_trim(&sb);
|
|
|
|
error(_("empty trailer token in trailer '%.*s'"),
|
|
|
|
(int) sb.len, sb.buf);
|
|
|
|
strbuf_release(&sb);
|
|
|
|
} else {
|
2017-08-01 12:03:31 +03:00
|
|
|
parse_trailer(&tok, &val, &conf, tr->text,
|
2016-10-21 20:55:00 +03:00
|
|
|
separator_pos);
|
2016-10-21 00:39:49 +03:00
|
|
|
add_arg_item(arg_head,
|
|
|
|
strbuf_detach(&tok, NULL),
|
|
|
|
strbuf_detach(&val, NULL),
|
2017-08-01 12:03:32 +03:00
|
|
|
conf, tr);
|
2016-10-21 20:55:00 +03:00
|
|
|
}
|
2014-10-13 22:16:26 +04:00
|
|
|
}
|
|
|
|
|
2016-10-21 20:55:00 +03:00
|
|
|
free(cl_separators);
|
2014-10-13 22:16:26 +04:00
|
|
|
}
|
2014-10-13 22:16:27 +04:00
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
static void read_input_file(struct strbuf *sb, const char *file)
|
2014-10-13 22:16:27 +04:00
|
|
|
{
|
|
|
|
if (file) {
|
2016-11-02 20:29:18 +03:00
|
|
|
if (strbuf_read_file(sb, file, 0) < 0)
|
2014-10-13 22:16:27 +04:00
|
|
|
die_errno(_("could not read input file '%s'"), file);
|
|
|
|
} else {
|
2016-11-02 20:29:18 +03:00
|
|
|
if (strbuf_read(sb, fileno(stdin), 0) < 0)
|
2014-10-13 22:16:27 +04:00
|
|
|
die_errno(_("could not read from stdin"));
|
|
|
|
}
|
2016-11-02 20:29:18 +03:00
|
|
|
}
|
2014-10-13 22:16:27 +04:00
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
static const char *next_line(const char *str)
|
|
|
|
{
|
|
|
|
const char *nl = strchrnul(str, '\n');
|
|
|
|
return nl + !!*nl;
|
|
|
|
}
|
2014-10-13 22:16:27 +04:00
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
/*
|
|
|
|
* Return the position of the start of the last line. If len is 0, return -1.
|
|
|
|
*/
|
2018-08-23 03:44:38 +03:00
|
|
|
static ssize_t last_line(const char *buf, size_t len)
|
2016-11-02 20:29:18 +03:00
|
|
|
{
|
2018-08-23 03:44:38 +03:00
|
|
|
ssize_t i;
|
2016-11-02 20:29:18 +03:00
|
|
|
if (len == 0)
|
|
|
|
return -1;
|
|
|
|
if (len == 1)
|
|
|
|
return 0;
|
|
|
|
/*
|
|
|
|
* Skip the last character (in addition to the null terminator),
|
|
|
|
* because if the last character is a newline, it is considered as part
|
|
|
|
* of the last line anyway.
|
|
|
|
*/
|
|
|
|
i = len - 2;
|
2014-10-13 22:16:27 +04:00
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
for (; i >= 0; i--) {
|
|
|
|
if (buf[i] == '\n')
|
|
|
|
return i + 1;
|
|
|
|
}
|
|
|
|
return 0;
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2023-10-20 22:01:34 +03:00
|
|
|
* 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.
|
2014-10-13 22:16:27 +04:00
|
|
|
*/
|
2023-10-20 22:01:34 +03:00
|
|
|
static size_t find_end_of_log_message(const char *input, int no_divider)
|
2014-10-13 22:16:27 +04:00
|
|
|
{
|
2023-10-20 22:01:34 +03:00
|
|
|
size_t end;
|
2016-11-02 20:29:18 +03:00
|
|
|
const char *s;
|
2014-10-13 22:16:27 +04:00
|
|
|
|
2023-10-20 22:01:34 +03:00
|
|
|
/* Assume the naive end of the input is already what we want. */
|
|
|
|
end = strlen(input);
|
|
|
|
|
|
|
|
/* Optionally skip over any patch part ("---" line and below). */
|
trailer: fix comment/cut-line regression with opts->no_divider
Commit 97e9d0b78a (trailer: find the end of the log message, 2023-10-20)
combined two code paths for finding the end of the log message. For the
"no_divider" case, we used to use find_trailer_end(), and that has now
been rolled into find_end_of_log_message(). But there's a regression;
that function returns early when no_divider is set, returning the whole
string.
That's not how find_trailer_end() behaved. Although it did skip the
"---" processing (which is what "no_divider" is meant to do), we should
still respect ignored_log_message_bytes(), which covers things like
comments, "commit -v" cut lines, and so on.
The bug is actually in the interpret-trailers command, but the obvious
way to experience it is by running "commit -v" with a "--trailer"
option. The new trailer will be added at the end of the verbose diff,
rather than before it (and consequently will be ignored entirely, since
everything after the diff's intro scissors line is thrown away).
I've added two tests here: one for interpret-trailers directly, which
shows the bug via the parsing routines, and one for "commit -v".
The fix itself is pretty simple: instead of returning early, no_divider
just skips the "---" handling but still calls ignored_log_message_bytes().
Reported-by: Philippe Blain <levraiphilippeblain@gmail.com>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2024-02-20 04:09:36 +03:00
|
|
|
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;
|
|
|
|
}
|
2023-10-20 22:01:34 +03:00
|
|
|
}
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
|
|
|
|
2023-10-20 22:01:34 +03:00
|
|
|
/* Skip over other ignorable bits. */
|
|
|
|
return end - ignored_log_message_bytes(input, end);
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-11-02 20:29:18 +03:00
|
|
|
* Return the position of the first trailer line or len if there are no
|
|
|
|
* trailers.
|
2014-10-13 22:16:27 +04:00
|
|
|
*/
|
2023-10-20 22:01:35 +03:00
|
|
|
static size_t find_trailer_block_start(const char *buf, size_t len)
|
2014-10-13 22:16:27 +04:00
|
|
|
{
|
2016-11-02 20:29:18 +03:00
|
|
|
const char *s;
|
2018-08-23 03:44:38 +03:00
|
|
|
ssize_t end_of_title, l;
|
|
|
|
int only_spaces = 1;
|
2016-10-21 20:55:01 +03:00
|
|
|
int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
|
2016-10-21 20:55:03 +03:00
|
|
|
/*
|
|
|
|
* Number of possible continuation lines encountered. This will be
|
|
|
|
* reset to 0 if we encounter a trailer (since those lines are to be
|
|
|
|
* considered continuations of that trailer), and added to
|
|
|
|
* non_trailer_lines if we encounter a non-trailer (since those lines
|
|
|
|
* are to be considered non-trailers).
|
|
|
|
*/
|
|
|
|
int possible_continuation_lines = 0;
|
2015-08-30 22:14:40 +03:00
|
|
|
|
|
|
|
/* The first paragraph is the title and cannot be trailers */
|
2016-11-02 20:29:18 +03:00
|
|
|
for (s = buf; s < buf + len; s = next_line(s)) {
|
|
|
|
if (s[0] == comment_line_char)
|
2015-08-30 22:14:40 +03:00
|
|
|
continue;
|
2016-11-02 20:29:18 +03:00
|
|
|
if (is_blank_line(s))
|
2015-08-30 22:14:40 +03:00
|
|
|
break;
|
|
|
|
}
|
2016-11-02 20:29:18 +03:00
|
|
|
end_of_title = s - buf;
|
2014-10-13 22:16:27 +04:00
|
|
|
|
|
|
|
/*
|
2016-10-21 20:55:01 +03:00
|
|
|
* Get the start of the trailers by looking starting from the end for a
|
|
|
|
* blank line before a set of non-blank lines that (i) are all
|
|
|
|
* trailers, or (ii) contains at least one Git-generated trailer and
|
|
|
|
* consists of at least 25% trailers.
|
2014-10-13 22:16:27 +04:00
|
|
|
*/
|
2016-11-02 20:29:18 +03:00
|
|
|
for (l = last_line(buf, len);
|
|
|
|
l >= end_of_title;
|
|
|
|
l = last_line(buf, l)) {
|
|
|
|
const char *bol = buf + l;
|
2016-10-21 20:55:01 +03:00
|
|
|
const char **p;
|
2018-08-23 03:44:38 +03:00
|
|
|
ssize_t separator_pos;
|
2016-10-21 20:55:01 +03:00
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
if (bol[0] == comment_line_char) {
|
2016-10-21 20:55:03 +03:00
|
|
|
non_trailer_lines += possible_continuation_lines;
|
|
|
|
possible_continuation_lines = 0;
|
2014-10-13 22:16:27 +04:00
|
|
|
continue;
|
2016-10-21 20:55:03 +03:00
|
|
|
}
|
2016-11-02 20:29:18 +03:00
|
|
|
if (is_blank_line(bol)) {
|
2014-10-13 22:16:27 +04:00
|
|
|
if (only_spaces)
|
|
|
|
continue;
|
2016-10-21 20:55:03 +03:00
|
|
|
non_trailer_lines += possible_continuation_lines;
|
2016-10-21 20:55:01 +03:00
|
|
|
if (recognized_prefix &&
|
|
|
|
trailer_lines * 3 >= non_trailer_lines)
|
2016-11-02 20:29:18 +03:00
|
|
|
return next_line(bol) - buf;
|
|
|
|
else if (trailer_lines && !non_trailer_lines)
|
|
|
|
return next_line(bol) - buf;
|
|
|
|
return len;
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
2016-10-21 20:55:01 +03:00
|
|
|
only_spaces = 0;
|
|
|
|
|
|
|
|
for (p = git_generated_prefixes; *p; p++) {
|
2016-11-02 20:29:18 +03:00
|
|
|
if (starts_with(bol, *p)) {
|
2016-10-21 20:55:01 +03:00
|
|
|
trailer_lines++;
|
2016-10-21 20:55:03 +03:00
|
|
|
possible_continuation_lines = 0;
|
2016-10-21 20:55:01 +03:00
|
|
|
recognized_prefix = 1;
|
|
|
|
goto continue_outer_loop;
|
|
|
|
}
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
2016-10-21 20:55:01 +03:00
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
separator_pos = find_separator(bol, separators);
|
|
|
|
if (separator_pos >= 1 && !isspace(bol[0])) {
|
2016-10-21 20:55:01 +03:00
|
|
|
struct list_head *pos;
|
|
|
|
|
|
|
|
trailer_lines++;
|
2016-10-21 20:55:03 +03:00
|
|
|
possible_continuation_lines = 0;
|
2016-10-21 20:55:01 +03:00
|
|
|
if (recognized_prefix)
|
|
|
|
continue;
|
|
|
|
list_for_each(pos, &conf_head) {
|
|
|
|
struct arg_item *item;
|
|
|
|
item = list_entry(pos, struct arg_item, list);
|
2016-11-02 20:29:18 +03:00
|
|
|
if (token_matches_item(bol, item,
|
2016-10-21 20:55:01 +03:00
|
|
|
separator_pos)) {
|
|
|
|
recognized_prefix = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-11-02 20:29:18 +03:00
|
|
|
} else if (isspace(bol[0]))
|
2016-10-21 20:55:03 +03:00
|
|
|
possible_continuation_lines++;
|
|
|
|
else {
|
2016-10-21 20:55:01 +03:00
|
|
|
non_trailer_lines++;
|
2016-10-21 20:55:03 +03:00
|
|
|
non_trailer_lines += possible_continuation_lines;
|
|
|
|
possible_continuation_lines = 0;
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
2016-10-21 20:55:01 +03:00
|
|
|
continue_outer_loop:
|
|
|
|
;
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
return len;
|
2014-11-09 12:23:42 +03:00
|
|
|
}
|
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
static int ends_with_blank_line(const char *buf, size_t len)
|
2014-10-13 22:16:27 +04:00
|
|
|
{
|
2018-08-23 03:44:38 +03:00
|
|
|
ssize_t ll = last_line(buf, len);
|
2016-11-02 20:29:18 +03:00
|
|
|
if (ll < 0)
|
|
|
|
return 0;
|
|
|
|
return is_blank_line(buf + ll);
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
|
|
|
|
2017-08-15 13:23:29 +03:00
|
|
|
static void unfold_value(struct strbuf *val)
|
|
|
|
{
|
|
|
|
struct strbuf out = STRBUF_INIT;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
strbuf_grow(&out, val->len);
|
|
|
|
i = 0;
|
|
|
|
while (i < val->len) {
|
|
|
|
char c = val->buf[i++];
|
|
|
|
if (c == '\n') {
|
|
|
|
/* Collapse continuation down to a single space. */
|
|
|
|
while (i < val->len && isspace(val->buf[i]))
|
|
|
|
i++;
|
|
|
|
strbuf_addch(&out, ' ');
|
|
|
|
} else {
|
|
|
|
strbuf_addch(&out, c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Empty lines may have left us with whitespace cruft at the edges */
|
|
|
|
strbuf_trim(&out);
|
|
|
|
|
|
|
|
/* output goes back to val as if we modified it in-place */
|
|
|
|
strbuf_swap(&out, val);
|
|
|
|
strbuf_release(&out);
|
|
|
|
}
|
|
|
|
|
2023-09-09 09:16:13 +03:00
|
|
|
/*
|
|
|
|
* 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)
|
2014-10-13 22:16:27 +04:00
|
|
|
{
|
2016-10-21 00:39:48 +03:00
|
|
|
struct strbuf tok = STRBUF_INIT;
|
|
|
|
struct strbuf val = STRBUF_INIT;
|
2018-08-23 03:45:44 +03:00
|
|
|
size_t i;
|
2014-10-13 22:16:27 +04:00
|
|
|
|
2023-09-09 09:16:13 +03:00
|
|
|
trailer_info_get(info, str, opts);
|
2014-10-13 22:16:27 +04:00
|
|
|
|
2023-09-09 09:16:13 +03:00
|
|
|
for (i = 0; i < info->trailer_nr; i++) {
|
2016-10-21 20:55:00 +03:00
|
|
|
int separator_pos;
|
2023-09-09 09:16:13 +03:00
|
|
|
char *trailer = info->trailers[i];
|
2016-11-02 20:29:19 +03:00
|
|
|
if (trailer[0] == comment_line_char)
|
2016-10-21 20:55:03 +03:00
|
|
|
continue;
|
2016-11-02 20:29:19 +03:00
|
|
|
separator_pos = find_separator(trailer, separators);
|
2016-10-21 20:55:00 +03:00
|
|
|
if (separator_pos >= 1) {
|
2016-11-02 20:29:19 +03:00
|
|
|
parse_trailer(&tok, &val, NULL, trailer,
|
2016-10-21 20:55:00 +03:00
|
|
|
separator_pos);
|
2017-08-15 13:23:29 +03:00
|
|
|
if (opts->unfold)
|
|
|
|
unfold_value(&val);
|
2016-11-02 20:29:19 +03:00
|
|
|
add_trailer_item(head,
|
|
|
|
strbuf_detach(&tok, NULL),
|
|
|
|
strbuf_detach(&val, NULL));
|
2017-08-15 13:23:21 +03:00
|
|
|
} else if (!opts->only_trailers) {
|
2016-11-02 20:29:19 +03:00
|
|
|
strbuf_addstr(&val, trailer);
|
2016-10-21 20:55:01 +03:00
|
|
|
strbuf_strip_suffix(&val, "\n");
|
|
|
|
add_trailer_item(head,
|
|
|
|
NULL,
|
|
|
|
strbuf_detach(&val, NULL));
|
2014-11-09 12:23:39 +03:00
|
|
|
}
|
2014-10-13 22:16:27 +04:00
|
|
|
}
|
|
|
|
}
|
2014-10-13 22:16:28 +04:00
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
static void free_all(struct list_head *head)
|
2014-10-13 22:16:28 +04:00
|
|
|
{
|
2016-10-21 00:39:47 +03:00
|
|
|
struct list_head *pos, *p;
|
|
|
|
list_for_each_safe(pos, p, head) {
|
|
|
|
list_del(pos);
|
|
|
|
free_trailer_item(list_entry(pos, struct trailer_item, list));
|
2014-10-13 22:16:28 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
tempfile: auto-allocate tempfiles on heap
The previous commit taught the tempfile code to give up
ownership over tempfiles that have been renamed or deleted.
That makes it possible to use a stack variable like this:
struct tempfile t;
create_tempfile(&t, ...);
...
if (!err)
rename_tempfile(&t, ...);
else
delete_tempfile(&t);
But doing it this way has a high potential for creating
memory errors. The tempfile we pass to create_tempfile()
ends up on a global linked list, and it's not safe for it to
go out of scope until we've called one of those two
deactivation functions.
Imagine that we add an early return from the function that
forgets to call delete_tempfile(). With a static or heap
tempfile variable, the worst case is that the tempfile hangs
around until the program exits (and some functions like
setup_shallow_temporary rely on this intentionally, creating
a tempfile and then leaving it for later cleanup).
But with a stack variable as above, this is a serious memory
error: the variable goes out of scope and may be filled with
garbage by the time the tempfile code looks at it. Let's
see if we can make it harder to get this wrong.
Since many callers need to allocate arbitrary numbers of
tempfiles, we can't rely on static storage as a general
solution. So we need to turn to the heap. We could just ask
all callers to pass us a heap variable, but that puts the
burden on them to call free() at the right time.
Instead, let's have the tempfile code handle the heap
allocation _and_ the deallocation (when the tempfile is
deactivated and removed from the list).
This changes the return value of all of the creation
functions. For the cleanup functions (delete and rename),
we'll add one extra bit of safety: instead of taking a
tempfile pointer, we'll take a pointer-to-pointer and set it
to NULL after freeing the object. This makes it safe to
double-call functions like delete_tempfile(), as the second
call treats the NULL input as a noop. Several callsites
follow this pattern.
The resulting patch does have a fair bit of noise, as each
caller needs to be converted to handle:
1. Storing a pointer instead of the struct itself.
2. Passing the pointer instead of taking the struct
address.
3. Handling a "struct tempfile *" return instead of a file
descriptor.
We could play games to make this less noisy. For example, by
defining the tempfile like this:
struct tempfile {
struct heap_allocated_part_of_tempfile {
int fd;
...etc
} *actual_data;
}
Callers would continue to have a "struct tempfile", and it
would be "active" only when the inner pointer was non-NULL.
But that just makes things more awkward in the long run.
There aren't that many callers, so we can simply bite
the bullet and adjust all of them. And the compiler makes it
easy for us to find them all.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-05 15:15:08 +03:00
|
|
|
static struct tempfile *trailers_tempfile;
|
2016-01-14 19:57:55 +03:00
|
|
|
|
|
|
|
static FILE *create_in_place_tempfile(const char *file)
|
|
|
|
{
|
|
|
|
struct stat st;
|
2018-02-14 21:59:58 +03:00
|
|
|
struct strbuf filename_template = STRBUF_INIT;
|
2016-01-14 19:57:55 +03:00
|
|
|
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, '/');
|
2022-05-02 19:50:37 +03:00
|
|
|
if (tail)
|
2018-02-14 21:59:58 +03:00
|
|
|
strbuf_add(&filename_template, file, tail - file + 1);
|
|
|
|
strbuf_addstr(&filename_template, "git-interpret-trailers-XXXXXX");
|
2016-01-14 19:57:55 +03:00
|
|
|
|
2018-02-14 21:59:58 +03:00
|
|
|
trailers_tempfile = xmks_tempfile_m(filename_template.buf, st.st_mode);
|
|
|
|
strbuf_release(&filename_template);
|
tempfile: auto-allocate tempfiles on heap
The previous commit taught the tempfile code to give up
ownership over tempfiles that have been renamed or deleted.
That makes it possible to use a stack variable like this:
struct tempfile t;
create_tempfile(&t, ...);
...
if (!err)
rename_tempfile(&t, ...);
else
delete_tempfile(&t);
But doing it this way has a high potential for creating
memory errors. The tempfile we pass to create_tempfile()
ends up on a global linked list, and it's not safe for it to
go out of scope until we've called one of those two
deactivation functions.
Imagine that we add an early return from the function that
forgets to call delete_tempfile(). With a static or heap
tempfile variable, the worst case is that the tempfile hangs
around until the program exits (and some functions like
setup_shallow_temporary rely on this intentionally, creating
a tempfile and then leaving it for later cleanup).
But with a stack variable as above, this is a serious memory
error: the variable goes out of scope and may be filled with
garbage by the time the tempfile code looks at it. Let's
see if we can make it harder to get this wrong.
Since many callers need to allocate arbitrary numbers of
tempfiles, we can't rely on static storage as a general
solution. So we need to turn to the heap. We could just ask
all callers to pass us a heap variable, but that puts the
burden on them to call free() at the right time.
Instead, let's have the tempfile code handle the heap
allocation _and_ the deallocation (when the tempfile is
deactivated and removed from the list).
This changes the return value of all of the creation
functions. For the cleanup functions (delete and rename),
we'll add one extra bit of safety: instead of taking a
tempfile pointer, we'll take a pointer-to-pointer and set it
to NULL after freeing the object. This makes it safe to
double-call functions like delete_tempfile(), as the second
call treats the NULL input as a noop. Several callsites
follow this pattern.
The resulting patch does have a fair bit of noise, as each
caller needs to be converted to handle:
1. Storing a pointer instead of the struct itself.
2. Passing the pointer instead of taking the struct
address.
3. Handling a "struct tempfile *" return instead of a file
descriptor.
We could play games to make this less noisy. For example, by
defining the tempfile like this:
struct tempfile {
struct heap_allocated_part_of_tempfile {
int fd;
...etc
} *actual_data;
}
Callers would continue to have a "struct tempfile", and it
would be "active" only when the inner pointer was non-NULL.
But that just makes things more awkward in the long run.
There aren't that many callers, so we can simply bite
the bullet and adjust all of them. And the compiler makes it
easy for us to find them all.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2017-09-05 15:15:08 +03:00
|
|
|
outfile = fdopen_tempfile(trailers_tempfile, "w");
|
2016-01-14 19:57:55 +03:00
|
|
|
if (!outfile)
|
|
|
|
die_errno(_("could not open temporary file"));
|
|
|
|
|
|
|
|
return outfile;
|
|
|
|
}
|
|
|
|
|
2017-08-10 21:03:58 +03:00
|
|
|
void process_trailers(const char *file,
|
|
|
|
const struct process_trailer_options *opts,
|
2017-08-01 12:03:31 +03:00
|
|
|
struct list_head *new_trailer_head)
|
2014-10-13 22:16:28 +04:00
|
|
|
{
|
2016-10-21 00:39:47 +03:00
|
|
|
LIST_HEAD(head);
|
2016-11-02 20:29:18 +03:00
|
|
|
struct strbuf sb = STRBUF_INIT;
|
2023-09-09 09:16:13 +03:00
|
|
|
struct trailer_info info;
|
2016-01-14 19:57:54 +03:00
|
|
|
FILE *outfile = stdout;
|
2014-10-13 22:16:28 +04:00
|
|
|
|
2016-11-02 20:29:19 +03:00
|
|
|
ensure_configured();
|
2014-10-13 22:16:28 +04:00
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
read_input_file(&sb, file);
|
2014-10-13 22:16:28 +04:00
|
|
|
|
2017-08-10 21:03:58 +03:00
|
|
|
if (opts->in_place)
|
2016-01-14 19:57:55 +03:00
|
|
|
outfile = create_in_place_tempfile(file);
|
|
|
|
|
2023-09-09 09:16:13 +03:00
|
|
|
parse_trailers(&info, sb.buf, &head, opts);
|
|
|
|
|
2014-10-13 22:16:28 +04:00
|
|
|
/* Print the lines before the trailers */
|
2023-09-09 09:16:13 +03:00
|
|
|
if (!opts->only_trailers)
|
2023-10-20 22:01:35 +03:00
|
|
|
fwrite(sb.buf, 1, info.trailer_block_start, outfile);
|
2023-09-09 09:16:13 +03:00
|
|
|
|
|
|
|
if (!opts->only_trailers && !info.blank_line_before_trailer)
|
|
|
|
fprintf(outfile, "\n");
|
|
|
|
|
2014-10-13 22:16:28 +04:00
|
|
|
|
2017-08-15 13:23:25 +03:00
|
|
|
if (!opts->only_input) {
|
2023-09-09 09:16:14 +03:00
|
|
|
LIST_HEAD(config_head);
|
2017-08-15 13:23:25 +03:00
|
|
|
LIST_HEAD(arg_head);
|
2023-09-09 09:16:14 +03:00
|
|
|
parse_trailers_from_config(&config_head);
|
|
|
|
parse_trailers_from_command_line_args(&arg_head, new_trailer_head);
|
|
|
|
list_splice(&config_head, &arg_head);
|
2017-08-15 13:23:25 +03:00
|
|
|
process_trailers_lists(&head, &arg_head);
|
|
|
|
}
|
2014-10-13 22:16:28 +04:00
|
|
|
|
2017-08-15 13:23:21 +03:00
|
|
|
print_all(outfile, &head, opts);
|
2014-10-13 22:16:28 +04:00
|
|
|
|
2016-10-21 00:39:47 +03:00
|
|
|
free_all(&head);
|
2023-09-09 09:16:13 +03:00
|
|
|
trailer_info_release(&info);
|
2014-10-13 22:16:28 +04:00
|
|
|
|
|
|
|
/* Print the lines after the trailers as is */
|
2017-08-15 13:23:21 +03:00
|
|
|
if (!opts->only_trailers)
|
2023-10-20 22:01:35 +03:00
|
|
|
fwrite(sb.buf + info.trailer_block_end, 1, sb.len - info.trailer_block_end, outfile);
|
2014-10-13 22:16:28 +04:00
|
|
|
|
2017-08-10 21:03:58 +03:00
|
|
|
if (opts->in_place)
|
2016-01-14 19:57:55 +03:00
|
|
|
if (rename_tempfile(&trailers_tempfile, file))
|
|
|
|
die_errno(_("could not rename temporary file to %s"), file);
|
|
|
|
|
2016-11-02 20:29:18 +03:00
|
|
|
strbuf_release(&sb);
|
2014-10-13 22:16:28 +04:00
|
|
|
}
|
2016-11-02 20:29:19 +03:00
|
|
|
|
2018-08-23 03:46:23 +03:00
|
|
|
void trailer_info_get(struct trailer_info *info, const char *str,
|
|
|
|
const struct process_trailer_options *opts)
|
2016-11-02 20:29:19 +03:00
|
|
|
{
|
2023-10-20 22:01:35 +03:00
|
|
|
size_t end_of_log_message = 0, trailer_block_start = 0;
|
2016-11-02 20:29:19 +03:00
|
|
|
struct strbuf **trailer_lines, **ptr;
|
|
|
|
char **trailer_strings = NULL;
|
|
|
|
size_t nr = 0, alloc = 0;
|
|
|
|
char **last = NULL;
|
|
|
|
|
|
|
|
ensure_configured();
|
|
|
|
|
2023-10-20 22:01:34 +03:00
|
|
|
end_of_log_message = find_end_of_log_message(str, opts->no_divider);
|
2023-10-20 22:01:35 +03:00
|
|
|
trailer_block_start = find_trailer_block_start(str, end_of_log_message);
|
2016-11-02 20:29:19 +03:00
|
|
|
|
2023-10-20 22:01:35 +03:00
|
|
|
trailer_lines = strbuf_split_buf(str + trailer_block_start,
|
|
|
|
end_of_log_message - trailer_block_start,
|
2016-11-02 20:29:19 +03:00
|
|
|
'\n',
|
|
|
|
0);
|
|
|
|
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_addbuf(&sb, *ptr);
|
|
|
|
*last = strbuf_detach(&sb, NULL);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
ALLOC_GROW(trailer_strings, nr + 1, alloc);
|
|
|
|
trailer_strings[nr] = strbuf_detach(*ptr, NULL);
|
|
|
|
last = find_separator(trailer_strings[nr], separators) >= 1
|
|
|
|
? &trailer_strings[nr]
|
|
|
|
: NULL;
|
|
|
|
nr++;
|
|
|
|
}
|
|
|
|
strbuf_list_free(trailer_lines);
|
|
|
|
|
|
|
|
info->blank_line_before_trailer = ends_with_blank_line(str,
|
2023-10-20 22:01:35 +03:00
|
|
|
trailer_block_start);
|
|
|
|
info->trailer_block_start = trailer_block_start;
|
|
|
|
info->trailer_block_end = end_of_log_message;
|
2016-11-02 20:29:19 +03:00
|
|
|
info->trailers = trailer_strings;
|
|
|
|
info->trailer_nr = nr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void trailer_info_release(struct trailer_info *info)
|
|
|
|
{
|
2018-08-23 03:45:44 +03:00
|
|
|
size_t i;
|
2016-11-02 20:29:19 +03:00
|
|
|
for (i = 0; i < info->trailer_nr; i++)
|
|
|
|
free(info->trailers[i]);
|
|
|
|
free(info->trailers);
|
|
|
|
}
|
2017-08-15 13:23:56 +03:00
|
|
|
|
|
|
|
static void format_trailer_info(struct strbuf *out,
|
|
|
|
const struct trailer_info *info,
|
2023-10-20 22:01:35 +03:00
|
|
|
const char *msg,
|
2017-08-15 13:23:56 +03:00
|
|
|
const struct process_trailer_options *opts)
|
|
|
|
{
|
2019-01-29 00:33:37 +03:00
|
|
|
size_t origlen = out->len;
|
2018-08-23 03:45:44 +03:00
|
|
|
size_t i;
|
2017-08-15 13:25:27 +03:00
|
|
|
|
|
|
|
/* If we want the whole block untouched, we can take the fast path. */
|
2020-12-09 18:52:06 +03:00
|
|
|
if (!opts->only_trailers && !opts->unfold && !opts->filter &&
|
2020-12-09 18:52:08 +03:00
|
|
|
!opts->separator && !opts->key_only && !opts->value_only &&
|
|
|
|
!opts->key_value_separator) {
|
2023-10-20 22:01:35 +03:00
|
|
|
strbuf_add(out, msg + info->trailer_block_start,
|
|
|
|
info->trailer_block_end - info->trailer_block_start);
|
2017-08-15 13:25:27 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < info->trailer_nr; i++) {
|
|
|
|
char *trailer = info->trailers[i];
|
2018-08-23 03:44:38 +03:00
|
|
|
ssize_t separator_pos = find_separator(trailer, separators);
|
2017-08-15 13:25:27 +03:00
|
|
|
|
|
|
|
if (separator_pos >= 1) {
|
|
|
|
struct strbuf tok = STRBUF_INIT;
|
|
|
|
struct strbuf val = STRBUF_INIT;
|
|
|
|
|
|
|
|
parse_trailer(&tok, &val, NULL, trailer, separator_pos);
|
2019-01-29 00:33:34 +03:00
|
|
|
if (!opts->filter || opts->filter(&tok, opts->filter_data)) {
|
|
|
|
if (opts->unfold)
|
|
|
|
unfold_value(&val);
|
2019-01-29 00:33:37 +03:00
|
|
|
|
|
|
|
if (opts->separator && out->len != origlen)
|
|
|
|
strbuf_addbuf(out, opts->separator);
|
2019-01-29 00:33:35 +03:00
|
|
|
if (!opts->value_only)
|
2020-12-09 18:52:07 +03:00
|
|
|
strbuf_addbuf(out, &tok);
|
2020-12-09 18:52:08 +03:00
|
|
|
if (!opts->key_only && !opts->value_only) {
|
|
|
|
if (opts->key_value_separator)
|
|
|
|
strbuf_addbuf(out, opts->key_value_separator);
|
|
|
|
else
|
|
|
|
strbuf_addstr(out, ": ");
|
|
|
|
}
|
2020-12-09 18:52:07 +03:00
|
|
|
if (!opts->key_only)
|
|
|
|
strbuf_addbuf(out, &val);
|
2019-01-29 00:33:37 +03:00
|
|
|
if (!opts->separator)
|
|
|
|
strbuf_addch(out, '\n');
|
2019-01-29 00:33:34 +03:00
|
|
|
}
|
2017-08-15 13:25:27 +03:00
|
|
|
strbuf_release(&tok);
|
|
|
|
strbuf_release(&val);
|
|
|
|
|
|
|
|
} else if (!opts->only_trailers) {
|
2019-01-29 00:33:37 +03:00
|
|
|
if (opts->separator && out->len != origlen) {
|
|
|
|
strbuf_addbuf(out, opts->separator);
|
|
|
|
}
|
2017-08-15 13:25:27 +03:00
|
|
|
strbuf_addstr(out, trailer);
|
2019-01-29 00:33:37 +03:00
|
|
|
if (opts->separator) {
|
|
|
|
strbuf_rtrim(out);
|
|
|
|
}
|
2017-08-15 13:25:27 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-15 13:23:56 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void format_trailers_from_commit(struct strbuf *out, const char *msg,
|
|
|
|
const struct process_trailer_options *opts)
|
|
|
|
{
|
|
|
|
struct trailer_info info;
|
|
|
|
|
2018-08-23 03:46:23 +03:00
|
|
|
trailer_info_get(&info, msg, opts);
|
2023-10-20 22:01:35 +03:00
|
|
|
format_trailer_info(out, &info, msg, opts);
|
2017-08-15 13:23:56 +03:00
|
|
|
trailer_info_release(&info);
|
|
|
|
}
|
2020-09-27 11:40:01 +03:00
|
|
|
|
|
|
|
void trailer_iterator_init(struct trailer_iterator *iter, const char *msg)
|
|
|
|
{
|
|
|
|
struct process_trailer_options opts = PROCESS_TRAILER_OPTIONS_INIT;
|
|
|
|
strbuf_init(&iter->key, 0);
|
|
|
|
strbuf_init(&iter->val, 0);
|
|
|
|
opts.no_divider = 1;
|
2023-09-09 09:16:12 +03:00
|
|
|
trailer_info_get(&iter->internal.info, msg, &opts);
|
|
|
|
iter->internal.cur = 0;
|
2020-09-27 11:40:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int trailer_iterator_advance(struct trailer_iterator *iter)
|
|
|
|
{
|
2023-09-09 09:16:12 +03:00
|
|
|
while (iter->internal.cur < iter->internal.info.trailer_nr) {
|
|
|
|
char *trailer = iter->internal.info.trailers[iter->internal.cur++];
|
2020-09-27 11:40:01 +03:00
|
|
|
int separator_pos = find_separator(trailer, separators);
|
|
|
|
|
|
|
|
if (separator_pos < 1)
|
|
|
|
continue; /* not a real trailer */
|
|
|
|
|
|
|
|
strbuf_reset(&iter->key);
|
|
|
|
strbuf_reset(&iter->val);
|
|
|
|
parse_trailer(&iter->key, &iter->val, NULL,
|
|
|
|
trailer, separator_pos);
|
|
|
|
unfold_value(&iter->val);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void trailer_iterator_release(struct trailer_iterator *iter)
|
|
|
|
{
|
2023-09-09 09:16:12 +03:00
|
|
|
trailer_info_release(&iter->internal.info);
|
2020-09-27 11:40:01 +03:00
|
|
|
strbuf_release(&iter->val);
|
|
|
|
strbuf_release(&iter->key);
|
|
|
|
}
|