Invokes specified command and directs smart transport streams to its
stdin/stdout. Handy for e.g. invoking ssh with some one-off parameters.
---
Makefile | 1 +
builtin.h | 1 +
builtin/remote-ext.c | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++
git.c | 1 +
4 files changed, 303 insertions(+), 0 deletions(-)
create mode 100644 builtin/remote-ext.c
diff --git a/Makefile b/Makefile
index ad53b52..88e752f 100644
--- a/Makefile
+++ b/Makefile
@@ -702,6 +702,7 @@ BUILTIN_OBJS += builtin/read-tree.o
BUILTIN_OBJS += builtin/receive-pack.o
BUILTIN_OBJS += builtin/reflog.o
BUILTIN_OBJS += builtin/remote.o
+BUILTIN_OBJS += builtin/remote-ext.o
BUILTIN_OBJS += builtin/remote-fd.o
BUILTIN_OBJS += builtin/replace.o
BUILTIN_OBJS += builtin/rerere.o
diff --git a/builtin.h b/builtin.h
index af60b28..eb9074d 100644
--- a/builtin.h
+++ b/builtin.h
@@ -139,5 +139,6 @@ extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
extern int cmd_replace(int argc, const char **argv, const char *prefix);
extern int cmd_remote_fd(int argc, const char **argv, const char *prefix);
+extern int cmd_remote_ext(int argc, const char **argv, const char *prefix);
#endif
diff --git a/builtin/remote-ext.c b/builtin/remote-ext.c
new file mode 100644
index 0000000..e9852ca
--- /dev/null
+++ b/builtin/remote-ext.c
@@ -0,0 +1,300 @@
+#include "git-compat-util.h"
+#include "transport.h"
+#include "run-command.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+/*
+ * URL syntax:
+ * 'command [arg1 [arg2 [...]]]' Invoke command with given arguments.
+ * Special characters:
+ * '\ ': Literal space in argument.
+ * '\\': Literal backslash.
+ * '\S': Name of service (git-upload-pack/git-upload-archive/
+ * git-receive-pack.
+ * '\s': Same as \s, but with possible git- prefix stripped.
+ * '\G': Only allowed as first 'character' of argument. Do not pass this
+ * Argument to command, instead send this as name of repository
+ * in in-line git://-style request (also activates sending this
+ * style of request).
+ * '\V': Only allowed as first 'character' of argument. Used in
+ * conjunction with '\G': Do not pass this argument to command,
+ * instead send this as vhost in git://-style request (note: does
+ * not activate sending git:// style request).
+ */
+
+char* git_req = NULL;
+char* git_req_vhost = NULL;
+
+static char *strip_escapes(const char *str, const char *service,
+ const char **next)
+{
+ char *ret;
+ size_t rpos = 0;
+ size_t wpos = 0;
+ size_t finallen = 0;
+ int escape = 0;
+ char special = 0;
+ size_t pslen = 0;
+ size_t pSlen = 0;
+ size_t psoff = 0;
+
+ /* Calculate prefix length for \s and lengths for \s and \S */
+ if (!strncmp(service, "git-", 4))
+ psoff = 4;
+ pSlen = strlen(service);
+ pslen = pSlen - psoff;
+
+ /* Calculate output length and start of next argument. */
+ while (str[rpos] && (escape || str[rpos] != ' ')) {
+ if (escape) {
+ switch (str[rpos]) {
+ case ' ':
+ case '\\':
+ finallen++;
+ break;
+ case 's':
+ finallen += pslen;
+ break;
+ case 'S':
+ finallen += pSlen;
+ break;
+ case 'G':
+ case 'V':
+ special = str[rpos];
+ if (rpos == 1)
+ break;
+ /* Fall-through to error. */
+ default:
+ die("Bad remote-ext placeholder '\\%c'.",
+ str[rpos]);
+ }
+ escape = 0;
+ } else
+ switch (str[rpos]) {
+ case '\\':
+ escape = 1;
+ break;
+ default:
+ finallen++;
+ break;
+ }
+ rpos++;
+ }
+ if (escape && !str[rpos])
+ die("remote-ext command has incomplete placeholder");
+ *next = str + rpos;
+ if (**next == ' ')
+ ++*next; /* Skip over space */
+
+ /*
+ * Do the actual placeholder substitution. The string will be short
+ * enough not to overflow integers.
+ */
+ ret = xmalloc(finallen + 1);
+ rpos = special ? 2 : 0; /* Skip first 2 bytes in specials. */
+ escape = 0;
+ while (str[rpos] && (escape || str[rpos] != ' ')) {
+ if (escape) {
+ switch(str[rpos]) {
+ case ' ':
+ case '\\':
+ ret[wpos++] = str[rpos];
+ break;
+ case 's':
+ strcpy(ret + wpos, service + psoff);
+ wpos += pslen;
+ break;
+ case 'S':
+ strcpy(ret + wpos, service);
+ wpos += pSlen;
+ break;
+ }
+ escape = 0;
+ } else
+ switch(str[rpos]) {
+ case '\\':
+ escape = 1;
+ break;
+ default:
+ ret[wpos++] = str[rpos];
+ break;
+ }
+ rpos++;
+ }
+ ret[wpos] = 0;
+ switch(special) {
+ case 'G':
+ git_req = ret;
+ return NULL;
+ case 'V':
+ git_req_vhost = ret;
+ return NULL;
+ default:
+ return ret;
+ }
+}
+
+/* Should be enough... */
+#define MAXARGUMENTS 256
+
+static const char **parse_argv(const char *arg, const char *service)
+{
+ int arguments = 0;
+ int i;
+ char** ret;
+ char *(temparray[MAXARGUMENTS + 1]);
+
+ while (*arg) {
+ char* ret;
+ if (arguments == MAXARGUMENTS)
+ die("remote-ext command has too many arguments");
+ ret = strip_escapes(arg, service, &arg);
+ if (ret)
+ temparray[arguments++] = ret;
+ }
+
+ ret = xcalloc(arguments + 1, sizeof(char*));
+ for (i = 0; i < arguments; i++)
+ ret[i] = temparray[i];
+
+ return (const char**)ret;
+}
+
+static void send_git_request(int stdin_fd, const char *serv, const char *repo,
+ const char *vhost)
+{
+ size_t bufferspace;
+ size_t wpos = 0;
+ size_t spos = 0;
+ size_t tmp;
+ char* buffer;
+
+ /*
+ * Request needs 12 bytes extra if there is vhost (xxxx \0host=\0) and
+ * 6 bytes extra (xxxx \0) if there is no vhost.
+ */
+ if (vhost)
+ bufferspace = strlen(serv) + strlen(repo) + strlen(vhost) + 12;
+ else
+ bufferspace = strlen(serv) + strlen(repo) + 6;
+
+ if (bufferspace > 0xFFFF)
+ die("Request too large to send");
+ buffer = xmalloc(bufferspace);
+
+ /* Packet length. */
+ sprintf(buffer + wpos, "%04x", (unsigned)bufferspace);
+ wpos += 4;
+
+ /* Service. */
+ tmp = strlen(serv);
+ memcpy(buffer + wpos, serv, tmp);
+ wpos += tmp;
+
+ /* Space. */
+ buffer[wpos++] = ' ';
+
+ /* Repo. */
+ tmp = strlen(repo);
+ memcpy(buffer + wpos, repo, tmp);
+ wpos += tmp;
+
+ /* NUL. */
+ buffer[wpos++] = '\0';
+
+ /* Vhost if any. */
+ if (vhost) {
+ /* Header name. */
+ strcpy(buffer + wpos, "host=");
+ wpos += 5;
+
+ /* Actual vhost */
+ tmp = strlen(vhost);
+ memcpy(buffer + wpos, vhost, tmp);
+ wpos += tmp;
+
+ /* NUL. */
+ buffer[wpos++] = '\0';
+ }
+
+ /* Send the request */
+ while (spos < wpos) {
+ ssize_t r;
+ r = write(stdin_fd, buffer + spos, wpos - spos);
+ if (r < 0 && errno != EINTR && errno != EAGAIN &&
+ errno != EWOULDBLOCK)
+ die_errno("Failed to send request");
+ else if (r < 0)
+ continue; /* Try again. */
+ else
+ spos += r;
+ }
+
+ free(buffer);
+}
+
+static int run_child(const char *arg, const char *service)
+{
+ int r;
+ struct child_process child;
+
+ memset(&child, 0, sizeof(child));
+ child.in = -1;
+ child.out = -1;
+ child.err = 0;
+ child.argv = parse_argv(arg, service);
+
+ if (start_command(&child) < 0)
+ die("Can't run specified command");
+
+ if (git_req)
+ send_git_request(child.in, service, git_req, git_req_vhost);
+
+ r = bidirectional_transfer_loop(child.out, child.in);
+ if (!r)
+ r = finish_command(&child);
+ else
+ finish_command(&child);
+ return r;
+}
+
+#define MAXCOMMAND 4096
+
+static int command_loop(const char *child)
+{
+ char buffer[MAXCOMMAND];
+
+ while (1) {
+ if (!fgets(buffer, MAXCOMMAND - 1, stdin))
+ exit(0);
+ //Strip end of line characters.
+ while (isspace((unsigned char)buffer[strlen(buffer) - 1]))
+ buffer[strlen(buffer) - 1] = 0;
+
+ if (!strcmp(buffer, "capabilities")) {
+ printf("*connect\n\n");
+ fflush(stdout);
+ } else if (!strncmp(buffer, "connect ", 8)) {
+ printf("\n");
+ fflush(stdout);
+ return run_child(child, buffer + 8);
+ } else {
+ fprintf(stderr, "Bad command");
+ return 1;
+ }
+ }
+}
+
+int cmd_remote_ext(int argc, const char **argv, const char *prefix)
+{
+ if (argc < 3) {
+ fprintf(stderr, "Error: URL missing");
+ exit(1);
+ }
+
+ return command_loop(argv[2]);
+}
diff --git a/git.c b/git.c
index 91de3d6..2cc0826 100644
--- a/git.c
+++ b/git.c
@@ -368,6 +368,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
+ { "remote-ext", cmd_remote_ext, 0 },
{ "remote-fd", cmd_remote_fd, 0 },
{ "replace", cmd_replace, RUN_SETUP },
{ "repo-config", cmd_config },
--
1.7.1.335.g93705.dirty
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html