{"thread":{"id":"36089","subject":"[PATCH v7 09/11] trailer: execute command from 'trailer.<name>.command'","startedAt":"2014-03-06T22:14:06Z","lastAt":"2014-03-06T22:14:06Z","messageCount":1,"participants":["Christian Couder"],"isPatch":true,"patchVersion":7,"patchTotal":11},"messages":[{"id":"236241","messageId":"20140306221409.29648.45677.chriscool@tuxfamily.org","threadId":"36089","inReplyTo":null,"subject":"[PATCH v7 09/11] trailer: execute command from 'trailer.<name>.command'","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2014-03-06T22:14:06Z","receivedAt":"2014-03-06T22:14:06Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Let the user specify a command that will give on its standard output\nthe value to use for the specified trailer.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n trailer.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 63 insertions(+)\n\ndiff --git a/trailer.c b/trailer.c\nindex cc87918..97a0fe7 100644\n--- a/trailer.c\n+++ b/trailer.c\n@@ -1,4 +1,5 @@\n #include \"cache.h\"\n+#include \"run-command.h\"\n #include \"trailer.h\"\n /*\n  * Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>\n@@ -13,11 +14,14 @@ struct conf_info {\n \tchar *name;\n \tchar *key;\n \tchar *command;\n+\tunsigned command_uses_arg : 1;\n \tenum action_where where;\n \tenum action_if_exists if_exists;\n \tenum action_if_missing if_missing;\n };\n \n+#define TRAILER_ARG_STRING \"$ARG\"\n+\n struct trailer_item {\n \tstruct trailer_item *previous;\n \tstruct trailer_item *next;\n@@ -59,6 +63,13 @@ static inline int contains_only_spaces(const char *str)\n \treturn !*s;\n }\n \n+static inline void strbuf_replace(struct strbuf *sb, const char *a, const char *b)\n+{\n+\tconst char *ptr = strstr(sb->buf, a);\n+\tif (ptr)\n+\t\tstrbuf_splice(sb, ptr - sb->buf, strlen(a), b, strlen(b));\n+}\n+\n static void free_trailer_item(struct trailer_item *item)\n {\n \tfree(item->conf.name);\n@@ -389,6 +400,7 @@ static int git_trailer_config(const char *conf_key, const char *value, void *cb)\n \t\t\tif (conf->command)\n \t\t\t\twarning(_(\"more than one %s\"), orig_conf_key);\n \t\t\tconf->command = xstrdup(value);\n+\t\t\tconf->command_uses_arg = !!strstr(conf->command, TRAILER_ARG_STRING);\n \t\t\tbreak;\n \t\tcase TRAILER_WHERE:\n \t\t\tif (set_where(conf, value))\n@@ -423,6 +435,44 @@ static void parse_trailer(struct strbuf *tok, struct strbuf *val, const char *tr\n \t}\n }\n \n+static int read_from_command(struct child_process *cp, struct strbuf *buf)\n+{\n+\tif (run_command(cp))\n+\t\treturn error(\"running trailer command '%s' failed\", cp->argv[0]);\n+\tif (strbuf_read(buf, cp->out, 1024) < 1)\n+\t\treturn error(\"reading from trailer command '%s' failed\", cp->argv[0]);\n+\tstrbuf_trim(buf);\n+\treturn 0;\n+}\n+\n+static const char *apply_command(const char *command, const char *arg)\n+{\n+\tstruct strbuf cmd = STRBUF_INIT;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct child_process cp;\n+\tconst char *argv[] = {NULL, NULL};\n+\tconst char *result = \"\";\n+\n+\tstrbuf_addstr(&cmd, command);\n+\tif (arg)\n+\t\tstrbuf_replace(&cmd, TRAILER_ARG_STRING, arg);\n+\n+\targv[0] = cmd.buf;\n+\tmemset(&cp, 0, sizeof(cp));\n+\tcp.argv = argv;\n+\tcp.env = local_repo_env;\n+\tcp.no_stdin = 1;\n+\tcp.out = -1;\n+\tcp.use_shell = 1;\n+\n+\tif (read_from_command(&cp, &buf))\n+\t\tstrbuf_release(&buf);\n+\telse\n+\t\tresult = strbuf_detach(&buf, NULL);\n+\n+\tstrbuf_release(&cmd);\n+\treturn result;\n+}\n \n static void duplicate_conf(struct conf_info *dst, struct conf_info *src)\n {\n@@ -445,6 +495,10 @@ static struct trailer_item *new_trailer_item(struct trailer_item *conf_item,\n \t\tduplicate_conf(&new->conf, &conf_item->conf);\n \t\tnew->token = xstrdup(conf_item->conf.key);\n \t\tfree(tok);\n+\t\tif (conf_item->conf.command_uses_arg || !val) {\n+\t\t\tnew->value = apply_command(conf_item->conf.command, val);\n+\t\t\tfree(val);\n+\t\t}\n \t} else\n \t\tnew->token = tok;\n \n@@ -497,12 +551,21 @@ static struct trailer_item *process_command_line_args(int argc, const char **arg\n \tint i;\n \tstruct trailer_item *arg_tok_first = NULL;\n \tstruct trailer_item *arg_tok_last = NULL;\n+\tstruct trailer_item *item;\n \n \tfor (i = 0; i < argc; i++) {\n \t\tstruct trailer_item *new = create_trailer_item(argv[i]);\n \t\tadd_trailer_item(&arg_tok_first, &arg_tok_last, new);\n \t}\n \n+\t/* Add conf commands that don't use $ARG */\n+\tfor (item = first_conf_item; item; item = item->next) {\n+\t\tif (item->conf.command && !item->conf.command_uses_arg) {\n+\t\t\tstruct trailer_item *new = new_trailer_item(item, NULL, NULL);\n+\t\t\tadd_trailer_item(&arg_tok_first, &arg_tok_last, new);\n+\t\t}\n+\t}\n+\n \treturn arg_tok_first;\n }\n \n-- \n1.8.5.2.204.gcfe299d.dirty\n"}]}