SysIII/usr/src/cmd/cu.c

Compare this file to the similar file:
Show the results in this format:

/***************************************************************
 *	cu [-s speed] [-a acu] [-l line] [ h ] [-o | -e] telno | 'dir'
 *
 *	speeds are: 110, 150, 300, 1200, 4800, 9600.
 *		Set default speed by defining the value of BAUD.
 *	-l and -a are for line and unit arguments from the file whose
 *		name is defined under LDEVS below.
 *	-h is for half-duplex (local echoing).
 *	-d can be used (with ddt) to get some tracing & diagnostics.
 *	-o or -e is for odd or even parity on transmission to remote.
 *	telno is either a telephone number or "dir" for a direct line.
 *	If "dir" is used, the the "-l dev" option is required.  If
 *	"-l dev", or "-a acu" are used, speed is taken from LDEVS.
 *
 *	Escape with `~' at beginning of line.
 *	Silent output diversions are ~>:filename and ~>>:filename.
 *	Terminate output diversion with ~> alone.
 *	~. is quit, and ~! gives local command or shell.
 *	Also ~$ for canned local procedure pumping remote.
 *	Both ~%put from [to]  and  ~%take from [to] invoke built-ins.
 *	Also, ~%break or just ~%b will transmit a BREAK to remote.
 *	~nostop turns off the DC3/DC1 input control from remote,
 *		(certain remote systems cannot cope with DC3 or DC1).
 *
 *	As a device-lockout semaphore mechanism, create an entry
 *	in the directory #defined as LOCK whose name is LCK..dev
 *	where dev is the device name taken from the "line" column
 *	in the file #defined as LDEVS.  Be sure to trap every possible
 *	way out of cu execution in order to "release" the device.
 *	Also, have the system start-up procedure clean all such
 *	entries from the LOCK directory.
 ***************************************************************/
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <termio.h>
#include <ctype.h>
#include <errno.h>

#define	LDEVS	"/usr/lib/uucp/L-devices"
#define	LOCK	"/usr/spool/uucp/LCK.."	/* where to synch with uucp */
#define	BAUD	"300"		/* default baud rate */
#define	ACULAST	"<"		/* character which terminates dialing*/
#define	RUB	'\177'		/* mnemonic */
#define	XON	'\21'		/* mnemonic */
#define	XOFF	'\23'		/* mnemonic */
#define	TTYIN	0		/* mnemonic */
#define	TTYOUT	1		/* mnemonic */
#define	TTYERR	2		/* mnemonic */
#define	YES	1		/* mnemonic */
#define	NO	0		/* mnemonic */
#define	EQUALS	!strcmp		/* mnemonic */
#define	DIFFER	strcmp		/* mnemonic */
#define	COPY	strcpy		/* mnemonic */
#define	EXEC(A, B, C) execl("/bin/sh", A, B, C, 0)

FILE	*Ldevices;		/* file pointer for Device name file */
struct	termio	tv, tv0, lv;

union {				/* this helps to quiet lint */
	int	i;
	unsigned char u;
	char	c;
} cx;

char
	tkill,			/* current input kill */
	terase,			/* current input erase */
	*devtype="ACU",		/* indicates dial or direct connect */
	*acu="",		/* ptr to name of requested ACU */
	*line="",		/* ptr to name of requested device */
	dspeed[6]=BAUD,		/* default baud rate */
	*speed,			/* ptr to requested line speed */
	*telno,			/* ptr to dialed tel-no or "dir" */
	cul[20],		/* line chosen */
	cua[20],		/* acu's device-name */
	lock[30+sizeof(LOCK)],	/* directory in which to make lockfile*/
	*strtok(),
	*strcat(),
	*strncpy(),
	*strpbrk(),
	*strcpy();

unsigned sleep(), alarm();
extern	errno;

int
	found=0,		/* set when device is seen legal */
	linarg,			/* length of arg for `-l' option */
	acuarg,			/* length of arg for `-a' option */
	rlfd,			/* fd for remote comm line */
	lfd= -1,		/* fd for the device-lock file */
	parity=0,		/* 0 for no parity, -1 odd, 1 even */
	intrupt=NO,		/* interrupt indicator */
#ifdef	ddt
	debug=NO,		/* flag turns on more diagnostics */
#endif
	duplex=YES,		/* half(NO), or full(YES) duplex */
	sstop=YES,		/* NO means remote can't XON/XOFF */
	rtn_code=1,
	takeflag=NO;		/* indicates a ~%take is in progress */
	onintrpt(),		/* interrupt routine */
	sigalarm(),		/* interrupt routine */
	hangup(),		/* interrupt routine */
	byebye(),		/* interrupt routine */
	die(),			/* interrupt routine */
	strcmp();

char	*msg[]= {
/*  0*/	"usage: cu [-s speed] [-a acu] [-l line] [ h ] [-o | -e] telno | \"dir\"\n",
/*  1*/	"interrupt",
/*  2*/	"dialer hung",
/*  3*/	"no answer",
/*  4*/	"can't fork",
/*  5*/	"acu problem",
/*  6*/	"line problem",
/*  7*/	"line hung",
/*  8*/	"Can not open: %s\r\n",
/*  9*/	"Connect failed: %s\r\n",
/* 10*/	"No line %s at %s baud\r\n",
/* 11*/	"\"%s\" with \"%s\" not %s at %s baud\r\n",
/* 12*/	"Device \"%s\" not %s\r\n",
/* 13*/	"Line gone\r\n",
/* 14*/	"Can't execute shell\r\n",
/* 15*/	"Can't divert %s\r\n",
/* 16*/	"Use `~~' to start line with `~'\r\n",
/* 17*/	"character missed\r\n",
/* 18*/	"after %ld bytes\r\n",
/* 19*/	"%d lines/%ld characters\r\n",
/* 20*/	"Only digits & '-'s or '='s in telno\n",
/* 21*/	"file transmission interrupted\r\n"
};

/***************************************************************
 *	main: get connection, and fork.
 *	Child invokes "receive" to read from remote & write to tty.
 *	Main line invokes "transmit" to read tty & write to remote.
 ***************************************************************/

main(argc, argv)
char *argv[];
{
	int	fk, errflag=0;
	extern	int	optind;
	extern	char	*optarg;

	speed = dspeed;
	while((cx.i = getopt(argc, argv, "dheos:l:a:")) != EOF)
		switch(cx.i) {
			case 'd':
#ifdef	ddt
				debug = YES;
#else
				++errflag;
#endif
				break;
			case 'h':
				duplex ^= YES;
				break;
			case 'e':
				if(++parity != 1)
					++errflag;
				break;
			case 'o':
				if(--parity != -1)
					++errflag;
				break;
			case 's':
				speed = optarg;
			  	break;
			case 'l':
				line = optarg;
			  	break;
			case 'a':
				acu = optarg;
			  	break;
			case '?':
				++errflag;
		}

	linarg = strlen(line);
	acuarg = strlen(acu);

	if(optind < argc && optind > 0) {
	    telno = argv[optind];
	    if(strlen(telno) != strspn(telno, "0123456789=-"))
		if(EQUALS(telno,"dir")) {
			if(linarg == 0)
				++errflag;
			devtype = COPY(devtype, "DIR");
		} else {
			display(msg[20]);
			++errflag;
		}
	} else
	    ++errflag;

	if(errflag) {
		display(msg[0]);
		exit(1);
	}

	if((Ldevices = fopen(LDEVS, "r")) == NULL) {
		display(msg[8], LDEVS);
		exit(1);
	}
	(void) ioctl(TTYIN, TCGETA, &tv0); /* save initial tty state */
	tkill = tv0.c_cc[VKILL];
	terase = tv0.c_cc[VERASE];

	(void) signal(SIGHUP, die);
	(void) signal(SIGQUIT, die);
	(void) signal(SIGINT, onintrpt);
	(void) signal(SIGALRM, sigalarm);

	while(1) {
		char	dvc[50], *find_dev();

		(void) COPY(dvc, find_dev(acu, line, speed));
		if(strlen(dvc) == 0) {
		    char  *why;

			why = found? "available": "known";
		    if(acuarg == 0 && linarg == 0)
			display(msg[10], why, speed);
		    else
	 		if(acuarg != 0 && linarg != 0)
			    display(msg[11], line, acu, why, speed);
			else
			    display(msg[12], linarg? line: acu, why);
		    die(8);
		}
		(void) COPY(lock, LOCK);
		(void) strcat(lock, dvc);
		if(geteuid() == 0 && access(lock, 0) == 0)
			continue;
		if((lfd = creat(lock, 0444)) < 0)
			continue;
		else {
			cx.i = getpid();
			(void) write(lfd, &cx.c, sizeof(cx.i));
		}
		(void) fclose(Ldevices);
		if(intrupt == YES)
			die(-1);
		if((rlfd = connect(cul, cua, telno, speed)) < 0) {
			display(msg[9], msg[-rlfd]);
			die(-rlfd);
		}
		break;
	}

	/* When we get this far we have an open communication line */

	mode(1);			/* put terminal in `raw' mode */

	display("Connected\r\n");
	
	if((fk = dofork()) == 0) {
		(void) signal(SIGHUP, hangup);
		(void) signal(SIGINT, SIG_IGN);
		(void) signal(SIGQUIT, hangup);
		receive();	/* This should run until killed */
		display("\r\n\07Lost carrier\r\n");
#ifdef ddt
		if(debug == YES) tdmp(rlfd);
#endif
		hangup();
	}
	(void) signal(SIGUSR1, byebye);
	(void) signal(SIGHUP, SIG_IGN);
	(void) signal(SIGQUIT, SIG_IGN);
	rtn_code = transmit();	/* This is the parent proccess */
	(void) kill(fk, SIGKILL); /* Kill paired "receive" proccess */
	byebye();
}

/***************************************************************
 *	transmit: copy stdin to rlfd, except:
 *	~.	terminate
 *	~!	local login-style shell
 *	~!cmd	execute cmd locally
 *	~$proc	execute proc locally, send output to line
 *	~%cmd	execute builtin cmd (put, take, or break)
 ****************************************************************/

transmit()
{
	char	b[200];
	register char	*p;
	register int	escape;

#ifdef	ddt
	if(debug == YES) display("transmit started\n\r");
#endif
	while(1) {
		p = b;
		while(r_char(TTYIN) == YES) {
			if(p == b)	/* Escape on leading  ~    */
				escape = (cx.c == '~');
			if(p == b+1)	/* But not on leading ~~   */
				escape &= (cx.c != '~');
			if(escape) {
				if(cx.c == '\r' || cx.c == '\n') {
					*p = '\0';
					if(tilda(b+1) == YES)
						return(0);
					break;
				}
				if(cx.c == RUB || cx.c == tkill
						|| cx.c == '\0') {
					display("\r\n");
					break;
				}
				if(w_char(TTYERR) == NO)
					return(7);	/* tty gone */
				if(cx.c == terase) {
					p = (--p < b)? b:p;
					continue;
				}
			} else {
				if(cx.c == '\0') {
					(void) ioctl(rlfd, TCSBRK, 0);
					flush();
					break;
				}
				if(w_char(rlfd) == NO) {
					display(msg[13]);
					return(2);
				}
				if(duplex == NO)
					if(w_char(TTYERR) == NO)
						return(7);
				if(cx.c == RUB) {
					flush();
					break;
				}
				if(cx.c == '\n' || cx.c == '\r'
					|| cx.c == tkill)
					break;
			}
			*p++ = cx.c;
		}
	}
}

flush()	/* routine to halt input from remote and flush buffers */
{
	(void) ioctl(TTYOUT, TCXONC, 0);
	(void) ioctl(rlfd, TCFLSH, 0);
	(void) ioctl(TTYOUT, TCFLSH, 1);
	(void) ioctl(TTYOUT, TCXONC, 1);
	if(takeflag == NO) {
		return;
	}
	display(msg[21]);
	(void) sleep(3);
	w_str("echo '~>';mesg y\n");
	takeflag = NO;
}

tilda(cmd)
char	*cmd;
{
	display("\r\n");
#ifdef	ddt
	if(debug == YES) display("call tilda(%s)\r\n", cmd);
#endif
	switch(cmd[0]) {
		case '.':
			if(EQUALS(devtype,"DIR"))
				if(cmd[1] != '.')
					w_str("\04\04\04\04\04");
			return(YES);
		case '!':
		case '$':
			shell(cmd);	/* Local shell */
			display("\r%c\r\n", *cmd);
			w_str("\n");
			break;
		case '%':
			dopercen(++cmd);
			break;
#ifdef ddt
		case 't':
			tdmp(TTYIN);
			break;
		case 'l':
			tdmp(rlfd);
			break;
#endif
		default:
			display(msg[16]);
	}
	return(NO);
}

/***************************************************************
 *	The routine "shell" takes an argument starting with
 *	either "!" or "$", and terminated with '\0'.
 *	If $arg, arg is the name of a local shell file which
 *	is executed and its output is passed to the remote.
 *	If !arg, we escape to a local shell to execute arg
 *	with output to TTY, and if arg is null, escape to
 *	a local shell and blind the remote line.  In either
 *	case, RUBout or '^D' will kill the escape status.
 **************************************************************/

shell(str)
char	*str;
{
	int	fk;

#ifdef	ddt
	if(debug == YES) display("call shell(%s)\r\n", str);
#endif
	fk = dofork();
	if(fk == 0) {
		/***********************************************
		 * Hook-up our "standard output"
		 * to either the tty or the line
		 * as appropriate for '!' or '$'
		 ***********************************************/
		(void) close(TTYOUT);
		(void) fcntl((*str == '$')? rlfd:TTYERR,F_DUPFD,TTYOUT);
		(void) close(rlfd);
		mode(0);	/* normal */
		(void) signal(SIGINT, SIG_DFL);
		if(*++str == '\0')
			EXEC("-", (char *) 0, (char *) 0);
		else
			EXEC("sh", "-c", str);
		display(msg[14]);
		exit(0);
	}
	if(fk > 0)
		while(wait((int *) 0) != fk);
	mode(1);
}

/***************************************************************
 *	This function implements the 'put', 'take', and 'break'
 *	commands which are internal to cu.
 ***************************************************************/

dopercen(cmd)
register char *cmd;
{
	char	*arg[5];
	int	narg;

#ifdef	ddt
	if(debug == YES) display("call dopercen(\"%s\")\r\n", cmd);
#endif
	arg[narg=0] = strtok(cmd, " \t\n");
	/* following loop breaks out the command and args */
	while((arg[++narg] = strtok((char*) NULL, " \t\n")) != NULL) {
		if(narg < 5)
			continue;
		else
			break;
	}

	if(EQUALS(arg[0], "take")) {
		if(narg < 2 || narg > 3) {
			display("usage: ~%%take from [to]\r\n");
			return;
		}
		if(narg == 2)
			arg[2] = arg[1];
		w_str("mesg n;echo '~>':");
		w_str(arg[2]);
		w_str(";cat ");
		w_str(arg[1]);
		w_str(";echo '~>';mesg y\n");
		takeflag = YES;
		return;
	}
	else if(EQUALS(arg[0], "put")) {
		FILE	*file;
		char	buf[512], spec[3], *b, *p, *q;
		int	len, tc=0, lines=0;
		long	chars=0L;

		if(narg < 2 || narg > 3) {
			display("usage: ~%%put from [to]\r\n");
			goto R;
		}
		if(parity) {
			display("parity problem\r\n");
			goto R;
		}
		if(narg == 2)
			arg[2] = arg[1];
		if((file = fopen(arg[1], "r")) == NULL) {
			display(msg[8], arg[1]);
R:
			w_str("\n");
			return;
		}
		w_str("stty -echo; cat - > ");
		w_str(arg[2]);
		w_str("; stty echo\n");
		intrupt = NO;
		mode(2);
		spec[0] = terase;
		spec[1] = tkill;
		spec[2] = '\0';
		(void) sleep(5);
		while(intrupt == NO &&
				fgets(b= &buf[256],256,file) != NULL) {
			len = strlen(b);
			chars += len;		/* character count */
			p = b;
			while(q = strpbrk(p, spec)) {
				b = strncpy(b-1, b, q-b);
				*(q-1) = '\\';
				p = q+1;
			}
			if((tc += len) >= 256) {
				(void) sleep(1);
				tc = len;
			}
			if(write(rlfd, b, strlen(b)) < 0) {
				display(msg[17]);
				intrupt = YES;
				break;
			}
			++lines;		/* line count */
		}
		mode(1);
		(void) fclose(file);
		if(intrupt == YES) {
			intrupt = NO;
			display(msg[21]);
			w_str("\n");
			display(msg[18], ++chars);
		} else
			display(msg[19], lines, chars);
		w_str("\04");
		(void) sleep(3);
		return;
	}
	else if(EQUALS(arg[0], "b") || EQUALS(arg[0], "break")) {
		(void) ioctl(rlfd, TCSBRK, 0);
		return;
	}
	else if(EQUALS(arg[0], "nostop")) {
		(void) ioctl(rlfd, TCGETA, &tv);
		tv.c_iflag &= ~IXOFF;
		(void) ioctl(rlfd, TCSETA, &tv);
		sstop = NO;
		mode(1);
		return;
	}
	display("~%%%s unknown to cu\r\n", arg[0]);
}

/***************************************************************
 *	receive: read from remote line, write to fd=1 (TTYOUT)
 *	catch:
 *	~>[>]:file
 *	.
 *	. stuff for file
 *	.
 *	~>	(ends diversion)
 ***************************************************************/

receive()
{
	register silent=NO, file;
	register char *p;
	int	tic;
	char	b[512];
	long	lseek(), count;

#ifdef	ddt
	if(debug == YES) display("receive started\r\n");
#endif
	file = -1;
	p = b;
	while(r_char(rlfd) == YES) {
		if(silent == NO)
			if(w_char(TTYOUT) == NO)
				return;
		*p++ = cx.c;
		if(cx.c != '\n' && (p-b) < 512)
			continue;
		/* remove (CR) and junk inserted at remote */
		while(p-2 >= b && (*(p-2) == '\r' || *(p-2) == '\0')) {
			--p;
			*(p-1) = *p;
		}
		/***********************************************
		 * The rest of this code is to deal with what
		 * happens at the beginning, middle or end of
		 * a diversion to a file.
		 ************************************************/
		if(b[0] == '~' && b[1] == '>') {
			/****************************************
			 * The line is the beginning or
			 * end of a diversion to a file.
			 ****************************************/
			if((file < 0) && (b[2] == ':' || b[2] == '>')) {
				/**********************************
				 * Beginning of a diversion
				 *********************************/
				int	append;

				*(p-1) = NULL; /* terminate file name */
				append = (b[2] == '>')? 1:0;
				p = b + 3 + append;
				if(append && (file=open(p,O_WRONLY))>0)
					(void) lseek(file, 0L, 2);
				else
					file = creat(p, 0666);
				if(file < 0)
					display(msg[15], p);
				else {
					silent = YES; 
					count = tic = 0;
				}
			} else {
				/*******************************
				 * End of a diversion (or queer data)
				 *******************************/
				if(b[2] != '\n')
					goto D;		/* queer data */
				if(silent = close(file)) {
					display(msg[15], b);
					silent = NO;
				}
				display("~>\r\n");
				display(msg[19], tic, count);
				file = -1;
			}
		} else {
			/***************************************
			 * This line is not an escape line.
			 * Either no diversion; or else yes, and
			 * we've got to divert the line to the file.
			 ***************************************/
D:
			if(file > 0) {
				(void) write(file, b, p-b);
				count += p-b;	/* tally char count */
				++tic;		/* tally lines */
			}
		}
		p = b;
	}
}

/***************************************************************
 *	change the TTY attributes of the users terminal:
 *	0 means restore attributes to pre-cu status.
 *	1 means set `raw' mode for use during cu session.
 *	2 means like 1 but accept interrupts from the keyboard.
 ***************************************************************/
mode(arg)
{
#ifdef	ddt
	if(debug == YES) display("call mode(%d)\r\n", arg);
#endif
	if(arg == 0) {
		(void) ioctl(TTYIN, TCSETAW, &tv0);
	} else {
		(void) ioctl(TTYIN, TCGETA, &tv);
		if(arg == 1) {
			tv.c_iflag &= ~(INLCR | ICRNL | BRKINT);
			if(sstop == YES)
				tv.c_iflag |= IXON;
			else
				tv.c_iflag &= ~IXON;
			tv.c_oflag |= OPOST;
			tv.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR
						| ONLRET);
			tv.c_lflag &= ~(ICANON | ECHO | ISIG);
			tv.c_cc[VMIN] = '\01';
			tv.c_cc[VTIME] = '\0';
		}
		if(arg == 2) {
			tv.c_iflag |= (IXON | BRKINT);
			tv.c_lflag |= ISIG;
		}
		(void) ioctl(TTYIN, TCSETAW, &tv);
	}
}

#ifdef ddt
tdmp(arg)
{
	struct termio xv;
	int	i;

	(void) printf("\rdevice status for fd=%d\n", arg);
	(void) printf("\rF_GETFL=%o,", fcntl(arg, F_GETFL,1));
	if(ioctl(arg, TCGETA, &xv) < 0) {
		char	buf[100];
		(void) sprintf(buf, "\rtdmp for fd=%d:", arg);
		perror(buf);
		return;
	}
	(void) printf("iflag=`%o',", xv.c_iflag);
	(void) printf("oflag=`%o',", xv.c_oflag);
	(void) printf("cflag=`%o',", xv.c_cflag);
	(void) printf("lflag=`%o',", xv.c_lflag);
	(void) printf("line=`%o'\r\n", xv.c_line);
	(void) printf("cc[0]=`%o',", xv.c_cc[0]);
	for(i=1; i<8; ++i)
		(void) printf("[%d]=`%o',", i, xv.c_cc[i]);
	(void) printf("\r\n");
}
#endif

dofork()
{
	register x, i=0;

	while(++i < 6)
		if((x = fork()) != -1)
			return(x);
#ifdef	ddt
	if(debug == YES) perror("dofork");
#endif
	display(msg[4]);
	return(x);
}

r_char(fd)
{
	extern	errno;
	int	rtn;
	char	str[30];

	errno = 0;
	if((rtn = read(fd, &cx.c, 1)) != 1) {
	    (void) sprintf(str,"read from fd=%d returned %d", fd, rtn);
	    perror(str);
#ifdef ddt
	    if(debug == YES) tdmp(fd);
#endif
	}
	return((rtn == 1)? YES:NO);
}

w_char(fd)
{
	return((write(fd, &cx.c, 1) == 1)? YES:NO);
}

/*VARARGS1*/
display(fmt, arg1, arg2, arg3, arg4, arg5)
char	*fmt;
{
	(void) fprintf(stderr, fmt, arg1, arg2, arg3, arg4, arg5);
}

w_str(string)
register char *string;
{
	int	len;

	len = strlen(string);
	if(write(rlfd, string, len) != len)
		display(msg[13]);
}

onintrpt()
{
	(void) signal(SIGINT, onintrpt);
	intrupt = YES;
}

sigalarm()
{
	(void) signal(SIGALRM, sigalarm);
}

byebye()	/* this is executed only in the parent proccess */
{
	(void) wait((int *) 0);
	display("\r\nDisconnected\r\n");
	die(rtn_code);
}

die(arg)	/* this is executed only in the parent proccess */
{
#ifdef ddt
	if(debug == YES) display("call die(%d)\r\n", arg);
#endif
	if(rlfd > 0)
		(void) close(rlfd);
	if(lfd > 0) {
		if(fork() == 0) {
			if(EQUALS(devtype, "ACU")) {
			/* hold lock-file entry 'till acu recovers */
				(void) signal(SIGINT, SIG_IGN);
				(void) signal(SIGALRM, SIG_IGN);
				(void) signal(SIGHUP, SIG_IGN);
				(void) sleep(12);
			}
			(void) close(lfd);
			if(unlink(lock) < 0)
				display("Can't unlink lock-file\n");
#ifdef ddt
			else if(debug == YES)
				display("Lock-file unlinked\n");
#endif
			exit(0);
		}
	}
	mode(0);	/* restore users prior tty status */
	exit(arg);
}

hangup()	/* this is executed only in the receive proccess */
{
	exit(kill(getppid(), SIGUSR1));
}

/***************************************************************
 *	connect: establish dial-out connection.
 *	Negative values returned (-2...-7) are error message indices.
 *	Be sure to disconnect tty when done, via HUPCL or stty 0.
 ***************************************************************/

connect(dev, xacu, xtelno, wspd)
char	*dev, *xacu, *xtelno, *wspd;
{
	int	er=0, dum, fdac, fd=0, t, w;
	char	sp_code, b[30];
	extern	errno;

#ifdef	ddt
	if(debug == YES) display("call connect(%s,%s,%s,%s)\r\n", dev,
						xacu, xtelno, wspd);
#endif
	switch(atoi(wspd)) {
		case 110:
			sp_code = B110;
			break;
		case 150:
			sp_code = B150;
			break;
		case 300:
			sp_code = B300;
			break;
		case 1200:
			sp_code = B1200;
			break;
		case 4800:
			sp_code = B4800;
			break;
		case 9600:
			sp_code = B9600;
			break;
		default:
			sp_code = B300;
	}
	if((fd = open(dev, O_RDWR | O_NDELAY)) < 0) {
		perror(dev);
		er = 6;
		goto X;
	}
	if((t = ioctl(fd, TCGETA, &lv)) < 0)
		perror("gtty for remote");
	else {
		lv.c_cflag &= ~CBAUD;
		lv.c_cflag |= (CREAD | HUPCL | sp_code);
		if(parity) {
			lv.c_cflag |= PARENB;
			lv.c_cflag &= ~CSIZE;
			lv.c_cflag |= CS7;
			if(parity < 0)
				lv.c_cflag |= PARODD;
		}
		if(EQUALS(devtype, "DIR"))
			lv.c_cflag |= CLOCAL;
		lv.c_iflag |= (IGNPAR | IGNBRK | ISTRIP | IXON | IXOFF);
		lv.c_iflag &= ~(INLCR | ICRNL);
		lv.c_cc[VERASE] = '\0';
		lv.c_cc[VKILL] = '\0';
		lv.c_cc[VMIN] = '\1';
		lv.c_cc[VTIME] = '\0';
		lv.c_lflag &= ~(ICANON | ECHO | ISIG);
		if((t = ioctl(fd, TCSETA, &lv)) < 0)
			perror("stty for remote");
	}
	if(t < 0) {
		er = (errno == EINTR? 7:6);
		goto X;
	}
	if(EQUALS(devtype, "ACU")) {
		if((fdac = open(xacu, O_WRONLY)) < 0) {
			perror(xacu);
			er = 5;
			goto X;
		}
		t = strlen(strcat(COPY(b,xtelno),ACULAST));
#ifdef	ddt
		if(debug == YES) display("dialing %s\n", b);
#endif
		(void) alarm((unsigned) (2*t+5));
		w = write(fdac, b, t);		/* dial the number */
		(void) alarm(0);
		if(w != t) {
			perror("write to acu");
			er = (errno == EINTR)? 2:5;
			goto X;
		}
		(void) close(fdac);	/* dialing is complete */
#ifdef	ddt
		if(debug == YES) display("dialing complete\n");
#endif
	}
	(void) alarm((unsigned) (t+15));
	dum = open(dev, O_RDWR);	/* wait here for carrier */
	(void) alarm(0);
	if(dum < 0) {
#ifdef	ddt
		if(debug == YES) perror(dev);
#endif
		er = (errno == EINTR)? 3:6;
		goto X;
	}
	(void) close(dum);	/* the dummy open used for waiting*/
	(void) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NDELAY);
#if ddt
	if(debug == YES && fd > 0) tdmp(fd);
#endif
X:
	if(intrupt == YES)
		er = 1;
#ifdef	ddt
	if(debug == YES)
		display("connect ends eith er=%d, fd=%d\n", er, fd);
#endif
	return(er? -er:fd);
}

/***************************************************************
 *	find_dev: find a device pair with the wanted characteristics
 *	      specified in line, acu, and speed arguments.
 *	The global 'devtype' should have the value "ACU" or "DIR",
 *		depending on value of 'telno' arg on the command line.
 *	Return pointer to device name for use in lock-out synch.
 *	The globals 'cua' and 'cul' will be set to contain the
 *		/dev directory names of the corresponding devices.
 *	If the L-devices list contains a '0' entry because the
 *		line is direct, the global 'cua' is set to '\0'.
 ***************************************************************/

char *
find_dev(xacu, ln, spd)
char *xacu, *ln, *spd;
{
	char	buf[50], *b;	/* place to read L-devices into */
	int	acarg, lnarg;

#ifdef	ddt
	if(debug == YES)
		display("call find_dev(%s, %s, %s)\n", xacu, ln, spd);
#endif

	acarg = strlen(xacu);
	lnarg = strlen(ln);
	while(fgets(buf, 50, Ldevices) != NULL) {
		if(DIFFER(devtype, strtok(buf, " \t")))
			continue;
		(void) strcat(COPY(cul,"/dev/"),strtok((char*)NULL," \t"));
		if(*(b = strtok((char *) NULL, " \t")) == '0')
			cua[0] = '\0';
		else
			(void) strcat(COPY(cua,"/dev/"),b);
		if(acarg) {
			if(DIFFER(devtype, "ACU"))
				break;
			if(DIFFER(xacu, cua) && DIFFER(xacu, &cua[5]))
				continue;
			(void) COPY(spd, strtok((char *)NULL, " \t\n"));
			++found;
			return(cul+5);
		}
		if(lnarg) {
			if(DIFFER(ln, cul) && DIFFER(ln, &cul[5]))
				continue;
			(void) COPY(spd, strtok((char *)NULL, " \t\n"));
			++found;
			return(cul+5);
		}
		if(EQUALS(devtype, "ACU")) {
			if(DIFFER(spd, strtok((char *) NULL, " \t\n")))
				continue;
			++found;
			return(cul+5);
		}
	}
	return("");
}