merge(third_party/git): Merge squashed git subtree at v2.23.0
Merge commit '1b593e1ea4' as 'third_party/git'
This commit is contained in:
commit
7ef0d62730
3629 changed files with 1139935 additions and 0 deletions
202
third_party/git/builtin/remote-ext.c
vendored
Normal file
202
third_party/git/builtin/remote-ext.c
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
#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 argv_array *out, const char *arg, const char *service)
|
||||
{
|
||||
while (*arg) {
|
||||
char *expanded = strip_escapes(arg, service, &arg);
|
||||
if (expanded)
|
||||
argv_array_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]);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue