22f9b7f3f5
We eventually want to drop the argv_array name and just use strvec consistently. There's no particular reason we have to do it all at once, or care about interactions between converted and unconverted bits. Because of our preprocessor compat layer, the names are interchangeable to the compiler (so even a definition and declaration using different names is OK). This patch converts all of the files in builtin/ to keep the diff to a manageable size. The conversion was done purely mechanically with: git ls-files '*.c' '*.h' | xargs perl -i -pe ' s/ARGV_ARRAY/STRVEC/g; s/argv_array/strvec/g; ' and then selectively staging files with "git add builtin/". We'll deal with any indentation/style fallouts separately. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
203 lines
4.5 KiB
C
203 lines
4.5 KiB
C
#include "builtin.h"
|
|
#include "transport.h"
|
|
#include "run-command.h"
|
|
#include "pkt-line.h"
|
|
|
|
static const char usage_msg[] =
|
|
"git remote-ext <remote> <url>";
|
|
|
|
/*
|
|
* URL syntax:
|
|
* 'command [arg1 [arg2 [...]]]' Invoke command with given arguments.
|
|
* Special characters:
|
|
* '% ': Literal space in argument.
|
|
* '%%': Literal percent sign.
|
|
* '%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).
|
|
*/
|
|
|
|
static char *git_req;
|
|
static char *git_req_vhost;
|
|
|
|
static char *strip_escapes(const char *str, const char *service,
|
|
const char **next)
|
|
{
|
|
size_t rpos = 0;
|
|
int escape = 0;
|
|
char special = 0;
|
|
const char *service_noprefix = service;
|
|
struct strbuf ret = STRBUF_INIT;
|
|
|
|
skip_prefix(service_noprefix, "git-", &service_noprefix);
|
|
|
|
/* Pass the service to command. */
|
|
setenv("GIT_EXT_SERVICE", service, 1);
|
|
setenv("GIT_EXT_SERVICE_NOPREFIX", service_noprefix, 1);
|
|
|
|
/* Scan the length of argument. */
|
|
while (str[rpos] && (escape || str[rpos] != ' ')) {
|
|
if (escape) {
|
|
switch (str[rpos]) {
|
|
case ' ':
|
|
case '%':
|
|
case 's':
|
|
case 'S':
|
|
break;
|
|
case 'G':
|
|
case 'V':
|
|
special = str[rpos];
|
|
if (rpos == 1)
|
|
break;
|
|
/* fallthrough */
|
|
default:
|
|
die("Bad remote-ext placeholder '%%%c'.",
|
|
str[rpos]);
|
|
}
|
|
escape = 0;
|
|
} else
|
|
escape = (str[rpos] == '%');
|
|
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.
|
|
*/
|
|
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 '%':
|
|
strbuf_addch(&ret, str[rpos]);
|
|
break;
|
|
case 's':
|
|
strbuf_addstr(&ret, service_noprefix);
|
|
break;
|
|
case 'S':
|
|
strbuf_addstr(&ret, service);
|
|
break;
|
|
}
|
|
escape = 0;
|
|
} else
|
|
switch (str[rpos]) {
|
|
case '%':
|
|
escape = 1;
|
|
break;
|
|
default:
|
|
strbuf_addch(&ret, str[rpos]);
|
|
break;
|
|
}
|
|
rpos++;
|
|
}
|
|
switch (special) {
|
|
case 'G':
|
|
git_req = strbuf_detach(&ret, NULL);
|
|
return NULL;
|
|
case 'V':
|
|
git_req_vhost = strbuf_detach(&ret, NULL);
|
|
return NULL;
|
|
default:
|
|
return strbuf_detach(&ret, NULL);
|
|
}
|
|
}
|
|
|
|
static void parse_argv(struct strvec *out, const char *arg, const char *service)
|
|
{
|
|
while (*arg) {
|
|
char *expanded = strip_escapes(arg, service, &arg);
|
|
if (expanded)
|
|
strvec_push(out, expanded);
|
|
free(expanded);
|
|
}
|
|
}
|
|
|
|
static void send_git_request(int stdin_fd, const char *serv, const char *repo,
|
|
const char *vhost)
|
|
{
|
|
if (!vhost)
|
|
packet_write_fmt(stdin_fd, "%s %s%c", serv, repo, 0);
|
|
else
|
|
packet_write_fmt(stdin_fd, "%s %s%chost=%s%c", serv, repo, 0,
|
|
vhost, 0);
|
|
}
|
|
|
|
static int run_child(const char *arg, const char *service)
|
|
{
|
|
int r;
|
|
struct child_process child = CHILD_PROCESS_INIT;
|
|
|
|
child.in = -1;
|
|
child.out = -1;
|
|
child.err = 0;
|
|
parse_argv(&child.args, 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) {
|
|
size_t i;
|
|
if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
|
|
if (ferror(stdin))
|
|
die("Command input error");
|
|
exit(0);
|
|
}
|
|
/* Strip end of line characters. */
|
|
i = strlen(buffer);
|
|
while (i > 0 && isspace(buffer[i - 1]))
|
|
buffer[--i] = 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)
|
|
usage(usage_msg);
|
|
|
|
return command_loop(argv[2]);
|
|
}
|