Continue where ae021d87 (use skip_prefix to avoid magic numbers) left off
and use skip_prefix() in more places for determining the lengths of prefix
strings to avoid using dependent constants and other indirect methods.
Signed-off-by: Rene Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
		
	
		
			
				
	
	
		
			240 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#include "builtin.h"
 | 
						|
#include "transport.h"
 | 
						|
#include "run-command.h"
 | 
						|
 | 
						|
/*
 | 
						|
 * 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;
 | 
						|
				/* Fall-through to error. */
 | 
						|
			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);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/* Should be enough... */
 | 
						|
#define MAXARGUMENTS 256
 | 
						|
 | 
						|
static const char **parse_argv(const char *arg, const char *service)
 | 
						|
{
 | 
						|
	int arguments = 0;
 | 
						|
	int i;
 | 
						|
	const char **ret;
 | 
						|
	char *temparray[MAXARGUMENTS + 1];
 | 
						|
 | 
						|
	while (*arg) {
 | 
						|
		char *expanded;
 | 
						|
		if (arguments == MAXARGUMENTS)
 | 
						|
			die("remote-ext command has too many arguments");
 | 
						|
		expanded = strip_escapes(arg, service, &arg);
 | 
						|
		if (expanded)
 | 
						|
			temparray[arguments++] = expanded;
 | 
						|
	}
 | 
						|
 | 
						|
	ret = xmalloc((arguments + 1) * sizeof(char *));
 | 
						|
	for (i = 0; i < arguments; i++)
 | 
						|
		ret[i] = temparray[i];
 | 
						|
	ret[arguments] = NULL;
 | 
						|
	return 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;
 | 
						|
	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);
 | 
						|
 | 
						|
	/* Make the packet. */
 | 
						|
	wpos = sprintf(buffer, "%04x%s %s%c", (unsigned)bufferspace,
 | 
						|
		serv, repo, 0);
 | 
						|
 | 
						|
	/* Add vhost if any. */
 | 
						|
	if (vhost)
 | 
						|
		sprintf(buffer + wpos, "host=%s%c", vhost, 0);
 | 
						|
 | 
						|
	/* Send the request */
 | 
						|
	if (write_in_full(stdin_fd, buffer, bufferspace) < 0)
 | 
						|
		die_errno("Failed to send request");
 | 
						|
 | 
						|
	free(buffer);
 | 
						|
}
 | 
						|
 | 
						|
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;
 | 
						|
	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) {
 | 
						|
		size_t i;
 | 
						|
		if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
 | 
						|
			if (ferror(stdin))
 | 
						|
				die("Comammand 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)
 | 
						|
		die("Expected two arguments");
 | 
						|
 | 
						|
	return command_loop(argv[2]);
 | 
						|
}
 |