#ifndef	lint
static char sccsid[] = "@(#)helper_rcv.c 1.18 89/08/22";
#endif

/*
 * Copyright (c) 1989 by Sun Microsystems, Inc. 
 */ 

#include "cbratp.h"
#include <sys/dir.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/stat.h>
#ifndef NOUNIXSOCKETS
#include <sys/un.h>
#include <strings.h>
#endif
#include <errno.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <pwd.h>
#include <stdio.h>
#include <signal.h>
#include <sys/param.h>

fd_set stopped_fds;
fd_set lseek_fds;
short firstfd;
short server_version;

extern int news_pid;
extern char *getenv();

static struct  hosts_entry {
    char inet_num[20];
    char name[30];
} hosts_cache[10];
static int next = 0;

#ifdef	DEBUG
extern long bytes_in;
extern long bytes_out;
extern long escapes_in;
extern long escapes_out;
#endif	/*DEBUG*/

rcv_control(c)
    register u_char c;
{
    u_char *b;
    u_char *p;
    int	s;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "control\n");
#endif	DEBUG

    if (chan[c]->len == 0) {
	free((char *) chan[c]->buf);
	return 0;
    }
    chan[c]->len = 0;
    p = b = chan[c]->buf;
    enqueue(c, (u_char *) 0, 0);
    wait_for(c);
#ifdef	DEBUG
    if (rs232_debug) {
	(void) fprintf(rs232_debugfile, "in %d (%d escaped)\n",
						    bytes_in, escapes_in);
	(void) fprintf(rs232_debugfile, "out %d (%d escaped)\n",
						    bytes_out, escapes_out);
    }
#endif	DEBUG
    drain();

    switch (*b++) {
    case QUIT_CMD:
	/*(void) lseek (0, -16L, 0);	/* will logout */
	exit(0);
    case CRC_CMD:
	crc_mode = *b;
	break;
    case SPEED_CMD:
	s = (int) decode_long(b);
	(void) set_baudrate(s);
	break;
    }

    free((char *) p);
    return 1;
}

rcv_open(c)
    register u_char c;
{
    u_char buffer[6];
    register u_char *b = buffer;
    int	flags;
    int	mode;
    int fd;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "open(%s)\n",chan[c]->buf);
#endif	DEBUG

    flags = (int) decode_long(chan[c]->buf + chan[c]->len - 8);
    mode = (int) decode_long(chan[c]->buf + chan[c]->len - 4);

    if ((fd = open((char *) chan[c]->buf, flags, mode)) < 0) {
err_ret:
	enqueue(c, (u_char *) 0, 0);
	free((char *) chan[c]->buf);
	return 0;
    }

    if (fcntl(fd, F_SETFD, 1) < 0) {
	(void) close(fd);
	goto err_ret;
    }

    if ((flags & O_WRONLY) == 0) {
	*b++ = fd;
	if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
	    (void) close(fd);
	    goto err_ret;
	}
	*b = new_channel();
	rd_chan[fd] = *b;
	alloc_channel(*b++, READ_CHN, (u_char) fd); 
	*b++ = READ_CHN;
	if (fd >= maximum_fd)
	    maximum_fd = fd + 1;
	FD_SET(fd, &read_fds); 
#ifdef	DEBUG
	if (rs232_debug)
	    (void) fprintf(rs232_debugfile, "open(%s),fd=%d,chan=%d\n",
						chan[c]->buf, fd, rd_chan[fd]);
#endif	DEBUG
    }

    if ((flags & O_WRONLY) || (flags & O_RDWR)) {
	*b++ = fd;
	*b = new_channel();
	wr_chan[fd] = *b;
	alloc_channel(*b++, WRITE_CHN, (u_char) fd); 
	*b++ = WRITE_CHN;
#ifdef	DEBUG
	if (rs232_debug)
	    (void) fprintf(rs232_debugfile, "open(%s),fd=%d,chan=%d\n",
						chan[c]->buf, fd, wr_chan[fd]);
#endif	DEBUG
    }

    enqueue(c, buffer, b - buffer);
    free((char *) chan[c]->buf);
    return 0;
}

rcv_close(c)
    u_char c;
{
    register int fd = *chan[c]->buf;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "close(%d)\n",fd);
#endif	DEBUG

    if (close(fd) < 0)
	enqueue(c, &c, 1); /* probably should be errno */
    else {
	FD_CLR(fd, &read_fds); 
	FD_CLR(fd, &stopped_fds); 
	FD_CLR(fd, &lseek_fds); 
	if (rd_chan[fd]) {
	    free_channel(rd_chan[fd]);
	    rd_chan[fd] = 0;
	}
	if (wr_chan[fd]) {
	    free_channel(wr_chan[fd]);
	    wr_chan[fd] = 0;
	}
	enqueue(c, (u_char *) 0, 0);
    }

    free((char *) chan[c]->buf);
    return 0;
}

rcv_lseek(c)
    register u_char c;
{
    u_char buffer[4];
    register u_char *b = buffer;
    off_t pos;
    extern off_t lseek();

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "lseek\n");
#endif	DEBUG

    if ((pos = lseek((int) chan[c]->buf[0],
	    (off_t) decode_long(chan[c]->buf + 2), (int) chan[c]->buf[1])) < 0)
	enqueue(c, (u_char *) 0, 0);
    else {
	b = encode_long(b, (long) pos);
	enqueue(c, buffer, b - buffer);
    }

    FD_SET(chan[c]->buf[0], &lseek_fds);
    free((char *) chan[c]->buf);
    return 0;
}

rcv_filesize(c)
    register u_char c;
{
    int fd = *chan[c]->buf;
    struct stat statbuf;
    u_char buffer[4];
    register u_char *b = buffer;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "filesize(%d)\n",fd);
#endif	DEBUG

    if (fstat(fd, &statbuf) < 0)
	enqueue(c, (u_char *) 0, 0);
    else {
	b = encode_long(b, (long) statbuf.st_size);
	enqueue(c, buffer, b - buffer);
    }

    free((char *) chan[c]->buf);
    return 0;
}

rcv_opencon(c)
    register u_char c;
{
    u_char buffer[6];
    register u_char *b = buffer;
    register u_char *p = chan[c]->buf;
    int         con = 0;
    int         lis = 0;
    register	fd = -1;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "opencon(%s)\n", p);
#endif	DEBUG

    if (*p == 'l')
        lis++, p++;
    else if (*p == 'c')
        con++, p++;
#ifndef NOUNIXSOCKETS
    if (*p == '/') {
        /* This is a Unix domain socket */
        struct sockaddr_un unsock;
        int         oldUmask;
        int         namelen = strlen((char *) p);
 
        unsock.sun_family = AF_UNIX;
        /*
         * Alas,  the length of the pathname for a Unix domain socket is
         * limited by the fact that it must fit into the generic struct
         * sockaddr........
         */
        if (namelen >= sizeof(unsock.sun_path))
            goto bad_socket;
        (void) strcpy(unsock.sun_path, (char *) p);
        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
            goto bad_socket;
        }
        if (con) {
            if (connect(fd, (struct sockaddr *) & unsock, namelen + 2))
                goto bad_socket;
            if (fcntl(fd, F_SETFL, FNDELAY) == -1)
                perror("socket fcntl"); /* XXX - this is a BUG! */

	    *b++ = fd;
	    *b = new_channel();
	    rd_chan[fd] = *b;
	    alloc_channel(*b++, READ_CHN, (u_char) fd); 
	    if (fd >= maximum_fd)
		maximum_fd = fd + 1;
	    FD_SET(fd, &read_fds); 
	    *b = new_channel();
	    wr_chan[fd] = *b;
	    alloc_channel(*b++, WRITE_CHN, (u_char) fd); 
	    enqueue(c, buffer, b - buffer);
#ifdef	DEBUG
	    if (rs232_debug)
		(void) fprintf(rs232_debugfile,
				"opencon(%s), fd=%d, r/wchan=%d/%d\n",
				chan[c]->buf, fd, rd_chan[fd], wr_chan[fd]);
#endif	DEBUG
        }
        else {
            /* Create a Unix domain listener */
            register char *q = rindex((char *) p, '/');
                      
            /* HACK ALERT */
            /*
             * the following is a hack to get around a bug in bind(2)
             * for AF_UNIX sockets.  since bind(2) returns EADDRINUSE
             * if the rendevous file exists, *not* if its actually
             * in use, we try to find out here whether its really
             * in use or not
             */
            if (access((char *) p, F_OK) == 0) {
                int     err, sock;
                char    foo[1];
             
                sock = socket(AF_UNIX, SOCK_DGRAM, 0);
                err = sendto(sock, foo, 1, 0, (struct sockaddr *) &unsock,
                        sizeof(struct sockaddr_un));
                if (err)        {
                    switch(errno)       {
                    case        ECONNREFUSED:
                        /* Unlink any old left-over socket */
                        (void) unlink(unsock.sun_path);
                        (void) close(sock);
                        break;
                    case        EPROTOTYPE:
                    default:
                        (void) close(sock);
                        /* fake error to look like what we want */
                        errno = EADDRINUSE;
                        goto bad_socket;
                    }
                }
            }

            if (q) {  
                /* Try to make each directory along the path */
                register char *r = index((char *) p + 1, '/');
                      
                oldUmask = umask(0);
                while (r && r <= q) {
                    *q = '\0';
                    *r = '\0';
                    (void) mkdir((char *) p, 0777);
                    *r = '/';
                    *q = '/';
                    r = index(r + 1, '/');
                }
                (void) umask(oldUmask);
            }    
            /* Bind the socket into the file system */
            if (bind(fd, (struct sockaddr *) & unsock, namelen + 2))
                goto bad_socket;
            if (listen(fd, 100))
                goto bad_socket;
            if (fcntl(fd, F_SETOWN, news_pid) < 0)
                perror("fcntl: open_socket setown");

	    *b++ = fd;
	    *b = new_channel();
	    rd_chan[fd] = *b;
	    alloc_channel(*b++, READ_CHN, (u_char) fd); 
	    if (fd >= maximum_fd)
		maximum_fd = fd + 1;
	    FD_SET(fd, &read_fds); 
	    enqueue(c, buffer, b - buffer);
        }
    }
    else
#endif
    if (*p && (fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) > 0) {
	struct sockaddr_in addr;
	char        *hostname;
	long        atol();

	/* Must be a TCP port */
	bzero((char *)&addr, sizeof(addr));
	addr.sin_family = AF_INET;
        if ((addr.sin_port = htons((unsigned short) atol((char *) p,
						    &hostname, 10))) != 0) {
            if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)0, 0) < 0)
                perror("setsockopt");

#ifdef TCPNODELAY             
              if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &mi, sizeof(int)))

                perror("setsockopt: NO-DELAY");
#endif
            if (con) {
                struct  hostent *hp;

                if (*hostname == '\0')
                    hostname = "localhost";
                if (!isalpha(*hostname))    /* skip separator
					     * character */
                    hostname++;
                hp = gethostbyname(hostname);
                if (hp == NULL)
                    goto bad_socket;
                bcopy((char *) hp->h_addr, (char *) &addr.sin_addr, (int) hp->h_length);
                if (addr.sin_port == 0 || connect(fd, (struct sockaddr *) &addr, sizeof addr))
                    goto bad_socket;
                if (fcntl(fd, F_SETFL, FNDELAY) == -1)
                    perror("socket fcntl");	/* XXX - this is a BUG! */

		*b++ = fd;
		*b = new_channel();
		rd_chan[fd] = *b;
		alloc_channel(*b++, READ_CHN, (u_char) fd); 
		if (fd >= maximum_fd)
		    maximum_fd = fd + 1;
		FD_SET(fd, &read_fds); 
		*b = new_channel();
		wr_chan[fd] = *b;
		alloc_channel(*b++, WRITE_CHN, (u_char) fd);
		enqueue(c, buffer, b - buffer);
#ifdef	DEBUG
		if (rs232_debug)
		    (void) fprintf(rs232_debugfile,
				    "opencon(%s), fd=%d, r/wchan=%d/%d\n",
				    chan[c]->buf, fd, rd_chan[fd], wr_chan[fd]);
#endif	DEBUG
            }
            else {
                addr.sin_addr.s_addr = INADDR_ANY;
                if (bind(fd, (struct sockaddr *) &addr, sizeof addr) < 0
                            || lis && listen(fd, 100) < 0)
                    goto bad_socket;
		if (fcntl(fd, F_SETOWN, news_pid) < 0)
                    perror("fcntl: open_socket setown");

		*b++ = fd;
		*b = new_channel();
		rd_chan[fd] = *b;
		alloc_channel(*b++, READ_CHN, (u_char) fd); 
		if (fd >= maximum_fd)
		    maximum_fd = fd + 1;
		FD_SET(fd, &read_fds); 
		enqueue(c, buffer, b - buffer);
#ifdef	DEBUG
		if (rs232_debug)
		    (void) fprintf(rs232_debugfile,
					    "opencon(%s), fd=%d, rchan=%d\n",
					    chan[c]->buf, fd, rd_chan[fd]);
#endif	DEBUG
            }
        }
	else {
	    goto bad_socket;
	}
    }
    else {
bad_socket:
	if (fd >= 0)
            (void) close(fd);
	enqueue(c, (u_char *) 0, 0);
    }

    free((char *) chan[c]->buf);
    return 0;
}

union genericaddr {
    struct sockaddr     generic;
    struct sockaddr_in  internet;
    struct sockaddr_un  nuxi;
};

rcv_acceptcon(c)
    register u_char c;
{
    union genericaddr addr;
    int namelen = sizeof(addr);
    int fd;
    u_char buffer[6];
    register u_char *b = buffer;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "acceptcon(%d)\n", *chan[c]->buf);
#endif	DEBUG

    bzero((char *) &addr, sizeof addr);
    if ((fd = accept((int) *chan[c]->buf, (struct sockaddr *)&addr,
							    &namelen)) < 0) {
	perror("X11/NeWS:");
	enqueue(c, (u_char *) 0, 0);
	free((char *) chan[c]->buf);
	return 0;
    }
 
    switch (addr.generic.sa_family) {
#ifndef NOUNIXSOCKETS
    case AF_UNSPEC:
    case AF_UNIX:
        break;
#endif
    case AF_INET:
        break;

    default:
        (void) fprintf(stderr,
                "Bad address family %d\n", addr.generic.sa_family);
        break;
    }

    if (fcntl(fd, F_SETFD, 1) < 0 || fcntl(fd, F_SETFL, FNDELAY) < 0
	    || fcntl(fd, F_SETOWN, news_pid) < 0) {
	(void) close(fd);
	enqueue(c, (u_char *) 0, 0);
	free((char *) chan[c]->buf);
	return 0;
    } 

    *b++ = fd;
    *b = new_channel();
    rd_chan[fd] = *b;
    alloc_channel(*b++, READ_CHN, (u_char) fd); 
    if (fd >= maximum_fd)
	maximum_fd = fd + 1;
    FD_SET(fd, &read_fds); 
    *b = new_channel();
    wr_chan[fd] = *b;
    alloc_channel(*b++, WRITE_CHN, (u_char) fd); 
    enqueue(c, buffer, b - buffer);
#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "acceptcon(%d), fd=%d, r/wchan=%d\n",
				*chan[c]->buf, fd, rd_chan[fd], wr_chan[fd]);
#endif	DEBUG

    free((char *) chan[c]->buf);
    return 0;
}

#define X_UNIX_PATH	"/tmp/.X11-unix/X"

rcv_serveraddr(c)
    register u_char c;
{
    register struct hostent *h;
    union genericaddr addr;
    char hname[sizeof addr + 30];
    u_char *p = chan[c]->buf;
    int namelen;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "serveraddr(%d)\n", *p);
#endif	DEBUG

    namelen = sizeof addr;
    if (getsockname((int) *p++, (struct sockaddr *) &addr, &namelen) < 0) {
	enqueue(c, (u_char *) 0, 0);
	free((char *) chan[c]->buf);
	return 0;
    }
    if (addr.generic.sa_family == 0 || addr.generic.sa_family == AF_UNIX) {
	char fname[128];

	(void)strcpy(fname, X_UNIX_PATH);
	(void)strcat(fname, "0");
	enqueue(c, (u_char *) fname, strlen(fname) + 1);
    }
    else {
	h = gethostbyname(my_hostname);
	if (h)
	    (void) sprintf(hname, "%u.%u;%s", 
		    ntohl(*(u_long *) h->h_addr),
		    ntohs(addr.internet.sin_port), h->h_name);
	else
	    (void) sprintf(hname, "%u.%u;localhost",
		    ntohl(addr.internet.sin_addr.s_addr), ntohs(addr.internet.sin_port));
	enqueue(c, (u_char *) hname, strlen(hname) + 1);
    }

    free((char *) chan[c]->buf);
    return 0;
}

rcv_clientaddr(c)
    register u_char c;
{
    register struct hostent *h;
    union genericaddr addr;
    u_char *p = chan[c]->buf;
    int         namelen;
    char  inetaddr[30];

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "clientaddr(%d)\n", *p);
#endif	DEBUG

    namelen = sizeof addr;
    if (getpeername((int) *p, (struct sockaddr *) &addr, &namelen) < 0)
	goto bad_access;
    if (addr.generic.sa_family == 0 || addr.generic.sa_family == AF_UNIX) {
	enqueue(c, (u_char *) my_hostname, strlen(my_hostname) + 1);
    }
    else {
        int i;
        char *h_name = 0;
	char *inet_ntoa();

        (void) strcpy(inetaddr, inet_ntoa(addr.internet.sin_addr));
        for(i=0;(i<next && i<10);i++)
            if (strcmp(hosts_cache[i].inet_num,inetaddr) == 0) {
                h_name = hosts_cache[i].name;
                break;
            }
        if (!h_name) {
            if ( (h = gethostbyaddr((char *) &addr.internet.sin_addr,
                                sizeof addr.internet.sin_addr,
                                addr.internet.sin_family)) == 0 )
                goto bad_access;
            (void) strcpy(hosts_cache[next].inet_num, inetaddr);
            (void) strcpy(hosts_cache[next++].name, h->h_name);
            h_name = h->h_name;
        }
	enqueue(c, (u_char *) h_name, strlen(h_name) + 1);
    }

    free((char *) chan[c]->buf);
    return 0;

bad_access:
    enqueue(c, (u_char *) 0, 0);
    free((char *) chan[c]->buf);
    return 0;

}

rcv_gethostname(c)
    register u_char c;
{
    register char *q;
    extern char *get_hostname();

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "gethostname(%d)\n",
						decode_long(chan[c]->buf));
#endif	DEBUG

    if (chan[c]->len > sizeof(long) &&
	    (q = get_hostname((int) decode_long(chan[c]->buf),
				(int) chan[c]->len - sizeof(long),
				chan[c]->buf + sizeof(long))) != NULL)
	enqueue(c, (u_char *) q, strlen(q) + 1);
    else
	enqueue(c, (u_char *) 0, 0);

    free((char *) chan[c]->buf);
    return 0;
}

rcv_gethostaddr(c)
    register u_char c;
{
    int family;
    int length;
    u_char *pAddr;
    u_char buffer[100];
    register u_char *b = buffer;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "gethostaddr(%s)\n",
						(char *)chan[c]->buf);
#endif	DEBUG

    if (get_hostaddress((char *) chan[c]->buf, &family, &length, &pAddr)) {
	b = encode_long(b, (long) family);
	bcopy((char *) pAddr, (char *) b, length);
	b += length;
	enqueue(c, buffer, b - buffer);
    }
    else
	enqueue(c, (u_char *) 0, 0);

    free((char *) chan[c]->buf);
    return 0;
}

rcv_servername(c)
    register u_char c;
{
#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "servername\n");
#endif	DEBUG

    (void) gethostname(my_hostname, 40);
    my_hostname[39] = 0;

    enqueue(c, (u_char *) my_hostname, strlen(my_hostname) + 1);
    free((char *) chan[c]->buf);
    return 0;
}

rcv_spawn(c)
    u_char c;
{
    char            combuf[400];
    char           *args[100];
    register char  *sp = (char *)chan[c]->buf;
    register char  *dp;
    register char **ap;
    register        cnt;
    int             err = 0;
    register int   len = chan[c]->len;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "spawn(%s)\n", sp);
#endif	DEBUG

    if (len <= 0)    /* Don't fork null process */
      return 1;

    for (dp = sp, cnt = len; --cnt >= 0; dp++)
	switch (*dp) {
	  case '!':
	  case '"':
	  case '$':
	  case '&':
	  case '(':
	  case ')':
	  case '*':
	  case ';':
	  case '<':
	  case '>':
	  case '?':
	  case '[':
	  case '\'':
	  case '\\':
	  case ']':
	  case '`':
	  case '{':
	  case '|':
	  case '}':
	  case '~':
	  cnt = -1;
	}

    ap = args;
    if (cnt == -1) {		/* no special characters, don't invoke the
				 * shell */
	dp = combuf;
	while (len > 0 && ap < &args[sizeof args / sizeof args[0] - 1]) {
	    *ap++ = dp;
	    while (dp < combuf + sizeof combuf - 1 && *sp > ' ' && --len >= 0)
		*dp++ = *sp++;
	    *dp++ = 0;
	    while (*sp <= ' ' && --len >= 0)
		sp++;
	}
    } else {
	static char    *shell;

	(void) strncpy(combuf, sp, len);
	combuf[len] = 0;
	if (shell == 0 && (shell = (char *) getenv("SHELL")) == 0)
	    shell = "/bin/sh";
	*ap++ = shell;
	*ap++ = "-c";
	*ap++ = combuf;
    }

    *ap++ = 0;
    if (vfork() == 0) {
	if (setpgrp(0, getpid()) < 0)
	    perror("setpgrp"); 
	if (close(0) < 0)
	    perror("close");
	if (open("/dev/null", O_RDWR, 0) < 0)
	    perror("open");
	if (dup2(0, 1) < 0)
	    perror("dup2");
	if (dup2(0, 2) < 0)
	    perror("dup2");
	if ((int) signal(SIGPIPE, SIG_DFL) < 0)
            perror("signal: SIGPIPE");
        if ((int) signal(SIGFPE, SIG_DFL) < 0)
            perror("signal: SIGFPE");
	execvp(args[0], args); 
	/*
	 * Beware: I'm depending on vfork semantics to propagate the error
	 * code.
	 */
	err = 1;
	_exit(0252);
    }

    free((char *) chan[c]->buf);
    return err;
}

rcv_homedir(c)
    register u_char c;
{
    register struct passwd *pw;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "homedir(%s)\n", chan[c]->buf);
#endif	DEBUG

    if ((pw = (struct passwd *) getpwnam((char *) chan[c]->buf)) == NULL)
	enqueue(c, (u_char *) 0, 0);
    else
	enqueue(c, (u_char *) pw->pw_dir, strlen(pw->pw_dir) + 1);

    free((char *) chan[c]->buf);
    return 0;
}

rcv_cwd(c)
    register u_char c;
{
    static char cwd[MAXPATHLEN];
    extern char *getwd();

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "cwd\n");
#endif	DEBUG

    if (getwd(cwd) == NULL)
	enqueue(c, (u_char *) 0, 0);
    else
	enqueue(c, (u_char *) cwd, strlen(cwd) + 1);

    free((char *) chan[c]->buf);
    return 0;
}

rcv_getenv(c)
    register u_char c;
{
    register char *buffer;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "getenv(%s)\n", chan[c]->buf);
#endif	DEBUG

    if (buffer = getenv((char *) chan[c]->buf))	/* null string => error */
	enqueue(c, (u_char *) buffer, strlen(buffer) + 1);
    else
	enqueue(c, (u_char *) 0, 0);

    free((char *) chan[c]->buf);
    return 0;
}

rcv_putenv(c)
    register u_char c;
{
#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "putenv(%s)\n", chan[c]->buf);
#endif	DEBUG

    return(NULL);
}

rcv_read(c)
    register u_char c;
{
    if (chan[c]->len > 0) {
	if (FD_ISSET(chan[c]->arg, &lseek_fds)) {
	    register int i = (int) decode_long(chan[c]->buf);
	    register char *p;

	    if (i >= 0) {
		p = malloc((unsigned) i);
		if ((i = read((int) chan[c]->arg, p, i)) >= 0)
		    enqueue(c, (u_char *) p, i);
		else
		    enqueue(c, (u_char *) 0, 0);;
		free(p);
	    }
	} else
	    FD_SET(chan[c]->arg, &stopped_fds);
    }
    else
	FD_CLR(chan[c]->arg, &stopped_fds);

    free((char *) chan[c]->buf);
    return 0;
}

rcv_write(c)
    register u_char c;
{
#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "write(%d,%d)\n",
					    chan[c]->arg, chan[c]->len);
#endif	DEBUG

    if (write((int)chan[c]->arg, (char *)chan[c]->buf, (int)chan[c]->len) < 0)
	enqueue(c, (u_char *) 0, 0);

    free((char *) chan[c]->buf);
    return 0;
}

rcv_version(c)
    register u_char c;
{
    register int fd;
    u_char buffer[sizeof(VERSION)];
    register u_char *b = buffer;
    extern short server_version;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "version\n");
#endif	DEBUG

    if (firstfd == 0) {		/* first time through */
	server_version = decode_short(chan[c]->buf);
	if ((fd = open("/dev/null", 0)) < 0) {
	    (void) fprintf(stderr, "rcv_version: open failed\n");
	    exit(1);
	}
	/* version.version set in main() */
	firstfd = version.firstfd = fd;
	(void) close(fd);
    }

    b = encode_short(b, version.version);
    b = encode_short(b, version.firstfd);
    b = encode_long(b, (long) version.res1);
    b = encode_long(b, (long) version.res2);
    b = encode_long(b, (long) version.res3);

    enqueue(c, buffer, b - buffer);
    free((char *) chan[c]->buf);
    return 0;
}

rcv_pipe(c)
    register u_char c;
{
    u_char buffer[512];
    register u_char *b = buffer;
    register int input, output;
    int ipipefd[2], opipefd[2];
    int pid;

#ifdef	DEBUG
    if (rs232_debug)
	(void) fprintf(rs232_debugfile, "pipe(%d,%s)\n", *chan[c]->buf,
							    chan[c]->buf+1);
#endif	DEBUG

    input = output = 0;
    switch (*chan[c]->buf) {
    case O_RDONLY:
	input = 1;
	break;
    case O_RDWR:
	input = 1;
	/* fall through */
    case O_WRONLY:
	output = 1;
	break;
    default:
	goto err_ret;
    }

    if (input && pipe(ipipefd) < 0) {
err_ret:
	enqueue(c, (u_char *) 0, 0);
	free((char *) chan[c]->buf);
	return 0;
    }

    if (output && pipe(opipefd) < 0) {
icloseret:
	if (input) {
	    (void) close(ipipefd[0]);
	    (void) close(ipipefd[1]);
	}
	goto err_ret;
    }

    if ((pid = vfork()) < 0) {
	if (output) {
	    (void) close(opipefd[0]);
	    (void) close(opipefd[1]);
	}
	goto icloseret;
    }

    if (pid == 0) {		/* child */
	if (input) {
	    (void) dup2(ipipefd[1], 1);
	    (void) close(ipipefd[0]);
	    (void) close(ipipefd[1]);
	}
	else {
	    (void) close(1);
	    (void) open("/dev/null", 2);
	}

	if (output) {
	    (void) dup2(opipefd[0], 0);
	    (void) close(opipefd[0]);
	    (void) close(opipefd[1]);
	}
	else {
	    (void) close(0);
	    (void) open("/dev/null", 1);
	}

	execl("/bin/sh", "sh", "-c", chan[c]->buf+1, 0);
	(void) write(2, "shell missing\n", 14);
	exit(1);
    }

    if (input) {
	(void) close(ipipefd[1]);
	if (fcntl(ipipefd[0], F_SETFD, 1) < 0 || 
		fcntl(ipipefd[0], F_SETFL, FNDELAY) < 0) {
	    (void) close(ipipefd[0]);
	    goto err_ret;
	}
	*b++ = ipipefd[0];
	*b = new_channel();
	rd_chan[ipipefd[0]] = *b;
	alloc_channel(*b++, READ_CHN, (u_char) ipipefd[0]);
	*b++ = READ_CHN;
	if (ipipefd[0] >= maximum_fd)
	    maximum_fd = ipipefd[0] + 1;
	FD_SET(ipipefd[0], &read_fds);

#ifdef	DEBUG
	if (rs232_debug)
	    (void) fprintf(rs232_debugfile, "input pipe(%s),fd=%d,chan=%d\n",
			    chan[c]->buf+1, ipipefd[0], rd_chan[ipipefd[0]]);
#endif	DEBUG
    }

    if (output) {
	(void) close(opipefd[0]);
	if (fcntl(opipefd[1], F_SETFD, 1) < 0) {
	    (void) close(opipefd[1]);
	    goto err_ret;
	}
	*b++ = opipefd[1];
	*b = new_channel();
	wr_chan[opipefd[1]] = *b;
	alloc_channel(*b++, WRITE_CHN, (u_char) opipefd[1]); 
	*b++ = WRITE_CHN;
#ifdef	DEBUG
	if (rs232_debug)
	    (void) fprintf(rs232_debugfile, "input pipe(%s),fd=%d,chan=%d\n",
			    chan[c]->buf+1, opipefd[1], wr_chan[opipefd[1]]);
#endif	DEBUG
    }

    enqueue(c, buffer, b - buffer);
    free((char *) chan[c]->buf);
    return 0;
}
