tvl-depot/third_party/git/builtin/remote-ext.c
Vincent Ambo f4609b896f merge(3p/git): Merge git subtree at v2.29.2
This also bumps the stable nixpkgs to 20.09 as of 2020-11-21, because
there is some breakage in the git build related to the netrc
credentials helper which someone has taken care of in nixpkgs.

The stable channel is not used for anything other than git, so this
should be fine.

Change-Id: I3575a19dab09e1e9556cf8231d717de9890484fb
2020-11-21 19:45:56 +01:00

202 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]);
}