@@ -77,6 +77,29 @@ void run_hooks_opt_clear(struct run_hooks_opt *o)
strvec_clear(&o->args);
}
+int pipe_from_string_list(struct strbuf *pipe, void *pp_cb, void *pp_task_cb)
+{
+ int *item_idx;
+ struct hook *ctx = pp_task_cb;
+ struct hook_cb_data *hook_cb = pp_cb;
+ struct string_list *to_pipe = hook_cb->options->feed_pipe_ctx;
+
+ /* Bootstrap the state manager if necessary. */
+ if (!ctx->feed_pipe_cb_data) {
+ ctx->feed_pipe_cb_data = xmalloc(sizeof(unsigned int));
+ *(int*)ctx->feed_pipe_cb_data = 0;
+ }
+
+ item_idx = ctx->feed_pipe_cb_data;
+
+ if (*item_idx < to_pipe->nr) {
+ strbuf_addf(pipe, "%s\n", to_pipe->items[*item_idx].string);
+ (*item_idx)++;
+ return 0;
+ }
+ return 1;
+}
+
static int pick_next_hook(struct child_process *cp,
struct strbuf *out,
void *pp_cb,
@@ -90,6 +113,10 @@ static int pick_next_hook(struct child_process *cp,
if (hook_cb->options->path_to_stdin) {
cp->no_stdin = 0;
cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
+ } else if (hook_cb->options->feed_pipe) {
+ /* ask for start_command() to make a pipe for us */
+ cp->in = -1;
+ cp->no_stdin = 0;
} else {
cp->no_stdin = 1;
}
@@ -164,7 +191,7 @@ int run_found_hooks(const char *hook_name, const char *hook_path,
run_processes_parallel_tr2(options->jobs,
pick_next_hook,
notify_start_failure,
- NULL,
+ options->feed_pipe,
notify_hook_finished,
&cb_data,
"hook",
@@ -182,6 +209,9 @@ int run_hooks(const char *hook_name, struct run_hooks_opt *options)
if (!options)
BUG("a struct run_hooks_opt must be provided to run_hooks");
+ if (options->path_to_stdin && options->feed_pipe)
+ BUG("choose only one method to populate stdin");
+
hook_path = find_hook(hook_name);
/*
@@ -19,6 +19,12 @@ int hook_exists(const char *hookname);
struct hook {
/* The path to the hook */
const char *hook_path;
+
+ /*
+ * Use this to keep state for your feed_pipe_fn if you are using
+ * run_hooks_opt.feed_pipe. Otherwise, do not touch it.
+ */
+ void *feed_pipe_cb_data;
};
struct run_hooks_opt
@@ -47,6 +53,19 @@ struct run_hooks_opt
/* Path to file which should be piped to stdin for each hook */
const char *path_to_stdin;
+
+ /*
+ * Callback and state pointer to ask for more content to pipe to stdin.
+ * Will be called repeatedly, for each hook. See
+ * hook.c:pipe_from_stdin() for an example. Keep per-hook state in
+ * hook.feed_pipe_cb_data (per process). Keep initialization context in
+ * feed_pipe_ctx (shared by all processes).
+ *
+ * See 'pipe_from_string_list()' for info about how to specify a
+ * string_list as the stdin input instead of writing your own handler.
+ */
+ feed_pipe_fn feed_pipe;
+ void *feed_pipe_ctx;
};
#define RUN_HOOKS_OPT_INIT { \
@@ -55,6 +74,14 @@ struct run_hooks_opt
.args = STRVEC_INIT, \
}
+/*
+ * To specify a 'struct string_list', set 'run_hooks_opt.feed_pipe_ctx' to the
+ * string_list and set 'run_hooks_opt.feed_pipe' to 'pipe_from_string_list()'.
+ * This will pipe each string in the list to stdin, separated by newlines. (Do
+ * not inject your own newlines.)
+ */
+int pipe_from_string_list(struct strbuf *pipe, void *pp_cb, void *pp_task_cb);
+
struct hook_cb_data {
/* rc reflects the cumulative failure state */
int rc;