3 #include "run-command.h"
 
   7  *      'command [arg1 [arg2 [...]]]'   Invoke command with given arguments.
 
   9  *      '% ': Literal space in argument.
 
  10  *      '%%': Literal percent sign.
 
  11  *      '%S': Name of service (git-upload-pack/git-upload-archive/
 
  13  *      '%s': Same as \s, but with possible git- prefix stripped.
 
  14  *      '%G': Only allowed as first 'character' of argument. Do not pass this
 
  15  *              Argument to command, instead send this as name of repository
 
  16  *              in in-line git://-style request (also activates sending this
 
  18  *      '%V': Only allowed as first 'character' of argument. Used in
 
  19  *              conjunction with '%G': Do not pass this argument to command,
 
  20  *              instead send this as vhost in git://-style request (note: does
 
  21  *              not activate sending git:// style request).
 
  25 static char *git_req_vhost;
 
  27 static char *strip_escapes(const char *str, const char *service,
 
  34         struct strbuf ret = STRBUF_INIT;
 
  36         /* Calculate prefix length for \s and lengths for \s and \S */
 
  37         if (!strncmp(service, "git-", 4))
 
  40         /* Pass the service to command. */
 
  41         setenv("GIT_EXT_SERVICE", service, 1);
 
  42         setenv("GIT_EXT_SERVICE_NOPREFIX", service + psoff, 1);
 
  44         /* Scan the length of argument. */
 
  45         while (str[rpos] && (escape || str[rpos] != ' ')) {
 
  58                                 /* Fall-through to error. */
 
  60                                 die("Bad remote-ext placeholder '%%%c'.",
 
  65                         escape = (str[rpos] == '%');
 
  68         if (escape && !str[rpos])
 
  69                 die("remote-ext command has incomplete placeholder");
 
  72                 ++*next;        /* Skip over space */
 
  75          * Do the actual placeholder substitution. The string will be short
 
  76          * enough not to overflow integers.
 
  78         rpos = special ? 2 : 0;         /* Skip first 2 bytes in specials. */
 
  80         while (str[rpos] && (escape || str[rpos] != ' ')) {
 
  85                                 strbuf_addch(&ret, str[rpos]);
 
  88                                 strbuf_addstr(&ret, service + psoff);
 
  91                                 strbuf_addstr(&ret, service);
 
 101                                 strbuf_addch(&ret, str[rpos]);
 
 108                 git_req = strbuf_detach(&ret, NULL);
 
 111                 git_req_vhost = strbuf_detach(&ret, NULL);
 
 114                 return strbuf_detach(&ret, NULL);
 
 118 /* Should be enough... */
 
 119 #define MAXARGUMENTS 256
 
 121 static const char **parse_argv(const char *arg, const char *service)
 
 126         char *temparray[MAXARGUMENTS + 1];
 
 130                 if (arguments == MAXARGUMENTS)
 
 131                         die("remote-ext command has too many arguments");
 
 132                 expanded = strip_escapes(arg, service, &arg);
 
 134                         temparray[arguments++] = expanded;
 
 137         ret = xmalloc((arguments + 1) * sizeof(char *));
 
 138         for (i = 0; i < arguments; i++)
 
 139                 ret[i] = temparray[i];
 
 140         ret[arguments] = NULL;
 
 144 static void send_git_request(int stdin_fd, const char *serv, const char *repo,
 
 152          * Request needs 12 bytes extra if there is vhost (xxxx \0host=\0) and
 
 153          * 6 bytes extra (xxxx \0) if there is no vhost.
 
 156                 bufferspace = strlen(serv) + strlen(repo) + strlen(vhost) + 12;
 
 158                 bufferspace = strlen(serv) + strlen(repo) + 6;
 
 160         if (bufferspace > 0xFFFF)
 
 161                 die("Request too large to send");
 
 162         buffer = xmalloc(bufferspace);
 
 164         /* Make the packet. */
 
 165         wpos = sprintf(buffer, "%04x%s %s%c", (unsigned)bufferspace,
 
 168         /* Add vhost if any. */
 
 170                 sprintf(buffer + wpos, "host=%s%c", vhost, 0);
 
 172         /* Send the request */
 
 173         if (write_in_full(stdin_fd, buffer, bufferspace) < 0)
 
 174                 die_errno("Failed to send request");
 
 179 static int run_child(const char *arg, const char *service)
 
 182         struct child_process child;
 
 184         memset(&child, 0, sizeof(child));
 
 188         child.argv = parse_argv(arg, service);
 
 190         if (start_command(&child) < 0)
 
 191                 die("Can't run specified command");
 
 194                 send_git_request(child.in, service, git_req, git_req_vhost);
 
 196         r = bidirectional_transfer_loop(child.out, child.in);
 
 198                 r = finish_command(&child);
 
 200                 finish_command(&child);
 
 204 #define MAXCOMMAND 4096
 
 206 static int command_loop(const char *child)
 
 208         char buffer[MAXCOMMAND];
 
 212                 if (!fgets(buffer, MAXCOMMAND - 1, stdin)) {
 
 214                                 die("Comammand input error");
 
 217                 /* Strip end of line characters. */
 
 219                 while (i > 0 && isspace(buffer[i - 1]))
 
 222                 if (!strcmp(buffer, "capabilities")) {
 
 223                         printf("*connect\n\n");
 
 225                 } else if (!strncmp(buffer, "connect ", 8)) {
 
 228                         return run_child(child, buffer + 8);
 
 230                         fprintf(stderr, "Bad command");
 
 236 int cmd_remote_ext(int argc, const char **argv, const char *prefix)
 
 239                 die("Expected two arguments");
 
 241         return command_loop(argv[2]);