rsh.con commit teach git-status about spaces in filenames (33bb218)
   1#include "rsh.h"
   2
   3#include <string.h>
   4#include <sys/types.h>
   5#include <sys/socket.h>
   6
   7#include "cache.h"
   8
   9#define COMMAND_SIZE 4096
  10
  11/*
  12 * Write a shell-quoted version of a string into a buffer, and
  13 * return bytes that ought to be output excluding final null.
  14 */
  15static int shell_quote(char *buf, int nmax, const char *str)
  16{
  17        char ch;
  18        int nq;
  19        int oc = 0;
  20
  21        while ( (ch = *str++) ) {
  22                nq = 0;
  23                if ( strchr(" !\"#$%&\'()*;<=>?[\\]^`{|}", ch) )
  24                        nq = 1;
  25
  26                if ( nq ) {
  27                        if ( nmax > 1 ) {
  28                                *buf++ = '\\';
  29                                nmax--;
  30                        }
  31                        oc++;
  32                }
  33
  34                if ( nmax > 1 ) {
  35                        *buf++ = ch;
  36                        nmax--;
  37                }
  38                oc++;
  39        }
  40
  41        if ( nmax )
  42                *buf = '\0';
  43
  44        return oc;
  45}
  46                        
  47/*
  48 * Append a string to a string buffer, with or without quoting.  Return true
  49 * if the buffer overflowed.
  50 */
  51static int add_to_string(char **ptrp, int *sizep, const char *str, int quote)
  52{
  53        char *p = *ptrp;
  54        int size = *sizep;
  55        int oc;
  56        int err = 0;
  57
  58        if ( quote ) {
  59                oc = shell_quote(p, size, str);
  60        } else {
  61                oc = strlen(str);
  62                memcpy(p, str, (oc >= size) ? size-1 : oc);
  63        }
  64
  65        if ( oc >= size ) {
  66                err = 1;
  67                oc = size-1;
  68        }
  69
  70        *ptrp  += oc;
  71        **ptrp  = '\0';
  72        *sizep -= oc;
  73        return err;
  74}
  75
  76int setup_connection(int *fd_in, int *fd_out, const char *remote_prog, 
  77                     char *url, int rmt_argc, char **rmt_argv)
  78{
  79        char *host;
  80        char *path;
  81        int sv[2];
  82        char command[COMMAND_SIZE];
  83        char *posn;
  84        int sizen;
  85        int of;
  86        int i;
  87
  88        if (!strcmp(url, "-")) {
  89                *fd_in = 0;
  90                *fd_out = 1;
  91                return 0;
  92        }
  93
  94        host = strstr(url, "//");
  95        if (host) {
  96                host += 2;
  97                path = strchr(host, '/');
  98        } else {
  99                host = url;
 100                path = strchr(host, ':');
 101                if (path)
 102                        *(path++) = '\0';
 103        }
 104        if (!path) {
 105                return error("Bad URL: %s", url);
 106        }
 107        /* $GIT_RSH <host> "env GIR_DIR=<path> <remote_prog> <args...>" */
 108        sizen = COMMAND_SIZE;
 109        posn = command;
 110        of = 0;
 111        of |= add_to_string(&posn, &sizen, "env ", 0);
 112        of |= add_to_string(&posn, &sizen, GIT_DIR_ENVIRONMENT, 0);
 113        of |= add_to_string(&posn, &sizen, "=", 0);
 114        of |= add_to_string(&posn, &sizen, path, 1);
 115        of |= add_to_string(&posn, &sizen, " ", 0);
 116        of |= add_to_string(&posn, &sizen, remote_prog, 1);
 117
 118        for ( i = 0 ; i < rmt_argc ; i++ ) {
 119                of |= add_to_string(&posn, &sizen, " ", 0);
 120                of |= add_to_string(&posn, &sizen, rmt_argv[i], 1);
 121        }
 122
 123        of |= add_to_string(&posn, &sizen, " -", 0);
 124
 125        if ( of )
 126                return error("Command line too long");
 127
 128        if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
 129                return error("Couldn't create socket");
 130
 131        if (!fork()) {
 132                const char *ssh, *ssh_basename;
 133                ssh = getenv("GIT_SSH");
 134                if (!ssh) ssh = "ssh";
 135                ssh_basename = strrchr(ssh, '/');
 136                if (!ssh_basename)
 137                        ssh_basename = ssh;
 138                else
 139                        ssh_basename++;
 140                close(sv[1]);
 141                dup2(sv[0], 0);
 142                dup2(sv[0], 1);
 143                execlp(ssh, ssh_basename, host, command, NULL);
 144        }
 145        close(sv[0]);
 146        *fd_in = sv[1];
 147        *fd_out = sv[1];
 148        return 0;
 149}