Subject: [PATCH 1/5] alias: add functions to do param substitution and alias running

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Fri, 24 Dec 2010 21:07:45 +0700

These functions help substitute %foo% in an alias command to real
values, then run the alias and return the first line from stdout.

The normal use case is:

    /* extract all %xxx% from cmd to params */
    extract_alias_params(cmd, params);
    param = lookup_alias_param(params, "%foo%");
    if (param)
        param->value = "value for %foo%";
    param = lookup_alias_param(params, "%bar%");
    if (param)
        param->value = "value for %bar%";
    /* substitute %foo% and %bar% */
    expand_alias_params(cmd, params);
    free_alias_params(params);
    if (!get_alias_oneline(alias, cmd, output))
        /* do something with output->buf here */

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 alias.c |  139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 cache.h |   11 +++++
 2 files changed, 150 insertions(+), 0 deletions(-)

diff --git a/alias.c b/alias.c
index eb9f08b..6626bb0 100644
--- a/alias.c
+++ b/alias.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "run-command.h"
 
 static const char *alias_key;
 static char *alias_val;
@@ -85,3 +86,141 @@ int split_cmdline(char *cmdline, const char ***argv)
 const char *split_cmdline_strerror(int split_cmdline_errno) {
 	return split_cmdline_errors[-split_cmdline_errno-1];
 }
+
+int extract_alias_params(const char *cmd, struct alias_param **params)
+{
+	const char *s = cmd;
+	int nr_param = 0;
+
+	*params = NULL;
+	while (s && (s = strchr(s, '%')) != NULL) {
+		int len = strcspn(s+1, "% ")+2;
+		if (len < 2 || s[len-1] != '%')
+			return error("malformed parameter at %s", s);
+		nr_param++;
+		*params = xrealloc(*params, sizeof(struct alias_param)*nr_param);
+		(*params)[nr_param-1].param = xstrndup(s, len);
+		(*params)[nr_param-1].pos = s - cmd;
+		(*params)[nr_param-1].value = NULL;
+		s += len;
+	}
+
+	nr_param++;
+	*params = xrealloc(*params, sizeof(struct alias_param)*nr_param);
+	(*params)[nr_param-1].param = NULL;
+	(*params)[nr_param-1].value = NULL;
+	return 0;
+}
+
+struct alias_param *lookup_alias_param(struct alias_param *params, const char *param)
+{
+	int i;
+	for (i = 0; params[i].param; i++)
+		if (!strcmp(params[i].param, param))
+			return params+i;
+	return NULL;
+}
+
+int expand_alias_params(struct strbuf *cmd, const struct alias_param *params)
+{
+	int i, offset = 0;
+
+	/* TODO: quote for '!' commands */
+	for (i = 0; params[i].param; i++) {
+		if (!params[i].value)
+			return error("param %s not substituted", params[i].param);
+		strbuf_splice(cmd,
+			      params[i].pos + offset, strlen(params[i].param),
+			      params[i].value, strlen(params[i].value));
+		offset += strlen(params[i].value) - strlen(params[i].param);
+	}
+
+	return 0;
+}
+
+void free_alias_params(struct alias_param *params)
+{
+	int i;
+	for (i = 0; params[i].param; i++) {
+		free(params[i].param);
+		free(params[i].value);
+	}
+	free(params);
+}
+
+static void *wait_and_finish(void *arg)
+{
+	struct child_process *cp = arg;
+	char buf[1024];
+	while (xread(cp->out, buf, 1024) > 0)
+		;
+	close(cp->out);
+	finish_command(cp);
+	free(cp->argv);
+	free(cp);
+	return (void *) (intptr_t) 0;
+}
+
+static int start_support_alias(const char *alias, char *cmd, struct child_process **cpp)
+{
+	struct child_process *cp;
+	const char **argv;
+	int count;
+
+	cp = xmalloc(sizeof(struct child_process));
+	memset(cp, 0, sizeof(struct child_process));
+	cp->in = 0;
+	cp->out = -1;
+
+	if (cmd[0] == '!') {
+		argv = xmalloc(sizeof(*argv)*4);
+		argv[0] = "/bin/sh";
+		argv[1] = "-c";
+		argv[2] = cmd+1;
+		argv[3] = NULL;
+	}
+	else {
+		count = split_cmdline(cmd, &argv);
+		if (count < 0) {
+			free(cp);
+			return error("Bad alias %s: %s", cmd, split_cmdline_strerror(count));
+		}
+		cp->git_cmd = 1;
+	}
+
+	cp->argv = argv;
+	if (start_command(cp)) {
+		error("Failed to run %s", cmd);
+		free(cp);
+		free(argv);
+		return -1;
+	}
+	*cpp = cp;
+	return 0;
+}
+
+int get_alias_oneline(const char *alias, char *cmd, struct strbuf *ref)
+{
+	struct child_process *cp;
+	FILE *fp;
+	int ret;
+
+	ret = start_support_alias(alias, cmd, &cp);
+	if (ret)
+		return ret;
+
+	fp = fdopen(cp->out, "r");
+	ret = strbuf_getline(ref, fp, '\n');
+	if (!ret) {
+		/* let it finish, if there's error, users should know */
+#ifdef NO_PTHREADS
+		wait_and_finish(cp);
+#else
+		pthread_t thread;
+		pthread_create(&thread, NULL, wait_and_finish, cp);
+#endif
+		return 0;
+	}
+	wait_and_finish(cp);
+	return -1;
+}
diff --git a/cache.h b/cache.h
index e83bc2d..20a37ff 100644
--- a/cache.h
+++ b/cache.h
@@ -1113,10 +1113,21 @@ extern int ws_blank_line(const char *line, int len, unsigned ws_rule);
 int report_path_error(const char *ps_matched, const char **pathspec, int prefix_offset);
 void overlay_tree_on_cache(const char *tree_name, const char *prefix);
 
+struct alias_param {
+	char *param;
+	int pos;
+	char *value;
+};
+
 char *alias_lookup(const char *alias);
 int split_cmdline(char *cmdline, const char ***argv);
 /* Takes a negative value returned by split_cmdline */
 const char *split_cmdline_strerror(int cmdline_errno);
+int extract_alias_params(const char *cmd, struct alias_param **params);
+struct alias_param *lookup_alias_param(struct alias_param *params, const char *param);
+int expand_alias_params(struct strbuf *cmd, const struct alias_param *params);
+void free_alias_params(struct alias_param *params);
+int get_alias_oneline(const char *alias, char *cmd, struct strbuf *result);
 
 /* git.c */
 struct startup_info {
-- 
1.7.3.3.476.g10a82