trailer: execute command from 'trailer.<name>.command'

Let the user specify a command that will give on its standard output the value to use for the specified trailer. Signed-off-by: Christian Couder <chriscool@tuxfamily.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Christian Couder committed Oct 13, 2014 at 20:16 UTC 85039fb6e4e7123958a40a22ef40e25bcb29f9c0
1 file changed +84 -1
trailer.c
+84 -1
@@ -1,5 +1,7 @@
1 #include "cache.h"
2 #include "string-list.h"
3 +#include "run-command.h"
4 +#include "string-list.h"
5 #include "trailer.h"
6 /*
7 * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
@@ -33,6 +35,8 @@ static struct trailer_item *first_conf_item;
35
36 static char *separators = ":";
37
38 +#define TRAILER_ARG_STRING "$ARG"
39 +
40 static int after_or_end(enum action_where where)
41 {
42 return (where == WHERE_AFTER) || (where == WHERE_END);
@@ -78,6 +82,13 @@ static inline int contains_only_spaces(const char *str)
82 return !*s;
83 }
84
85 +static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)
86 +{
87 + const char *ptr = strstr(sb->buf, a);
88 + if (ptr)
89 + strbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));
90 +}
91 +
92 static void free_trailer_item(struct trailer_item *item)
93 {
94 free(item->conf.name);
@@ -203,6 +214,63 @@ static struct trailer_item *remove_first(struct trailer_item **first)
214 return item;
215 }
216
217 +static int read_from_command(struct child_process *cp, struct strbuf *buf)
218 +{
219 + if (run_command(cp))
220 + return error("running trailer command '%s' failed", cp->argv[0]);
221 + if (strbuf_read(buf, cp->out, 1024) < 1)
222 + return error("reading from trailer command '%s' failed", cp->argv[0]);
223 + strbuf_trim(buf);
224 + return 0;
225 +}
226 +
227 +static const char *apply_command(const char *command, const char *arg)
228 +{
229 + struct strbuf cmd = STRBUF_INIT;
230 + struct strbuf buf = STRBUF_INIT;
231 + struct child_process cp;
232 + const char *argv[] = {NULL, NULL};
233 + const char *result;
234 +
235 + strbuf_addstr(&cmd, command);
236 + if (arg)
237 + strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
238 +
239 + argv[0] = cmd.buf;
240 + memset(&cp, 0, sizeof(cp));
241 + cp.argv = argv;
242 + cp.env = local_repo_env;
243 + cp.no_stdin = 1;
244 + cp.out = -1;
245 + cp.use_shell = 1;
246 +
247 + if (read_from_command(&cp, &buf)) {
248 + strbuf_release(&buf);
249 + result = xstrdup("");
250 + } else
251 + result = strbuf_detach(&buf, NULL);
252 +
253 + strbuf_release(&cmd);
254 + return result;
255 +}
256 +
257 +static void apply_item_command(struct trailer_item *in_tok, struct trailer_item *arg_tok)
258 +{
259 + if (arg_tok->conf.command) {
260 + const char *arg;
261 + if (arg_tok->value && arg_tok->value[0]) {
262 + arg = arg_tok->value;
263 + } else {
264 + if (in_tok && in_tok->value)
265 + arg = xstrdup(in_tok->value);
266 + else
267 + arg = xstrdup("");
268 + }
269 + arg_tok->value = apply_command(arg_tok->conf.command, arg);
270 + free((char *)arg);
271 + }
272 +}
273 +
274 static void apply_arg_if_exists(struct trailer_item *in_tok,
275 struct trailer_item *arg_tok,
276 struct trailer_item *on_tok,
@@ -214,16 +282,19 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,
282 free_trailer_item(arg_tok);
283 break;
284 case EXISTS_REPLACE:
285 + apply_item_command(in_tok, arg_tok);
286 add_arg_to_input_list(on_tok, arg_tok,
287 in_tok_first, in_tok_last);
288 remove_from_list(in_tok, in_tok_first, in_tok_last);
289 free_trailer_item(in_tok);
290 break;
291 case EXISTS_ADD:
292 + apply_item_command(in_tok, arg_tok);
293 add_arg_to_input_list(on_tok, arg_tok,
294 in_tok_first, in_tok_last);
295 break;
296 case EXISTS_ADD_IF_DIFFERENT:
297 + apply_item_command(in_tok, arg_tok);
298 if (check_if_different(in_tok, arg_tok, 1))
299 add_arg_to_input_list(on_tok, arg_tok,
300 in_tok_first, in_tok_last);
@@ -231,6 +302,7 @@ static void apply_arg_if_exists(struct trailer_item *in_tok,
302 free_trailer_item(arg_tok);
303 break;
304 case EXISTS_ADD_IF_DIFFERENT_NEIGHBOR:
305 + apply_item_command(in_tok, arg_tok);
306 if (check_if_different(on_tok, arg_tok, 0))
307 add_arg_to_input_list(on_tok, arg_tok,
308 in_tok_first, in_tok_last);
@@ -254,6 +326,7 @@ static void apply_arg_if_missing(struct trailer_item **in_tok_first,
326 case MISSING_ADD:
327 where = arg_tok->conf.where;
328 in_tok = after_or_end(where) ? in_tok_last : in_tok_first;
329 + apply_item_command(NULL, arg_tok);
330 if (*in_tok) {
331 add_arg_to_input_list(*in_tok, arg_tok,
332 in_tok_first, in_tok_last);
@@ -537,7 +610,7 @@ static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,
610 char *tok, char *val)
611 {
612 struct trailer_item *new = xcalloc(sizeof(*new), 1);
540 - new->value = val;
613 + new->value = val ? val : xstrdup("");
614
615 if (conf_item) {
616 duplicate_conf(&new->conf, &conf_item->conf);
@@ -604,7 +677,17 @@ static struct trailer_item *process_command_line_args(struct string_list *traile
677 struct trailer_item *arg_tok_first = NULL;
678 struct trailer_item *arg_tok_last = NULL;
679 struct string_list_item *tr;
680 + struct trailer_item *item;
681 +
682 + /* Add a trailer item for each configured trailer with a command */
683 + for (item = first_conf_item; item; item = item->next) {
684 + if (item->conf.command) {
685 + struct trailer_item *new = new_trailer_item(item, NULL, NULL);
686 + add_trailer_item(&arg_tok_first, &arg_tok_last, new);
687 + }
688 + }
689
690 + /* Add a trailer item for each trailer on the command line */
691 for_each_string_list_item(tr, trailers) {
692 struct trailer_item *new = create_trailer_item(tr->string);
693 add_trailer_item(&arg_tok_first, &arg_tok_last, new);