xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 17484)
111758Ssam #ifndef lint
2*17484Sleres static char sccsid[] = "@(#)telnet.c	4.25 (Berkeley) 12/06/84";
311758Ssam #endif
411758Ssam 
56000Sroot /*
66000Sroot  * User telnet program.
76000Sroot  */
89217Ssam #include <sys/types.h>
99217Ssam #include <sys/socket.h>
109972Ssam #include <sys/ioctl.h>
119217Ssam 
129217Ssam #include <netinet/in.h>
139217Ssam 
1412212Ssam #define	TELOPTS
1512212Ssam #include <arpa/telnet.h>
1612212Ssam 
176000Sroot #include <stdio.h>
186000Sroot #include <ctype.h>
196000Sroot #include <errno.h>
206000Sroot #include <signal.h>
216000Sroot #include <setjmp.h>
228345Ssam #include <netdb.h>
239217Ssam 
246000Sroot #define	strip(x)	((x)&0177)
256000Sroot 
266000Sroot char	ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
278378Ssam char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
286000Sroot 
296000Sroot char	hisopts[256];
306000Sroot char	myopts[256];
316000Sroot 
326000Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
336000Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
346000Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
356000Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
366000Sroot 
376000Sroot int	connected;
386000Sroot int	net;
399972Ssam int	showoptions = 0;
407377Sfeldman int	options;
4110339Ssam int	debug = 0;
429972Ssam int	crmod = 0;
436000Sroot char	*prompt;
449972Ssam char	escape = CTRL(]);
456000Sroot 
466000Sroot char	line[200];
476000Sroot int	margc;
486000Sroot char	*margv[20];
496000Sroot 
506000Sroot jmp_buf	toplevel;
516000Sroot jmp_buf	peerdied;
526000Sroot 
536000Sroot extern	int errno;
546000Sroot 
556000Sroot int	tn(), quit(), suspend(), bye(), help();
566024Ssam int	setescape(), status(), toggle(), setoptions();
5710603Ssam int	setcrmod(), setdebug();
586000Sroot 
598378Ssam #define HELPINDENT (sizeof ("connect"))
606000Sroot 
616000Sroot struct cmd {
6211758Ssam 	char	*name;		/* command name */
6311758Ssam 	char	*help;		/* help string */
6411758Ssam 	int	(*handler)();	/* routine which executes command */
656000Sroot };
666000Sroot 
6711758Ssam char	openhelp[] =	"connect to a site";
6811758Ssam char	closehelp[] =	"close current connection";
6911758Ssam char	quithelp[] =	"exit telnet";
7011758Ssam char	zhelp[] =	"suspend telnet";
7111758Ssam char	debughelp[] =	"toggle debugging";
7211758Ssam char	escapehelp[] =	"set escape character";
7311758Ssam char	statushelp[] =	"print status information";
7411758Ssam char	helphelp[] =	"print help information";
7511758Ssam char	optionshelp[] =	"toggle viewing of options processing";
7611758Ssam char	crmodhelp[] =	"toggle mapping of received carriage returns";
776000Sroot 
786000Sroot struct cmd cmdtab[] = {
7911758Ssam 	{ "open",	openhelp,	tn },
8011758Ssam 	{ "close",	closehelp,	bye },
8111758Ssam 	{ "quit",	quithelp,	quit },
826000Sroot 	{ "z",		zhelp,		suspend },
8311758Ssam 	{ "escape",	escapehelp,	setescape },
8411758Ssam 	{ "status",	statushelp,	status },
8511758Ssam 	{ "options",	optionshelp,	setoptions },
8611758Ssam 	{ "crmod",	crmodhelp,	setcrmod },
8711758Ssam 	{ "debug",	debughelp,	setdebug },
8811758Ssam 	{ "?",		helphelp,	help },
896000Sroot 	0
906000Sroot };
916000Sroot 
929972Ssam struct sockaddr_in sin;
936000Sroot 
946000Sroot int	intr(), deadpeer();
956000Sroot char	*control();
966000Sroot struct	cmd *getcmd();
978345Ssam struct	servent *sp;
986000Sroot 
9913076Ssam struct	tchars otc;
10013076Ssam struct	ltchars oltc;
10113076Ssam struct	sgttyb ottyb;
1028378Ssam 
1036000Sroot main(argc, argv)
1046000Sroot 	int argc;
1056000Sroot 	char *argv[];
1066000Sroot {
1078345Ssam 	sp = getservbyname("telnet", "tcp");
1088345Ssam 	if (sp == 0) {
1098345Ssam 		fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
1108345Ssam 		exit(1);
1118345Ssam 	}
11213076Ssam 	ioctl(0, TIOCGETP, (char *)&ottyb);
11313076Ssam 	ioctl(0, TIOCGETC, (char *)&otc);
11413076Ssam 	ioctl(0, TIOCGLTC, (char *)&oltc);
1156000Sroot 	setbuf(stdin, 0);
1166000Sroot 	setbuf(stdout, 0);
1176000Sroot 	prompt = argv[0];
118*17484Sleres 	if (argc > 1 && !strcmp(argv[1], "-d")) {
119*17484Sleres 		debug = 1;
120*17484Sleres 		argv++;
121*17484Sleres 		argc--;
122*17484Sleres 	}
1236000Sroot 	if (argc != 1) {
1246000Sroot 		if (setjmp(toplevel) != 0)
1256000Sroot 			exit(0);
1266000Sroot 		tn(argc, argv);
1276000Sroot 	}
1286000Sroot 	setjmp(toplevel);
1296000Sroot 	for (;;)
1306000Sroot 		command(1);
1316000Sroot }
1326000Sroot 
1338377Ssam char	*hostname;
1348377Ssam char	hnamebuf[32];
1356000Sroot 
1366000Sroot tn(argc, argv)
1376000Sroot 	int argc;
1386000Sroot 	char *argv[];
1396000Sroot {
1406000Sroot 	register int c;
1418377Ssam 	register struct hostent *host;
1426000Sroot 
1436000Sroot 	if (connected) {
1448377Ssam 		printf("?Already connected to %s\n", hostname);
1456000Sroot 		return;
1466000Sroot 	}
1476000Sroot 	if (argc < 2) {
1486000Sroot 		strcpy(line, "Connect ");
1496000Sroot 		printf("(to) ");
1506000Sroot 		gets(&line[strlen(line)]);
1516000Sroot 		makeargv();
1526000Sroot 		argc = margc;
1536000Sroot 		argv = margv;
1546000Sroot 	}
1556000Sroot 	if (argc > 3) {
1566000Sroot 		printf("usage: %s host-name [port]\n", argv[0]);
1576000Sroot 		return;
1586000Sroot 	}
1598345Ssam 	host = gethostbyname(argv[1]);
1608377Ssam 	if (host) {
1619217Ssam 		sin.sin_family = host->h_addrtype;
1629217Ssam 		bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length);
1638377Ssam 		hostname = host->h_name;
1648377Ssam 	} else {
1659217Ssam 		sin.sin_family = AF_INET;
1668377Ssam 		sin.sin_addr.s_addr = inet_addr(argv[1]);
1678377Ssam 		if (sin.sin_addr.s_addr == -1) {
1688377Ssam 			printf("%s: unknown host\n", argv[1]);
1698377Ssam 			return;
1708377Ssam 		}
1718377Ssam 		strcpy(hnamebuf, argv[1]);
1728377Ssam 		hostname = hnamebuf;
1736000Sroot 	}
1748345Ssam 	sin.sin_port = sp->s_port;
1756024Ssam 	if (argc == 3) {
1766024Ssam 		sin.sin_port = atoi(argv[2]);
1776024Ssam 		if (sin.sin_port < 0) {
1786024Ssam 			printf("%s: bad port number\n", argv[2]);
1796024Ssam 			return;
1806024Ssam 		}
1819968Ssam 		sin.sin_port = htons(sin.sin_port);
1826024Ssam 	}
1839287Ssam 	net = socket(AF_INET, SOCK_STREAM, 0, 0);
1849217Ssam 	if (net < 0) {
1859217Ssam 		perror("telnet: socket");
1866000Sroot 		return;
1876000Sroot 	}
188*17484Sleres 	if (debug &&
189*17484Sleres 	    setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0)
19011758Ssam 		perror("setsockopt (SO_DEBUG)");
19112989Ssam 	signal(SIGINT, intr);
19212989Ssam 	signal(SIGPIPE, deadpeer);
1936000Sroot 	printf("Trying...\n");
1949217Ssam 	if (connect(net, (caddr_t)&sin, sizeof (sin), 0) < 0) {
1959217Ssam 		perror("telnet: connect");
19612989Ssam 		signal(SIGINT, SIG_DFL);
1976000Sroot 		return;
1986000Sroot 	}
1996000Sroot 	connected++;
2006000Sroot 	call(status, "status", 0);
2016000Sroot 	if (setjmp(peerdied) == 0)
2026000Sroot 		telnet(net);
2036000Sroot 	fprintf(stderr, "Connection closed by foreign host.\n");
2046000Sroot 	exit(1);
2056000Sroot }
2066000Sroot 
2076000Sroot /*
2086000Sroot  * Print status about the connection.
2096000Sroot  */
2106000Sroot /*VARARGS*/
2116000Sroot status()
2126000Sroot {
2136000Sroot 	if (connected)
2148377Ssam 		printf("Connected to %s.\n", hostname);
2156000Sroot 	else
2166000Sroot 		printf("No connection.\n");
2176000Sroot 	printf("Escape character is '%s'.\n", control(escape));
2189972Ssam 	fflush(stdout);
2196000Sroot }
2206000Sroot 
2216000Sroot makeargv()
2226000Sroot {
2236000Sroot 	register char *cp;
2246000Sroot 	register char **argp = margv;
2256000Sroot 
2266000Sroot 	margc = 0;
2276000Sroot 	for (cp = line; *cp;) {
2286000Sroot 		while (isspace(*cp))
2296000Sroot 			cp++;
2306000Sroot 		if (*cp == '\0')
2316000Sroot 			break;
2326000Sroot 		*argp++ = cp;
2336000Sroot 		margc += 1;
2346000Sroot 		while (*cp != '\0' && !isspace(*cp))
2356000Sroot 			cp++;
2366000Sroot 		if (*cp == '\0')
2376000Sroot 			break;
2386000Sroot 		*cp++ = '\0';
2396000Sroot 	}
2406000Sroot 	*argp++ = 0;
2416000Sroot }
2426000Sroot 
2436000Sroot /*VARARGS*/
2446000Sroot suspend()
2456000Sroot {
2466000Sroot 	register int save;
2476000Sroot 
2486000Sroot 	save = mode(0);
2498378Ssam 	kill(0, SIGTSTP);
2508378Ssam 	/* reget parameters in case they were changed */
25113076Ssam 	ioctl(0, TIOCGETP, (char *)&ottyb);
25213076Ssam 	ioctl(0, TIOCGETC, (char *)&otc);
25313076Ssam 	ioctl(0, TIOCGLTC, (char *)&oltc);
2548378Ssam 	(void) mode(save);
2556000Sroot }
2566000Sroot 
2576000Sroot /*VARARGS*/
2586000Sroot bye()
2596000Sroot {
2608378Ssam 	register char *op;
2616000Sroot 
2628378Ssam 	(void) mode(0);
2636000Sroot 	if (connected) {
26411758Ssam 		shutdown(net, 2);
2656000Sroot 		printf("Connection closed.\n");
2666000Sroot 		close(net);
2676000Sroot 		connected = 0;
2688378Ssam 		/* reset his options */
2698378Ssam 		for (op = hisopts; op < &hisopts[256]; op++)
2708378Ssam 			*op = 0;
2716000Sroot 	}
2726000Sroot }
2736000Sroot 
2746000Sroot /*VARARGS*/
2756000Sroot quit()
2766000Sroot {
2776000Sroot 	call(bye, "bye", 0);
2786000Sroot 	exit(0);
2796000Sroot }
2806000Sroot 
2816000Sroot /*
2826000Sroot  * Help command.
2836000Sroot  */
2846000Sroot help(argc, argv)
2856000Sroot 	int argc;
2866000Sroot 	char *argv[];
2876000Sroot {
2886000Sroot 	register struct cmd *c;
2896000Sroot 
2906000Sroot 	if (argc == 1) {
2916000Sroot 		printf("Commands may be abbreviated.  Commands are:\n\n");
2926000Sroot 		for (c = cmdtab; c->name; c++)
2936000Sroot 			printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
2946000Sroot 		return;
2956000Sroot 	}
2966000Sroot 	while (--argc > 0) {
2976000Sroot 		register char *arg;
2986000Sroot 		arg = *++argv;
2996000Sroot 		c = getcmd(arg);
3006000Sroot 		if (c == (struct cmd *)-1)
3016000Sroot 			printf("?Ambiguous help command %s\n", arg);
3026000Sroot 		else if (c == (struct cmd *)0)
3036000Sroot 			printf("?Invalid help command %s\n", arg);
3046000Sroot 		else
3056000Sroot 			printf("%s\n", c->help);
3066000Sroot 	}
3076000Sroot }
3086000Sroot 
3096000Sroot /*
3106000Sroot  * Call routine with argc, argv set from args (terminated by 0).
3116000Sroot  * VARARGS2
3126000Sroot  */
3136000Sroot call(routine, args)
3146000Sroot 	int (*routine)();
3156000Sroot 	int args;
3166000Sroot {
3176000Sroot 	register int *argp;
3186000Sroot 	register int argc;
3196000Sroot 
3206000Sroot 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
3216000Sroot 		;
3226000Sroot 	(*routine)(argc, &args);
3236000Sroot }
3246000Sroot 
32513076Ssam struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
32613076Ssam struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
3279972Ssam 
3286000Sroot mode(f)
3296000Sroot 	register int f;
3306000Sroot {
3318378Ssam 	static int prevmode = 0;
33213076Ssam 	struct tchars *tc;
33313076Ssam 	struct ltchars *ltc;
33413076Ssam 	struct sgttyb sb;
33513076Ssam 	int onoff, old;
3366000Sroot 
3378378Ssam 	if (prevmode == f)
3388378Ssam 		return (f);
3398378Ssam 	old = prevmode;
3408378Ssam 	prevmode = f;
34113076Ssam 	sb = ottyb;
3426000Sroot 	switch (f) {
3438378Ssam 
3446000Sroot 	case 0:
3456000Sroot 		onoff = 0;
3469972Ssam 		tc = &otc;
34713076Ssam 		ltc = &oltc;
3486000Sroot 		break;
3496000Sroot 
3506000Sroot 	case 1:
3516000Sroot 	case 2:
35213076Ssam 		sb.sg_flags |= CBREAK;
3538378Ssam 		if (f == 1)
35413076Ssam 			sb.sg_flags &= ~(ECHO|CRMOD);
3558378Ssam 		else
35613076Ssam 			sb.sg_flags |= ECHO|CRMOD;
35713076Ssam 		sb.sg_erase = sb.sg_kill = -1;
3589972Ssam 		tc = &notc;
35913076Ssam 		ltc = &noltc;
3606000Sroot 		onoff = 1;
3619972Ssam 		break;
3629972Ssam 
3639972Ssam 	default:
3649972Ssam 		return;
3656000Sroot 	}
36613076Ssam 	ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
36713076Ssam 	ioctl(fileno(stdin), TIOCSETC, (char *)tc);
36813076Ssam 	ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
3696000Sroot 	ioctl(fileno(stdin), FIONBIO, &onoff);
3706000Sroot 	ioctl(fileno(stdout), FIONBIO, &onoff);
3716000Sroot 	return (old);
3726000Sroot }
3736000Sroot 
3746000Sroot char	sibuf[BUFSIZ], *sbp;
3756000Sroot char	tibuf[BUFSIZ], *tbp;
3766000Sroot int	scc, tcc;
3776000Sroot 
3786000Sroot /*
3796000Sroot  * Select from tty and network...
3806000Sroot  */
3816000Sroot telnet(s)
3826000Sroot 	int s;
3836000Sroot {
3846000Sroot 	register int c;
3856000Sroot 	int tin = fileno(stdin), tout = fileno(stdout);
3866000Sroot 	int on = 1;
3876000Sroot 
3888378Ssam 	(void) mode(2);
3896000Sroot 	ioctl(s, FIONBIO, &on);
3906000Sroot 	for (;;) {
3916000Sroot 		int ibits = 0, obits = 0;
3926000Sroot 
3936000Sroot 		if (nfrontp - nbackp)
3946000Sroot 			obits |= (1 << s);
3956000Sroot 		else
3966000Sroot 			ibits |= (1 << tin);
3976000Sroot 		if (tfrontp - tbackp)
3986000Sroot 			obits |= (1 << tout);
3996000Sroot 		else
4006000Sroot 			ibits |= (1 << s);
4016000Sroot 		if (scc < 0 && tcc < 0)
4026000Sroot 			break;
4039217Ssam 		select(16, &ibits, &obits, 0, 0);
4046000Sroot 		if (ibits == 0 && obits == 0) {
4056000Sroot 			sleep(5);
4066000Sroot 			continue;
4076000Sroot 		}
4086000Sroot 
4096000Sroot 		/*
4106000Sroot 		 * Something to read from the network...
4116000Sroot 		 */
4126000Sroot 		if (ibits & (1 << s)) {
4138378Ssam 			scc = read(s, sibuf, sizeof (sibuf));
4146000Sroot 			if (scc < 0 && errno == EWOULDBLOCK)
4156000Sroot 				scc = 0;
4166000Sroot 			else {
4176000Sroot 				if (scc <= 0)
4186000Sroot 					break;
4196000Sroot 				sbp = sibuf;
4206000Sroot 			}
4216000Sroot 		}
4226000Sroot 
4236000Sroot 		/*
4246000Sroot 		 * Something to read from the tty...
4256000Sroot 		 */
4266000Sroot 		if (ibits & (1 << tin)) {
4278378Ssam 			tcc = read(tin, tibuf, sizeof (tibuf));
4286000Sroot 			if (tcc < 0 && errno == EWOULDBLOCK)
4296000Sroot 				tcc = 0;
4306000Sroot 			else {
4316000Sroot 				if (tcc <= 0)
4326000Sroot 					break;
4336000Sroot 				tbp = tibuf;
4346000Sroot 			}
4356000Sroot 		}
4366000Sroot 
4376000Sroot 		while (tcc > 0) {
4386000Sroot 			register int c;
4396000Sroot 
4406000Sroot 			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
4416000Sroot 				break;
4426000Sroot 			c = *tbp++ & 0377, tcc--;
4436000Sroot 			if (strip(c) == escape) {
4446000Sroot 				command(0);
4456000Sroot 				tcc = 0;
4466000Sroot 				break;
4476000Sroot 			}
44811758Ssam 			if (c == IAC)
44911758Ssam 				*nfrontp++ = c;
4506000Sroot 			*nfrontp++ = c;
4516000Sroot 		}
4526000Sroot 		if ((obits & (1 << s)) && (nfrontp - nbackp) > 0)
4536000Sroot 			netflush(s);
4546000Sroot 		if (scc > 0)
4556000Sroot 			telrcv();
4566000Sroot 		if ((obits & (1 << tout)) && (tfrontp - tbackp) > 0)
4576000Sroot 			ttyflush(tout);
4586000Sroot 	}
4598378Ssam 	(void) mode(0);
4606000Sroot }
4616000Sroot 
4626000Sroot command(top)
4636000Sroot 	int top;
4646000Sroot {
4656000Sroot 	register struct cmd *c;
4666000Sroot 	int oldmode, wasopen;
4676000Sroot 
4686000Sroot 	oldmode = mode(0);
4696000Sroot 	if (!top)
4706000Sroot 		putchar('\n');
4716000Sroot 	else
47212989Ssam 		signal(SIGINT, SIG_DFL);
4736000Sroot 	for (;;) {
4746000Sroot 		printf("%s> ", prompt);
47513997Ssam 		if (gets(line) == 0) {
47613997Ssam 			if (feof(stdin)) {
47713997Ssam 				clearerr(stdin);
47813997Ssam 				putchar('\n');
47913997Ssam 			}
4806000Sroot 			break;
48113997Ssam 		}
4826000Sroot 		if (line[0] == 0)
4836000Sroot 			break;
4846000Sroot 		makeargv();
4856000Sroot 		c = getcmd(margv[0]);
4866000Sroot 		if (c == (struct cmd *)-1) {
4876000Sroot 			printf("?Ambiguous command\n");
4886000Sroot 			continue;
4896000Sroot 		}
4906000Sroot 		if (c == 0) {
4916000Sroot 			printf("?Invalid command\n");
4926000Sroot 			continue;
4936000Sroot 		}
4946000Sroot 		(*c->handler)(margc, margv);
4956000Sroot 		if (c->handler != help)
4966000Sroot 			break;
4976000Sroot 	}
4986000Sroot 	if (!top) {
4996000Sroot 		if (!connected)
5006000Sroot 			longjmp(toplevel, 1);
5018378Ssam 		(void) mode(oldmode);
5026000Sroot 	}
5036000Sroot }
5046000Sroot 
5056000Sroot /*
5066000Sroot  * Telnet receiver states for fsm
5076000Sroot  */
5086000Sroot #define	TS_DATA		0
5096000Sroot #define	TS_IAC		1
5106000Sroot #define	TS_WILL		2
5116000Sroot #define	TS_WONT		3
5126000Sroot #define	TS_DO		4
5136000Sroot #define	TS_DONT		5
5146000Sroot 
5156000Sroot telrcv()
5166000Sroot {
5176000Sroot 	register int c;
5186000Sroot 	static int state = TS_DATA;
5196000Sroot 
5206000Sroot 	while (scc > 0) {
5216000Sroot 		c = *sbp++ & 0377, scc--;
5226000Sroot 		switch (state) {
5236000Sroot 
5246000Sroot 		case TS_DATA:
5259972Ssam 			if (c == IAC) {
5266000Sroot 				state = TS_IAC;
5279972Ssam 				continue;
5289972Ssam 			}
5299972Ssam 			*tfrontp++ = c;
5309972Ssam 			/*
5319972Ssam 			 * This hack is needed since we can't set
5329972Ssam 			 * CRMOD on output only.  Machines like MULTICS
5339972Ssam 			 * like to send \r without \n; since we must
5349972Ssam 			 * turn off CRMOD to get proper input, the mapping
5359972Ssam 			 * is done here (sigh).
5369972Ssam 			 */
5379972Ssam 			if (c == '\r' && crmod)
5389972Ssam 				*tfrontp++ = '\n';
5396000Sroot 			continue;
5406000Sroot 
5416000Sroot 		case TS_IAC:
5426000Sroot 			switch (c) {
5436000Sroot 
5446000Sroot 			case WILL:
5456000Sroot 				state = TS_WILL;
5466000Sroot 				continue;
5476000Sroot 
5486000Sroot 			case WONT:
5496000Sroot 				state = TS_WONT;
5506000Sroot 				continue;
5516000Sroot 
5526000Sroot 			case DO:
5536000Sroot 				state = TS_DO;
5546000Sroot 				continue;
5556000Sroot 
5566000Sroot 			case DONT:
5576000Sroot 				state = TS_DONT;
5586000Sroot 				continue;
5596000Sroot 
5606000Sroot 			case DM:
5616000Sroot 				ioctl(fileno(stdout), TIOCFLUSH, 0);
5626000Sroot 				break;
5636000Sroot 
5646000Sroot 			case NOP:
5656000Sroot 			case GA:
5666000Sroot 				break;
5676000Sroot 
5686000Sroot 			default:
5696000Sroot 				break;
5706000Sroot 			}
5716000Sroot 			state = TS_DATA;
5726000Sroot 			continue;
5736000Sroot 
5746000Sroot 		case TS_WILL:
5758345Ssam 			printoption("RCVD", will, c, !hisopts[c]);
5766000Sroot 			if (!hisopts[c])
5776000Sroot 				willoption(c);
5786000Sroot 			state = TS_DATA;
5796000Sroot 			continue;
5806000Sroot 
5816000Sroot 		case TS_WONT:
5828345Ssam 			printoption("RCVD", wont, c, hisopts[c]);
5836000Sroot 			if (hisopts[c])
5846000Sroot 				wontoption(c);
5856000Sroot 			state = TS_DATA;
5866000Sroot 			continue;
5876000Sroot 
5886000Sroot 		case TS_DO:
5898345Ssam 			printoption("RCVD", doopt, c, !myopts[c]);
5906000Sroot 			if (!myopts[c])
5916000Sroot 				dooption(c);
5926000Sroot 			state = TS_DATA;
5936000Sroot 			continue;
5946000Sroot 
5956000Sroot 		case TS_DONT:
5968345Ssam 			printoption("RCVD", dont, c, myopts[c]);
5976000Sroot 			if (myopts[c]) {
5986000Sroot 				myopts[c] = 0;
5996000Sroot 				sprintf(nfrontp, wont, c);
6008378Ssam 				nfrontp += sizeof (wont) - 2;
6018345Ssam 				printoption("SENT", wont, c);
6026000Sroot 			}
6036000Sroot 			state = TS_DATA;
6046000Sroot 			continue;
6056000Sroot 		}
6066000Sroot 	}
6076000Sroot }
6086000Sroot 
6096000Sroot willoption(option)
6106000Sroot 	int option;
6116000Sroot {
6126000Sroot 	char *fmt;
6136000Sroot 
6146000Sroot 	switch (option) {
6156000Sroot 
6166000Sroot 	case TELOPT_ECHO:
6178378Ssam 		(void) mode(1);
6186000Sroot 
6196000Sroot 	case TELOPT_SGA:
6206000Sroot 		hisopts[option] = 1;
6216000Sroot 		fmt = doopt;
6226000Sroot 		break;
6236000Sroot 
6246000Sroot 	case TELOPT_TM:
6256000Sroot 		fmt = dont;
6266000Sroot 		break;
6276000Sroot 
6286000Sroot 	default:
6296000Sroot 		fmt = dont;
6306000Sroot 		break;
6316000Sroot 	}
6326024Ssam 	sprintf(nfrontp, fmt, option);
6338378Ssam 	nfrontp += sizeof (dont) - 2;
6348345Ssam 	printoption("SENT", fmt, option);
6356000Sroot }
6366000Sroot 
6376000Sroot wontoption(option)
6386000Sroot 	int option;
6396000Sroot {
6406000Sroot 	char *fmt;
6416000Sroot 
6426000Sroot 	switch (option) {
6436000Sroot 
6446000Sroot 	case TELOPT_ECHO:
6458378Ssam 		(void) mode(2);
6466000Sroot 
6476000Sroot 	case TELOPT_SGA:
6486000Sroot 		hisopts[option] = 0;
6496000Sroot 		fmt = dont;
6506000Sroot 		break;
6516000Sroot 
6526000Sroot 	default:
6536000Sroot 		fmt = dont;
6546000Sroot 	}
6556000Sroot 	sprintf(nfrontp, fmt, option);
6568378Ssam 	nfrontp += sizeof (doopt) - 2;
6578345Ssam 	printoption("SENT", fmt, option);
6586000Sroot }
6596000Sroot 
6606000Sroot dooption(option)
6616000Sroot 	int option;
6626000Sroot {
6636000Sroot 	char *fmt;
6646000Sroot 
6656000Sroot 	switch (option) {
6666000Sroot 
6676000Sroot 	case TELOPT_TM:
6686000Sroot 		fmt = wont;
6696000Sroot 		break;
6706000Sroot 
67113231Ssam 	case TELOPT_ECHO:
67213231Ssam 		(void) mode(2);
67313231Ssam 		fmt = will;
67413231Ssam 		hisopts[option] = 0;
67513231Ssam 		break;
67613231Ssam 
6776000Sroot 	case TELOPT_SGA:
6786000Sroot 		fmt = will;
6796000Sroot 		break;
6806000Sroot 
6816000Sroot 	default:
6826000Sroot 		fmt = wont;
6836000Sroot 		break;
6846000Sroot 	}
6856000Sroot 	sprintf(nfrontp, fmt, option);
6868378Ssam 	nfrontp += sizeof (doopt) - 2;
6878345Ssam 	printoption("SENT", fmt, option);
6886000Sroot }
6896000Sroot 
6906000Sroot /*
6916000Sroot  * Set the escape character.
6926000Sroot  */
6936000Sroot setescape(argc, argv)
6946000Sroot 	int argc;
6956000Sroot 	char *argv[];
6966000Sroot {
6976000Sroot 	register char *arg;
6986000Sroot 	char buf[50];
6996000Sroot 
7006000Sroot 	if (argc > 2)
7016000Sroot 		arg = argv[1];
7026000Sroot 	else {
7036000Sroot 		printf("new escape character: ");
7046000Sroot 		gets(buf);
7056000Sroot 		arg = buf;
7066000Sroot 	}
7076000Sroot 	if (arg[0] != '\0')
7086000Sroot 		escape = arg[0];
7096000Sroot 	printf("Escape character is '%s'.\n", control(escape));
7109972Ssam 	fflush(stdout);
7116000Sroot }
7126000Sroot 
7136024Ssam /*VARARGS*/
7146024Ssam setoptions()
7156024Ssam {
7169972Ssam 
7176024Ssam 	showoptions = !showoptions;
7186024Ssam 	printf("%s show option processing.\n", showoptions ? "Will" : "Wont");
7199972Ssam 	fflush(stdout);
7206024Ssam }
7216024Ssam 
7229972Ssam /*VARARGS*/
7239972Ssam setcrmod()
7249972Ssam {
7259972Ssam 
7269972Ssam 	crmod = !crmod;
7279972Ssam 	printf("%s map carriage return on output.\n", crmod ? "Will" : "Wont");
7289972Ssam 	fflush(stdout);
7299972Ssam }
7309972Ssam 
73110339Ssam /*VARARGS*/
73210339Ssam setdebug()
73310339Ssam {
73410339Ssam 
735*17484Sleres 	debug = debug ? 0 : 1;
73610339Ssam 	printf("%s turn on socket level debugging.\n",
73710339Ssam 		debug ? "Will" : "Wont");
73810339Ssam 	fflush(stdout);
739*17484Sleres 	if (net > 0 &&
740*17484Sleres 	    setsockopt(net, SOL_SOCKET, SO_DEBUG, &debug, sizeof(debug)) < 0)
74111758Ssam 		perror("setsockopt (SO_DEBUG)");
74210339Ssam }
74310339Ssam 
7446000Sroot /*
7456000Sroot  * Construct a control character sequence
7466000Sroot  * for a special character.
7476000Sroot  */
7486000Sroot char *
7496000Sroot control(c)
7506000Sroot 	register int c;
7516000Sroot {
7526000Sroot 	static char buf[3];
7536000Sroot 
7546000Sroot 	if (c == 0177)
7556000Sroot 		return ("^?");
7566000Sroot 	if (c >= 040) {
7576000Sroot 		buf[0] = c;
7586000Sroot 		buf[1] = 0;
7596000Sroot 	} else {
7606000Sroot 		buf[0] = '^';
7616000Sroot 		buf[1] = '@'+c;
7626000Sroot 		buf[2] = 0;
7636000Sroot 	}
7646000Sroot 	return (buf);
7656000Sroot }
7666000Sroot 
7676000Sroot struct cmd *
7686000Sroot getcmd(name)
7696000Sroot 	register char *name;
7706000Sroot {
7716000Sroot 	register char *p, *q;
7726000Sroot 	register struct cmd *c, *found;
7736000Sroot 	register int nmatches, longest;
7746000Sroot 
7756000Sroot 	longest = 0;
7766000Sroot 	nmatches = 0;
7776000Sroot 	found = 0;
7786000Sroot 	for (c = cmdtab; p = c->name; c++) {
7796000Sroot 		for (q = name; *q == *p++; q++)
7806000Sroot 			if (*q == 0)		/* exact match? */
7816000Sroot 				return (c);
7826000Sroot 		if (!*q) {			/* the name was a prefix */
7836000Sroot 			if (q - name > longest) {
7846000Sroot 				longest = q - name;
7856000Sroot 				nmatches = 1;
7866000Sroot 				found = c;
7876000Sroot 			} else if (q - name == longest)
7886000Sroot 				nmatches++;
7896000Sroot 		}
7906000Sroot 	}
7916000Sroot 	if (nmatches > 1)
7926000Sroot 		return ((struct cmd *)-1);
7936000Sroot 	return (found);
7946000Sroot }
7956000Sroot 
7966000Sroot deadpeer()
7976000Sroot {
7988378Ssam 	(void) mode(0);
7996000Sroot 	longjmp(peerdied, -1);
8006000Sroot }
8016000Sroot 
8026000Sroot intr()
8036000Sroot {
8048378Ssam 	(void) mode(0);
8056000Sroot 	longjmp(toplevel, -1);
8066000Sroot }
8076000Sroot 
8086000Sroot ttyflush(fd)
8096000Sroot {
8106000Sroot 	int n;
8116000Sroot 
8126000Sroot 	if ((n = tfrontp - tbackp) > 0)
8136000Sroot 		n = write(fd, tbackp, n);
8148345Ssam 	if (n < 0)
8158345Ssam 		return;
8166000Sroot 	tbackp += n;
8176000Sroot 	if (tbackp == tfrontp)
8186000Sroot 		tbackp = tfrontp = ttyobuf;
8196000Sroot }
8206000Sroot 
8216000Sroot netflush(fd)
8226000Sroot {
8236000Sroot 	int n;
8246000Sroot 
8256000Sroot 	if ((n = nfrontp - nbackp) > 0)
8266000Sroot 		n = write(fd, nbackp, n);
8276501Ssam 	if (n < 0) {
8286501Ssam 		if (errno != ENOBUFS && errno != EWOULDBLOCK) {
8298378Ssam 			(void) mode(0);
8308377Ssam 			perror(hostname);
8316501Ssam 			close(fd);
8326501Ssam 			longjmp(peerdied, -1);
8336501Ssam 			/*NOTREACHED*/
8346501Ssam 		}
8356000Sroot 		n = 0;
8366501Ssam 	}
8376000Sroot 	nbackp += n;
8386000Sroot 	if (nbackp == nfrontp)
8396000Sroot 		nbackp = nfrontp = netobuf;
8406000Sroot }
8416024Ssam 
8426293Sroot /*VARARGS*/
8436293Sroot printoption(direction, fmt, option, what)
8446024Ssam 	char *direction, *fmt;
8456293Sroot 	int option, what;
8466024Ssam {
8478345Ssam 	if (!showoptions)
8488345Ssam 		return;
8496024Ssam 	printf("%s ", direction);
8506024Ssam 	if (fmt == doopt)
8516024Ssam 		fmt = "do";
8526024Ssam 	else if (fmt == dont)
8536024Ssam 		fmt = "dont";
8546024Ssam 	else if (fmt == will)
8556024Ssam 		fmt = "will";
8566024Ssam 	else if (fmt == wont)
8576024Ssam 		fmt = "wont";
8586024Ssam 	else
8596024Ssam 		fmt = "???";
8606024Ssam 	if (option < TELOPT_SUPDUP)
8616293Sroot 		printf("%s %s", fmt, telopts[option]);
8626024Ssam 	else
8636293Sroot 		printf("%s %d", fmt, option);
8646293Sroot 	if (*direction == '<') {
8656293Sroot 		printf("\r\n");
8666293Sroot 		return;
8676293Sroot 	}
8686293Sroot 	printf(" (%s)\r\n", what ? "reply" : "don't reply");
8696024Ssam }
870