xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 27261)
121580Sdist /*
221580Sdist  * Copyright (c) 1983 Regents of the University of California.
321580Sdist  * All rights reserved.  The Berkeley software License Agreement
421580Sdist  * specifies the terms and conditions for redistribution.
521580Sdist  */
621580Sdist 
711758Ssam #ifndef lint
821580Sdist char copyright[] =
921580Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\
1021580Sdist  All rights reserved.\n";
1121580Sdist #endif not lint
1211758Ssam 
1321580Sdist #ifndef lint
14*27261Sminshall static char sccsid[] = "@(#)telnet.c	5.12 (Berkeley) 04/22/86";
1521580Sdist #endif not lint
1621580Sdist 
176000Sroot /*
186000Sroot  * User telnet program.
196000Sroot  */
209217Ssam #include <sys/types.h>
219217Ssam #include <sys/socket.h>
229972Ssam #include <sys/ioctl.h>
2327178Sminshall #include <sys/time.h>
249217Ssam 
259217Ssam #include <netinet/in.h>
269217Ssam 
2712212Ssam #define	TELOPTS
2812212Ssam #include <arpa/telnet.h>
2927186Sminshall #include <arpa/inet.h>
3012212Ssam 
316000Sroot #include <stdio.h>
326000Sroot #include <ctype.h>
336000Sroot #include <errno.h>
346000Sroot #include <signal.h>
356000Sroot #include <setjmp.h>
368345Ssam #include <netdb.h>
3727186Sminshall #include <strings.h>
389217Ssam 
3927178Sminshall 
4027178Sminshall 
4127178Sminshall /*
4227178Sminshall  * The following is defined just in case someone should want to run
4327178Sminshall  * this telnet on a 4.2 system.
4427178Sminshall  *
4527178Sminshall  * This has never been tested, so good luck...
4627178Sminshall  */
4727178Sminshall #ifndef	FD_SETSIZE
4827178Sminshall 
4927178Sminshall typedef long	fd_set;
5027178Sminshall 
5127178Sminshall #define	FD_SET(n, p)	(*(p) |= (1<<(n)))
5227178Sminshall #define	FD_CLR(n, p)	(*(p) &= ~(1<<(n)))
5327178Sminshall #define	FD_ISSET(n, p)	(*(p) & (1<<(n)))
5427178Sminshall #define FD_ZERO(p)	(*(p) = 0)
5527178Sminshall 
5627178Sminshall #endif
5727178Sminshall 
5827228Sminshall #define	strip(x)	((x)&0x7f)
596000Sroot 
6027228Sminshall char	ttyobuf[2*BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
6127228Sminshall #define	TTYADD(c)	{ if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
6227228Sminshall #define	TTYLOC()	(tfrontp)
6327228Sminshall #define	TTYMAX()	(ttyobuf+sizeof ttyobuf-1)
6427228Sminshall #define	TTYMIN()	(netobuf)
6527228Sminshall #define	TTYBYTES()	(tfrontp-tbackp)
6627228Sminshall #define	TTYROOM()	(TTYMAX()-TTYLOC()+1)
6727088Sminshall 
6827228Sminshall char	netobuf[2*BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
6927088Sminshall #define	NETADD(c)	{ *nfrontp++ = c; }
7027088Sminshall #define	NET2ADD(c1,c2)	{ NETADD(c1); NETADD(c2); }
7127088Sminshall #define NETLOC()	(nfrontp)
7227228Sminshall #define	NETMAX()	(netobuf+sizeof netobuf-1)
7327228Sminshall #define	NETBYTES()	(nfrontp-nbackp)
7427228Sminshall #define	NETROOM()	(NETMAX()-NETLOC()+1)
7527088Sminshall char	*neturg = 0;		/* one past last byte of urgent data */
766000Sroot 
776000Sroot char	hisopts[256];
786000Sroot char	myopts[256];
796000Sroot 
806000Sroot char	doopt[] = { IAC, DO, '%', 'c', 0 };
816000Sroot char	dont[] = { IAC, DONT, '%', 'c', 0 };
826000Sroot char	will[] = { IAC, WILL, '%', 'c', 0 };
836000Sroot char	wont[] = { IAC, WONT, '%', 'c', 0 };
846000Sroot 
8527088Sminshall struct cmd {
8627088Sminshall 	char	*name;		/* command name */
8727088Sminshall 	char	*help;		/* help string */
8827088Sminshall 	int	(*handler)();	/* routine which executes command */
8927088Sminshall 	int	dohelp;		/* Should we give general help information? */
9027088Sminshall 	int	needconnect;	/* Do we need to be connected to execute? */
9127088Sminshall };
9227088Sminshall 
936000Sroot int	connected;
946000Sroot int	net;
9527088Sminshall int	tout;
969972Ssam int	showoptions = 0;
9710339Ssam int	debug = 0;
989972Ssam int	crmod = 0;
9927088Sminshall int	netdata = 0;
10027021Sminshall static FILE	*NetTrace;
10125289Skarels int	telnetport = 1;
10227088Sminshall 
10327088Sminshall 
1046000Sroot char	*prompt;
1059972Ssam char	escape = CTRL(]);
10627110Sminshall char	echoc = CTRL(E);
1076000Sroot 
10827186Sminshall int	SYNCHing = 0;		/* we are in TELNET SYNCH mode */
10927186Sminshall int	flushout = 0;		/* flush output */
11027228Sminshall int	autoflush = 0;		/* flush output when interrupting? */
11127186Sminshall int	autosynch = 0;		/* send interrupt characters with SYNCH? */
112*27261Sminshall int	localchars = 0;		/* we recognize interrupt/quit */
113*27261Sminshall int	donelclchars = 0;	/* the user has set "localchars" */
11427186Sminshall int	dontlecho = 0;		/* do we suppress local echoing right now? */
11527186Sminshall 
1166000Sroot char	line[200];
1176000Sroot int	margc;
1186000Sroot char	*margv[20];
1196000Sroot 
1206000Sroot jmp_buf	toplevel;
1216000Sroot jmp_buf	peerdied;
1226000Sroot 
1236000Sroot extern	int errno;
1246000Sroot 
1256000Sroot 
1269972Ssam struct sockaddr_in sin;
1276000Sroot 
1286000Sroot struct	cmd *getcmd();
1298345Ssam struct	servent *sp;
1306000Sroot 
13127110Sminshall struct	tchars otc, ntc;
13227228Sminshall struct	ltchars oltc, nltc;
13327110Sminshall struct	sgttyb ottyb, nttyb;
13427110Sminshall int	globalmode = 0;
13527110Sminshall int	flushline = 1;
1368378Ssam 
13727110Sminshall char	*hostname;
13827110Sminshall char	hnamebuf[32];
13927110Sminshall 
14027110Sminshall /*
14127110Sminshall  * The following are some clocks used to decide how to interpret
14227178Sminshall  * the relationship between various variables.
14327110Sminshall  */
14427110Sminshall 
14527110Sminshall struct {
14627110Sminshall     int
14727110Sminshall 	system,			/* what the current time is */
14827110Sminshall 	echotoggle,		/* last time user entered echo character */
14927178Sminshall 	modenegotiated,		/* last time operating mode negotiated */
15027178Sminshall 	didnetreceive,		/* last time we read data from network */
15127178Sminshall 	gotDM;			/* when did we last see a data mark */
15227186Sminshall } clocks;
15327110Sminshall 
15427186Sminshall #define	settimer(x)	clocks.x = clocks.system++
15527110Sminshall 
15627110Sminshall /*
15727110Sminshall  * Various utility routines.
15827110Sminshall  */
1596000Sroot 
16027186Sminshall char *ambiguous;		/* special return value */
16127186Sminshall #define Ambiguous(t)	((t)&ambiguous)
16227186Sminshall 
16327186Sminshall 
16427088Sminshall char **
16527088Sminshall genget(name, table, next)
16627088Sminshall char	*name;		/* name to match */
16727088Sminshall char	**table;		/* name entry in table */
16827088Sminshall char	**(*next)();	/* routine to return next entry in table */
1696000Sroot {
17027088Sminshall 	register char *p, *q;
17127088Sminshall 	register char **c, **found;
17227088Sminshall 	register int nmatches, longest;
1736000Sroot 
17427088Sminshall 	longest = 0;
17527088Sminshall 	nmatches = 0;
17627088Sminshall 	found = 0;
17727088Sminshall 	for (c = table; p = *c; c = (*next)(c)) {
17827088Sminshall 		for (q = name; *q == *p++; q++)
17927088Sminshall 			if (*q == 0)		/* exact match? */
18027088Sminshall 				return (c);
18127088Sminshall 		if (!*q) {			/* the name was a prefix */
18227088Sminshall 			if (q - name > longest) {
18327088Sminshall 				longest = q - name;
18427088Sminshall 				nmatches = 1;
18527088Sminshall 				found = c;
18627088Sminshall 			} else if (q - name == longest)
18727088Sminshall 				nmatches++;
1888377Ssam 		}
1896000Sroot 	}
19027088Sminshall 	if (nmatches > 1)
19127186Sminshall 		return Ambiguous(char **);
19227088Sminshall 	return (found);
1936000Sroot }
1946000Sroot 
19527110Sminshall /*
19627110Sminshall  * Make a character string into a number.
19727110Sminshall  *
19827186Sminshall  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
19927110Sminshall  */
2006000Sroot 
20127110Sminshall special(s)
20227110Sminshall register char *s;
20327110Sminshall {
20427110Sminshall 	register char c;
20527110Sminshall 	char b;
20627110Sminshall 
20727110Sminshall 	switch (*s) {
20827110Sminshall 	case '^':
20927110Sminshall 		b = *++s;
21027110Sminshall 		if (b == '?') {
21127228Sminshall 		    c = b | 0x40;		/* DEL */
21227110Sminshall 		} else {
21327110Sminshall 		    c = b & 0x1f;
21427110Sminshall 		}
21527110Sminshall 		break;
21627110Sminshall 	default:
21727110Sminshall 		c = *s;
21827110Sminshall 		break;
21927110Sminshall 	}
22027110Sminshall 	return c;
22127110Sminshall }
22227186Sminshall 
22327186Sminshall /*
22427186Sminshall  * Construct a control character sequence
22527186Sminshall  * for a special character.
22627186Sminshall  */
22727186Sminshall char *
22827186Sminshall control(c)
22927186Sminshall 	register int c;
23027186Sminshall {
23127186Sminshall 	static char buf[3];
23227186Sminshall 
23327228Sminshall 	if (c == 0x7f)
23427186Sminshall 		return ("^?");
23527186Sminshall 	if (c == '\377') {
23627186Sminshall 		return "off";
23727186Sminshall 	}
23827186Sminshall 	if (c >= 0x20) {
23927186Sminshall 		buf[0] = c;
24027186Sminshall 		buf[1] = 0;
24127186Sminshall 	} else {
24227186Sminshall 		buf[0] = '^';
24327186Sminshall 		buf[1] = '@'+c;
24427186Sminshall 		buf[2] = 0;
24527186Sminshall 	}
24627186Sminshall 	return (buf);
24727186Sminshall }
24827110Sminshall 
24927110Sminshall /*
25027186Sminshall  * Check to see if any out-of-band data exists on a socket (for
25127186Sminshall  * Telnet "synch" processing).
25227186Sminshall  */
25327186Sminshall 
25427186Sminshall int
25527186Sminshall stilloob(s)
25627186Sminshall int	s;		/* socket number */
25727186Sminshall {
25827186Sminshall     static struct timeval timeout = { 0 };
25927186Sminshall     fd_set	excepts;
26027186Sminshall     int value;
26127186Sminshall 
26227186Sminshall     do {
26327186Sminshall 	FD_ZERO(&excepts);
26427186Sminshall 	FD_SET(s, &excepts);
26527186Sminshall 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
26627186Sminshall     } while ((value == -1) && (errno = EINTR));
26727186Sminshall 
26827186Sminshall     if (value < 0) {
26927186Sminshall 	perror("select");
27027186Sminshall 	quit();
27127186Sminshall     }
27227186Sminshall     if (FD_ISSET(s, &excepts)) {
27327186Sminshall 	return 1;
27427186Sminshall     } else {
27527186Sminshall 	return 0;
27627186Sminshall     }
27727186Sminshall }
27827186Sminshall 
27927186Sminshall 
28027186Sminshall /*
28127186Sminshall  *  netflush
28227186Sminshall  *		Send as much data as possible to the network,
28327186Sminshall  *	handling requests for urgent data.
28427186Sminshall  */
28527186Sminshall 
28627186Sminshall 
28727186Sminshall netflush(fd)
28827186Sminshall {
28927186Sminshall     int n;
29027186Sminshall 
29127186Sminshall     if ((n = nfrontp - nbackp) > 0) {
29227186Sminshall 	if (!neturg) {
29327186Sminshall 	    n = write(fd, nbackp, n);	/* normal write */
29427186Sminshall 	} else {
29527186Sminshall 	    n = neturg - nbackp;
29627186Sminshall 	    /*
29727186Sminshall 	     * In 4.2 (and 4.3) systems, there is some question about
29827186Sminshall 	     * what byte in a sendOOB operation is the "OOB" data.
29927186Sminshall 	     * To make ourselves compatible, we only send ONE byte
30027186Sminshall 	     * out of band, the one WE THINK should be OOB (though
30127186Sminshall 	     * we really have more the TCP philosophy of urgent data
30227186Sminshall 	     * rather than the Unix philosophy of OOB data).
30327186Sminshall 	     */
30427186Sminshall 	    if (n > 1) {
30527186Sminshall 		n = send(fd, nbackp, n-1, 0);	/* send URGENT all by itself */
30627186Sminshall 	    } else {
30727186Sminshall 		n = send(fd, nbackp, n, MSG_OOB);	/* URGENT data */
30827186Sminshall 	    }
30927186Sminshall 	}
31027186Sminshall     }
31127186Sminshall     if (n < 0) {
31227186Sminshall 	if (errno != ENOBUFS && errno != EWOULDBLOCK) {
31327186Sminshall 	    setcommandmode();
31427186Sminshall 	    perror(hostname);
31527186Sminshall 	    close(fd);
31627186Sminshall 	    neturg = 0;
31727186Sminshall 	    longjmp(peerdied, -1);
31827186Sminshall 	    /*NOTREACHED*/
31927186Sminshall 	}
32027186Sminshall 	n = 0;
32127186Sminshall     }
32227186Sminshall     if (netdata && n) {
32327186Sminshall 	Dump('>', nbackp, n);
32427186Sminshall     }
32527186Sminshall     nbackp += n;
32627186Sminshall     if (nbackp >= neturg) {
32727186Sminshall 	neturg = 0;
32827186Sminshall     }
32927186Sminshall     if (nbackp == nfrontp) {
33027186Sminshall 	nbackp = nfrontp = netobuf;
33127186Sminshall     }
33227186Sminshall }
333*27261Sminshall 
334*27261Sminshall /*
335*27261Sminshall  * nextitem()
336*27261Sminshall  *
337*27261Sminshall  *	Return the address of the next "item" in the TELNET data
338*27261Sminshall  * stream.  This will be the address of the next character if
339*27261Sminshall  * the current address is a user data character, or it will
340*27261Sminshall  * be the address of the character following the TELNET command
341*27261Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
342*27261Sminshall  * character.
343*27261Sminshall  */
34427186Sminshall 
345*27261Sminshall char *
346*27261Sminshall nextitem(current)
347*27261Sminshall char	*current;
348*27261Sminshall {
349*27261Sminshall     if ((*current&0xff) != IAC) {
350*27261Sminshall 	return current+1;
351*27261Sminshall     }
352*27261Sminshall     switch (*(current+1)&0xff) {
353*27261Sminshall     case DO:
354*27261Sminshall     case DONT:
355*27261Sminshall     case WILL:
356*27261Sminshall     case WONT:
357*27261Sminshall 	return current+3;
358*27261Sminshall     case SB:		/* loop forever looking for the SE */
359*27261Sminshall 	{
360*27261Sminshall 	    register char *look = current+2;
361*27261Sminshall 
362*27261Sminshall 	    for (;;) {
363*27261Sminshall 		if ((*look++&0xff) == IAC) {
364*27261Sminshall 		    if ((*look++&0xff) == SE) {
365*27261Sminshall 			return look;
366*27261Sminshall 		    }
367*27261Sminshall 		}
368*27261Sminshall 	    }
369*27261Sminshall 	}
370*27261Sminshall     default:
371*27261Sminshall 	return current+2;
372*27261Sminshall     }
373*27261Sminshall }
37427186Sminshall /*
375*27261Sminshall  * netclear()
376*27261Sminshall  *
377*27261Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
378*27261Sminshall  * the path to the network.
379*27261Sminshall  *
380*27261Sminshall  *	Things are a bit tricky since we may have sent the first
381*27261Sminshall  * byte or so of a previous TELNET command into the network.
382*27261Sminshall  * So, we have to scan the network buffer from the beginning
383*27261Sminshall  * until we are up to where we want to be.
384*27261Sminshall  *
385*27261Sminshall  *	A side effect of what we do, just to keep things
386*27261Sminshall  * simple, is to clear the urgent data pointer.  The principal
387*27261Sminshall  * caller should be setting the urgent data pointer AFTER calling
388*27261Sminshall  * us in any case.
389*27261Sminshall  */
390*27261Sminshall 
391*27261Sminshall netclear()
392*27261Sminshall {
393*27261Sminshall     register char *thisitem, *next;
394*27261Sminshall     char *good;
395*27261Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
396*27261Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
397*27261Sminshall 
398*27261Sminshall     thisitem = netobuf;
399*27261Sminshall 
400*27261Sminshall     while ((next = nextitem(thisitem)) <= nbackp) {
401*27261Sminshall 	thisitem = next;
402*27261Sminshall     }
403*27261Sminshall 
404*27261Sminshall     /* Now, thisitem is first before/at boundary. */
405*27261Sminshall 
406*27261Sminshall     good = netobuf;	/* where the good bytes go */
407*27261Sminshall 
408*27261Sminshall     while (nfrontp > thisitem) {
409*27261Sminshall 	if (wewant(thisitem)) {
410*27261Sminshall 	    int length;
411*27261Sminshall 
412*27261Sminshall 	    next = thisitem;
413*27261Sminshall 	    do {
414*27261Sminshall 		next = nextitem(next);
415*27261Sminshall 	    } while (wewant(next) && (nfrontp > next));
416*27261Sminshall 	    length = next-thisitem;
417*27261Sminshall 	    bcopy(thisitem, good, length);
418*27261Sminshall 	    good += length;
419*27261Sminshall 	    thisitem = next;
420*27261Sminshall 	} else {
421*27261Sminshall 	    thisitem = nextitem(thisitem);
422*27261Sminshall 	}
423*27261Sminshall     }
424*27261Sminshall 
425*27261Sminshall     nbackp = netobuf;
426*27261Sminshall     nfrontp = good;		/* next byte to be sent */
427*27261Sminshall     neturg = 0;
428*27261Sminshall }
429*27261Sminshall 
430*27261Sminshall /*
43127186Sminshall  * Send as much data as possible to the terminal.
43227186Sminshall  */
43327186Sminshall 
43427186Sminshall 
43527186Sminshall ttyflush()
43627186Sminshall {
43727186Sminshall     int n;
43827186Sminshall 
43927186Sminshall     if ((n = tfrontp - tbackp) > 0) {
44027228Sminshall 	if (!(SYNCHing||flushout)) {
44127186Sminshall 	    n = write(tout, tbackp, n);
44227186Sminshall 	} else {
44327186Sminshall 	    ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
44427228Sminshall 	    /* we leave 'n' alone! */
44527186Sminshall 	}
44627186Sminshall     }
44727186Sminshall     if (n < 0) {
44827186Sminshall 	return;
44927186Sminshall     }
45027186Sminshall     tbackp += n;
45127186Sminshall     if (tbackp == tfrontp) {
45227186Sminshall 	tbackp = tfrontp = ttyobuf;
45327186Sminshall     }
45427186Sminshall }
45527186Sminshall 
45627186Sminshall /*
45727110Sminshall  * Various signal handling routines.
45827110Sminshall  */
45927110Sminshall 
46027110Sminshall deadpeer()
46127110Sminshall {
46227110Sminshall 	setcommandmode();
46327110Sminshall 	longjmp(peerdied, -1);
46427110Sminshall }
46527110Sminshall 
46627110Sminshall intr()
46727110Sminshall {
468*27261Sminshall     if (localchars) {
46927110Sminshall 	intp();
47027110Sminshall 	return;
47127110Sminshall     }
47227110Sminshall     setcommandmode();
47327110Sminshall     longjmp(toplevel, -1);
47427110Sminshall }
47527110Sminshall 
47627110Sminshall intr2()
47727110Sminshall {
478*27261Sminshall     if (localchars) {
47927110Sminshall 	sendbrk();
48027110Sminshall 	return;
48127110Sminshall     }
48227110Sminshall }
48327110Sminshall 
48427110Sminshall doescape()
48527110Sminshall {
48627110Sminshall     command(0);
48727110Sminshall }
48827110Sminshall 
48927110Sminshall /*
49027186Sminshall  * The following are routines used to print out debugging information.
49127186Sminshall  */
49227186Sminshall 
49327186Sminshall 
49427186Sminshall static
49527186Sminshall Dump(direction, buffer, length)
49627186Sminshall char	direction;
49727186Sminshall char	*buffer;
49827186Sminshall int	length;
49927186Sminshall {
50027186Sminshall #   define BYTES_PER_LINE	32
50127186Sminshall #   define min(x,y)	((x<y)? x:y)
50227186Sminshall     char *pThis;
50327186Sminshall     int offset;
50427186Sminshall 
50527186Sminshall     offset = 0;
50627186Sminshall 
50727186Sminshall     while (length) {
50827186Sminshall 	/* print one line */
50927186Sminshall 	fprintf(NetTrace, "%c 0x%x\t", direction, offset);
51027186Sminshall 	pThis = buffer;
51127186Sminshall 	buffer = buffer+min(length, BYTES_PER_LINE);
51227186Sminshall 	while (pThis < buffer) {
51327186Sminshall 	    fprintf(NetTrace, "%.2x", (*pThis)&0xff);
51427186Sminshall 	    pThis++;
51527186Sminshall 	}
51627186Sminshall 	fprintf(NetTrace, "\n");
51727186Sminshall 	length -= BYTES_PER_LINE;
51827186Sminshall 	offset += BYTES_PER_LINE;
51927186Sminshall 	if (length < 0) {
52027186Sminshall 	    return;
52127186Sminshall 	}
52227186Sminshall 	/* find next unique line */
52327186Sminshall     }
52427186Sminshall }
52527186Sminshall 
52627186Sminshall 
52727186Sminshall /*VARARGS*/
52827186Sminshall printoption(direction, fmt, option, what)
52927186Sminshall 	char *direction, *fmt;
53027186Sminshall 	int option, what;
53127186Sminshall {
53227186Sminshall 	if (!showoptions)
53327186Sminshall 		return;
53427186Sminshall 	printf("%s ", direction);
53527186Sminshall 	if (fmt == doopt)
53627186Sminshall 		fmt = "do";
53727186Sminshall 	else if (fmt == dont)
53827186Sminshall 		fmt = "dont";
53927186Sminshall 	else if (fmt == will)
54027186Sminshall 		fmt = "will";
54127186Sminshall 	else if (fmt == wont)
54227186Sminshall 		fmt = "wont";
54327186Sminshall 	else
54427186Sminshall 		fmt = "???";
54527186Sminshall 	if (option < TELOPT_SUPDUP)
54627186Sminshall 		printf("%s %s", fmt, telopts[option]);
54727186Sminshall 	else
54827186Sminshall 		printf("%s %d", fmt, option);
54927186Sminshall 	if (*direction == '<') {
55027186Sminshall 		printf("\r\n");
55127186Sminshall 		return;
55227186Sminshall 	}
55327186Sminshall 	printf(" (%s)\r\n", what ? "reply" : "don't reply");
55427186Sminshall }
55527186Sminshall 
55627186Sminshall /*
55727110Sminshall  * Mode - set up terminal to a specific mode.
55827110Sminshall  */
55927110Sminshall 
5609972Ssam 
5616000Sroot mode(f)
5626000Sroot 	register int f;
5636000Sroot {
5648378Ssam 	static int prevmode = 0;
56513076Ssam 	struct tchars *tc;
56613076Ssam 	struct ltchars *ltc;
56713076Ssam 	struct sgttyb sb;
56813076Ssam 	int onoff, old;
56927228Sminshall 	struct	tchars notc2;
57027228Sminshall 	struct	ltchars noltc2;
57127228Sminshall 	static struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
57227228Sminshall 	static struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
5736000Sroot 
57427110Sminshall 	globalmode = f;
5758378Ssam 	if (prevmode == f)
57627186Sminshall 		return;
5778378Ssam 	old = prevmode;
5788378Ssam 	prevmode = f;
57927110Sminshall 	sb = nttyb;
5806000Sroot 	switch (f) {
5818378Ssam 
5826000Sroot 	case 0:
5836000Sroot 		onoff = 0;
5849972Ssam 		tc = &otc;
58513076Ssam 		ltc = &oltc;
5866000Sroot 		break;
5876000Sroot 
58827110Sminshall 	case 1:		/* remote character processing, remote echo */
58927110Sminshall 	case 2:		/* remote character processing, local echo */
59013076Ssam 		sb.sg_flags |= CBREAK;
5918378Ssam 		if (f == 1)
59213076Ssam 			sb.sg_flags &= ~(ECHO|CRMOD);
5938378Ssam 		else
59413076Ssam 			sb.sg_flags |= ECHO|CRMOD;
59513076Ssam 		sb.sg_erase = sb.sg_kill = -1;
5969972Ssam 		tc = &notc;
59727110Sminshall 		/*
59827110Sminshall 		 * If user hasn't specified one way or the other,
59927110Sminshall 		 * then default to not trapping signals.
60027110Sminshall 		 */
601*27261Sminshall 		if (!donelclchars) {
602*27261Sminshall 			localchars = 0;
60327228Sminshall 		}
604*27261Sminshall 		if (localchars) {
60527110Sminshall 			notc2 = notc;
60627110Sminshall 			notc2.t_intrc = ntc.t_intrc;
60727110Sminshall 			notc2.t_quitc = ntc.t_quitc;
60827110Sminshall 			tc = &notc2;
60927110Sminshall 		} else
61027110Sminshall 			tc = &notc;
61113076Ssam 		ltc = &noltc;
6126000Sroot 		onoff = 1;
6139972Ssam 		break;
61427110Sminshall 	case 3:		/* local character processing, remote echo */
61527110Sminshall 	case 4:		/* local character processing, local echo */
61627110Sminshall 	case 5:		/* local character processing, no echo */
61727110Sminshall 		sb.sg_flags &= ~CBREAK;
61827110Sminshall 		sb.sg_flags |= CRMOD;
61927110Sminshall 		if (f == 4)
62027110Sminshall 			sb.sg_flags |= ECHO;
62127110Sminshall 		else
62227110Sminshall 			sb.sg_flags &= ~ECHO;
62327228Sminshall 		notc2 = ntc;
62427228Sminshall 		tc = &notc2;
62527228Sminshall 		noltc2 = oltc;
62627228Sminshall 		ltc = &noltc2;
62727110Sminshall 		/*
62827110Sminshall 		 * If user hasn't specified one way or the other,
62927110Sminshall 		 * then default to trapping signals.
63027110Sminshall 		 */
631*27261Sminshall 		if (!donelclchars) {
632*27261Sminshall 			localchars = 1;
63327228Sminshall 		}
634*27261Sminshall 		if (localchars) {
63527228Sminshall 			notc2.t_brkc = nltc.t_flushc;
63627228Sminshall 			noltc2.t_flushc = -1;
63727228Sminshall 		} else {
63827110Sminshall 			notc2.t_intrc = notc2.t_quitc = -1;
63927110Sminshall 		}
64027110Sminshall 		noltc2.t_suspc = escape;
64127110Sminshall 		noltc2.t_dsuspc = -1;
64227110Sminshall 		onoff = 1;
64327110Sminshall 		break;
6449972Ssam 
6459972Ssam 	default:
6469972Ssam 		return;
6476000Sroot 	}
64813076Ssam 	ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
64913076Ssam 	ioctl(fileno(stdin), TIOCSETC, (char *)tc);
65013076Ssam 	ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
65127186Sminshall 	ioctl(fileno(stdin), FIONBIO, (char *)&onoff);
65227186Sminshall 	ioctl(fileno(stdout), FIONBIO, (char *)&onoff);
65327110Sminshall 	if (f >= 3)
65427110Sminshall 		signal(SIGTSTP, doescape);
65527110Sminshall 	else if (old >= 3) {
65627110Sminshall 		signal(SIGTSTP, SIG_DFL);
65727110Sminshall 		sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
65827110Sminshall 	}
6596000Sroot }
66027186Sminshall 
66127110Sminshall /*
66227110Sminshall  * These routines decides on what the mode should be (based on the values
66327110Sminshall  * of various global variables).
66427110Sminshall  */
66527110Sminshall 
66627178Sminshall char *modedescriptions[] = {
66727178Sminshall 	"telnet command mode",					/* 0 */
66827178Sminshall 	"character-at-a-time mode",				/* 1 */
66927178Sminshall 	"character-at-a-time mode (local echo)",		/* 2 */
67027178Sminshall 	"line-by-line mode (remote echo)",			/* 3 */
67127178Sminshall 	"line-by-line mode",					/* 4 */
67227178Sminshall 	"line-by-line mode (local echoing suppressed)",		/* 5 */
67327178Sminshall };
67427178Sminshall 
67527178Sminshall getconnmode()
67627110Sminshall {
67727110Sminshall     static char newmode[8] = { 4, 5, 3, 3, 2, 2, 1, 1 };
67827186Sminshall     int modeindex = 0;
67927110Sminshall 
68027110Sminshall     if (hisopts[TELOPT_ECHO]) {
68127186Sminshall 	modeindex += 2;
68227110Sminshall     }
68327110Sminshall     if (hisopts[TELOPT_SGA]) {
68427186Sminshall 	modeindex += 4;
68527110Sminshall     }
68627186Sminshall     if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
68727186Sminshall 	modeindex += 1;
68827110Sminshall     }
68927186Sminshall     return newmode[modeindex];
69027110Sminshall }
69127110Sminshall 
69227178Sminshall setconnmode()
69327178Sminshall {
69427178Sminshall     mode(getconnmode());
69527178Sminshall }
69627110Sminshall 
69727178Sminshall 
69827110Sminshall setcommandmode()
69927110Sminshall {
70027110Sminshall     mode(0);
70127110Sminshall }
70227110Sminshall 
7036000Sroot char	sibuf[BUFSIZ], *sbp;
7046000Sroot char	tibuf[BUFSIZ], *tbp;
7056000Sroot int	scc, tcc;
7066000Sroot 
70727228Sminshall 
7086000Sroot /*
7096000Sroot  * Select from tty and network...
7106000Sroot  */
71127088Sminshall telnet()
7126000Sroot {
7136000Sroot 	register int c;
71427088Sminshall 	int tin = fileno(stdin);
7156000Sroot 	int on = 1;
716*27261Sminshall 	fd_set ibits, obits, xbits;
7176000Sroot 
71827088Sminshall 	tout = fileno(stdout);
71927110Sminshall 	setconnmode();
72027228Sminshall 	scc = 0;
72127228Sminshall 	tcc = 0;
722*27261Sminshall 	FD_ZERO(&ibits);
723*27261Sminshall 	FD_ZERO(&obits);
724*27261Sminshall 	FD_ZERO(&xbits);
725*27261Sminshall 
72627186Sminshall 	ioctl(net, FIONBIO, (char *)&on);
72727228Sminshall #if	defined(xxxSO_OOBINLINE)
72827228Sminshall 	setsockopt(net, SOL_SOCKET, SO_OOBINLINE, on, sizeof on);
72927228Sminshall #endif	/* defined(xxxSO_OOBINLINE) */
73027088Sminshall 	if (telnetport && !hisopts[TELOPT_SGA]) {
73117922Sralph 		willoption(TELOPT_SGA);
73227088Sminshall 	}
7336000Sroot 	for (;;) {
73427110Sminshall 		if (scc < 0 && tcc < 0) {
7356000Sroot 			break;
73627110Sminshall 		}
73727110Sminshall 
73827228Sminshall 		if (((globalmode < 4) || flushline) && NETBYTES()) {
73927110Sminshall 			FD_SET(net, &obits);
74027088Sminshall 		} else {
74127110Sminshall 			FD_SET(tin, &ibits);
74227088Sminshall 		}
74327228Sminshall 		if (TTYBYTES()) {
74427110Sminshall 			FD_SET(tout, &obits);
74527110Sminshall 		} else {
74627110Sminshall 			FD_SET(net, &ibits);
74727110Sminshall 		}
74827186Sminshall 		if (!SYNCHing) {
74927110Sminshall 			FD_SET(net, &xbits);
75027110Sminshall 		}
75127186Sminshall 		if ((c = select(16, &ibits, &obits, &xbits,
75227186Sminshall 						(struct timeval *)0)) < 1) {
75327110Sminshall 			if (c == -1) {
75427110Sminshall 				/*
75527110Sminshall 				 * we can get EINTR if we are in line mode,
75627110Sminshall 				 * and the user does an escape (TSTP), or
75727110Sminshall 				 * some other signal generator.
75827110Sminshall 				 */
75927110Sminshall 				if (errno == EINTR) {
76027110Sminshall 					continue;
76127110Sminshall 				}
76227110Sminshall 			}
7636000Sroot 			sleep(5);
7646000Sroot 			continue;
7656000Sroot 		}
7666000Sroot 
7676000Sroot 		/*
76827088Sminshall 		 * Any urgent data?
76927088Sminshall 		 */
77027110Sminshall 		if (FD_ISSET(net, &xbits)) {
771*27261Sminshall 		    FD_CLR(net, &xbits);
77227186Sminshall 		    SYNCHing = 1;
77327088Sminshall 		    ttyflush();	/* flush already enqueued data */
77427088Sminshall 		}
77527088Sminshall 
77627088Sminshall 		/*
7776000Sroot 		 * Something to read from the network...
7786000Sroot 		 */
77927110Sminshall 		if (FD_ISSET(net, &ibits)) {
78027228Sminshall 			int canread;
78127228Sminshall 
782*27261Sminshall 			FD_CLR(net, &ibits);
78327228Sminshall 			if (scc == 0) {
78427228Sminshall 			    sbp = sibuf;
78527228Sminshall 			}
78627228Sminshall 			canread = sibuf + sizeof sibuf - sbp;
78727228Sminshall #if	!defined(xxxSO_OOBINLINE)
78827178Sminshall 			/*
78927178Sminshall 			 * In 4.2 (and some early 4.3) systems, the
79027178Sminshall 			 * OOB indication and data handling in the kernel
79127178Sminshall 			 * is such that if two separate TCP Urgent requests
79227178Sminshall 			 * come in, one byte of TCP data will be overlaid.
79327178Sminshall 			 * This is fatal for Telnet, but we try to live
79427178Sminshall 			 * with it.
79527178Sminshall 			 *
79627178Sminshall 			 * In addition, in 4.2 (and...), a special protocol
79727178Sminshall 			 * is needed to pick up the TCP Urgent data in
79827178Sminshall 			 * the correct sequence.
79927178Sminshall 			 *
80027178Sminshall 			 * What we do is:  if we think we are in urgent
80127178Sminshall 			 * mode, we look to see if we are "at the mark".
80227178Sminshall 			 * If we are, we do an OOB receive.  If we run
80327178Sminshall 			 * this twice, we will do the OOB receive twice,
80427178Sminshall 			 * but the second will fail, since the second
80527178Sminshall 			 * time we were "at the mark", but there wasn't
80627178Sminshall 			 * any data there (the kernel doesn't reset
80727178Sminshall 			 * "at the mark" until we do a normal read).
80827178Sminshall 			 * Once we've read the OOB data, we go ahead
80927178Sminshall 			 * and do normal reads.
81027178Sminshall 			 *
81127178Sminshall 			 * There is also another problem, which is that
81227178Sminshall 			 * since the OOB byte we read doesn't put us
81327178Sminshall 			 * out of OOB state, and since that byte is most
81427178Sminshall 			 * likely the TELNET DM (data mark), we would
81527186Sminshall 			 * stay in the TELNET SYNCH (SYNCHing) state.
81627178Sminshall 			 * So, clocks to the rescue.  If we've "just"
81727178Sminshall 			 * received a DM, then we test for the
81827178Sminshall 			 * presence of OOB data when the receive OOB
81927178Sminshall 			 * fails (and AFTER we did the normal mode read
82027178Sminshall 			 * to clear "at the mark").
82127178Sminshall 			 */
82227186Sminshall 		    if (SYNCHing) {
82327178Sminshall 			int atmark;
82427178Sminshall 
82527186Sminshall 			ioctl(net, SIOCATMARK, (char *)&atmark);
82627178Sminshall 			if (atmark) {
82727228Sminshall 			    c = recv(net, sibuf, canread, MSG_OOB);
82827228Sminshall 			    if ((c == -1) && (errno == EINVAL)) {
82927228Sminshall 				c = read(net, sibuf, canread);
83027186Sminshall 				if (clocks.didnetreceive < clocks.gotDM) {
83127186Sminshall 				    SYNCHing = stilloob(net);
83227021Sminshall 				}
83327178Sminshall 			    }
83427178Sminshall 			} else {
83527228Sminshall 			    c = read(net, sibuf, canread);
8366000Sroot 			}
83727178Sminshall 		    } else {
83827228Sminshall 			c = read(net, sibuf, canread);
83927178Sminshall 		    }
84027178Sminshall 		    settimer(didnetreceive);
84127228Sminshall #else	/* !defined(xxxSO_OOBINLINE) */
84227228Sminshall 		    c = read(net, sbp, canread);
84327228Sminshall #endif	/* !defined(xxxSO_OOBINLINE) */
84427228Sminshall 		    if (c < 0 && errno == EWOULDBLOCK) {
84527228Sminshall 			c = 0;
84627228Sminshall 		    } else if (c <= 0) {
84727228Sminshall 			break;
84827178Sminshall 		    }
84927228Sminshall 		    if (netdata) {
85027228Sminshall 			Dump('<', sbp, c);
85127228Sminshall 		    }
85227228Sminshall 		    scc += c;
8536000Sroot 		}
8546000Sroot 
8556000Sroot 		/*
8566000Sroot 		 * Something to read from the tty...
8576000Sroot 		 */
85827110Sminshall 		if (FD_ISSET(tin, &ibits)) {
859*27261Sminshall 			FD_CLR(tin, &ibits);
86027228Sminshall 			if (tcc == 0) {
86127228Sminshall 			    tbp = tibuf;	/* nothing left, reset */
8626000Sroot 			}
86327228Sminshall 			c = read(tin, tbp, tibuf+sizeof tibuf - tbp);
86427228Sminshall 			if (c < 0 && errno == EWOULDBLOCK) {
86527228Sminshall 				c = 0;
86627228Sminshall 			} else if (c <= 0) {
86727228Sminshall 				tcc = c;
86827228Sminshall 				break;
86927228Sminshall 			}
87027228Sminshall 			tcc += c;
8716000Sroot 		}
8726000Sroot 
8736000Sroot 		while (tcc > 0) {
87427186Sminshall 			register int sc;
8756000Sroot 
87627228Sminshall 			if (NETROOM() < 2) {
87727110Sminshall 				flushline = 1;
8786000Sroot 				break;
87927110Sminshall 			}
88027186Sminshall 			c = *tbp++ & 0xff, sc = strip(c), tcc--;
88127186Sminshall 			if (sc == escape) {
8826000Sroot 				command(0);
8836000Sroot 				tcc = 0;
88427110Sminshall 				flushline = 1;
8856000Sroot 				break;
886*27261Sminshall 			} else if ((globalmode >= 4) && (sc == echoc)) {
88727110Sminshall 				if (tcc > 0 && strip(*tbp) == echoc) {
88827110Sminshall 					tbp++;
88927110Sminshall 					tcc--;
89027110Sminshall 				} else {
89127110Sminshall 					dontlecho = !dontlecho;
89227110Sminshall 					settimer(echotoggle);
89327110Sminshall 					setconnmode();
89427110Sminshall 					tcc = 0;
89527110Sminshall 					flushline = 1;
89627110Sminshall 					break;
89727110Sminshall 				}
8986000Sroot 			}
899*27261Sminshall 			if (localchars) {
90027186Sminshall 				if (sc == ntc.t_intrc) {
90127110Sminshall 					intp();
90227110Sminshall 					break;
90327186Sminshall 				} else if (sc == ntc.t_quitc) {
90427110Sminshall 					sendbrk();
90527110Sminshall 					break;
90627228Sminshall 				} else if (sc == nltc.t_flushc) {
90727228Sminshall 					NET2ADD(IAC, AO);
90827228Sminshall 					if (autoflush) {
90927228Sminshall 					    doflush();
91027228Sminshall 					}
91127228Sminshall 					break;
91227110Sminshall 				} else if (globalmode > 2) {
91327110Sminshall 					;
91427186Sminshall 				} else if (sc == nttyb.sg_kill) {
91527110Sminshall 					NET2ADD(IAC, EL);
91627110Sminshall 					break;
91727186Sminshall 				} else if (sc == nttyb.sg_erase) {
91827110Sminshall 					NET2ADD(IAC, EC);
91927110Sminshall 					break;
92027110Sminshall 				}
92127110Sminshall 			}
92217922Sralph 			switch (c) {
92317922Sralph 			case '\n':
92427021Sminshall 				/*
92527021Sminshall 				 * If echoing is happening locally,
92627021Sminshall 				 * then a newline (unix) is CRLF (TELNET).
92727021Sminshall 				 */
92827088Sminshall 				if (!hisopts[TELOPT_ECHO]) {
92927088Sminshall 					NETADD('\r');
93027088Sminshall 				}
93127088Sminshall 				NETADD('\n');
93227110Sminshall 				flushline = 1;
93317922Sralph 				break;
93417922Sralph 			case '\r':
93527088Sminshall 				NET2ADD('\r', '\0');
93627110Sminshall 				flushline = 1;
93717922Sralph 				break;
93817922Sralph 			case IAC:
93927088Sminshall 				NET2ADD(IAC, IAC);
94027021Sminshall 				break;
94117922Sralph 			default:
94227088Sminshall 				NETADD(c);
94317922Sralph 				break;
94417922Sralph 			}
9456000Sroot 		}
94627110Sminshall 		if (((globalmode < 4) || flushline) &&
94727228Sminshall 		    FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
948*27261Sminshall 			FD_CLR(net, &obits);
94927088Sminshall 			netflush(net);
95027110Sminshall 		}
9516000Sroot 		if (scc > 0)
9526000Sroot 			telrcv();
953*27261Sminshall 		if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) {
954*27261Sminshall 			FD_CLR(tout, &obits);
95527088Sminshall 			ttyflush();
956*27261Sminshall 		}
9576000Sroot 	}
95827110Sminshall 	setcommandmode();
9596000Sroot }
96027110Sminshall 
9616000Sroot /*
9626000Sroot  * Telnet receiver states for fsm
9636000Sroot  */
9646000Sroot #define	TS_DATA		0
9656000Sroot #define	TS_IAC		1
9666000Sroot #define	TS_WILL		2
9676000Sroot #define	TS_WONT		3
9686000Sroot #define	TS_DO		4
9696000Sroot #define	TS_DONT		5
97027021Sminshall #define	TS_CR		6
9716000Sroot 
9726000Sroot telrcv()
9736000Sroot {
9746000Sroot 	register int c;
9756000Sroot 	static int state = TS_DATA;
9766000Sroot 
97727228Sminshall 	while ((scc > 0) && (TTYROOM() > 2)) {
97827186Sminshall 		c = *sbp++ & 0xff, scc--;
9796000Sroot 		switch (state) {
9806000Sroot 
98127021Sminshall 		case TS_CR:
98227021Sminshall 			state = TS_DATA;
98327228Sminshall 			if (c == '\0') {
98427228Sminshall 			    break;	/* Ignore \0 after CR */
98527228Sminshall 			} else if (c == '\n') {
98627228Sminshall 			    if (hisopts[TELOPT_ECHO] && !crmod) {
98727228Sminshall 				TTYADD(c);
98827228Sminshall 			    }
98927228Sminshall 			    break;
99027021Sminshall 			}
99127228Sminshall 			/* Else, fall through */
99227021Sminshall 
9936000Sroot 		case TS_DATA:
9949972Ssam 			if (c == IAC) {
9956000Sroot 				state = TS_IAC;
9969972Ssam 				continue;
9979972Ssam 			}
99827228Sminshall 			    /*
99927228Sminshall 			     * The 'crmod' hack (see following) is needed
100027228Sminshall 			     * since we can't * set CRMOD on output only.
100127228Sminshall 			     * Machines like MULTICS like to send \r without
100227228Sminshall 			     * \n; since we must turn off CRMOD to get proper
100327228Sminshall 			     * input, the mapping is done here (sigh).
100427228Sminshall 			     */
100527021Sminshall 			if (c == '\r') {
100627021Sminshall 				if (scc > 0) {
100727186Sminshall 					c = *sbp&0xff;
100827021Sminshall 					if (c == 0) {
100927021Sminshall 						sbp++, scc--;
101027228Sminshall 						/* a "true" CR */
101127088Sminshall 						TTYADD('\r');
101227021Sminshall 					} else if (!hisopts[TELOPT_ECHO] &&
101327021Sminshall 								(c == '\n')) {
101427021Sminshall 						sbp++, scc--;
101527088Sminshall 						TTYADD('\n');
101627021Sminshall 					} else {
101727088Sminshall 						TTYADD('\r');
101827228Sminshall 						if (crmod) {
101927228Sminshall 							TTYADD('\n');
102027228Sminshall 						}
102127021Sminshall 					}
102227021Sminshall 				} else {
102327021Sminshall 					state = TS_CR;
102427088Sminshall 					TTYADD('\r');
102527228Sminshall 					if (crmod) {
102627228Sminshall 						TTYADD('\n');
102727228Sminshall 					}
102827021Sminshall 				}
102927021Sminshall 			} else {
103027088Sminshall 				TTYADD(c);
103127021Sminshall 			}
10326000Sroot 			continue;
10336000Sroot 
10346000Sroot 		case TS_IAC:
10356000Sroot 			switch (c) {
10366000Sroot 
10376000Sroot 			case WILL:
10386000Sroot 				state = TS_WILL;
10396000Sroot 				continue;
10406000Sroot 
10416000Sroot 			case WONT:
10426000Sroot 				state = TS_WONT;
10436000Sroot 				continue;
10446000Sroot 
10456000Sroot 			case DO:
10466000Sroot 				state = TS_DO;
10476000Sroot 				continue;
10486000Sroot 
10496000Sroot 			case DONT:
10506000Sroot 				state = TS_DONT;
10516000Sroot 				continue;
10526000Sroot 
10536000Sroot 			case DM:
105427088Sminshall 				/*
105527088Sminshall 				 * We may have missed an urgent notification,
105627088Sminshall 				 * so make sure we flush whatever is in the
105727088Sminshall 				 * buffer currently.
105827088Sminshall 				 */
105927186Sminshall 				SYNCHing = 1;
106027088Sminshall 				ttyflush();
106127186Sminshall 				SYNCHing = stilloob(net);
106227178Sminshall 				settimer(gotDM);
10636000Sroot 				break;
10646000Sroot 
10656000Sroot 			case NOP:
10666000Sroot 			case GA:
10676000Sroot 				break;
10686000Sroot 
10696000Sroot 			default:
10706000Sroot 				break;
10716000Sroot 			}
10726000Sroot 			state = TS_DATA;
10736000Sroot 			continue;
10746000Sroot 
10756000Sroot 		case TS_WILL:
10768345Ssam 			printoption("RCVD", will, c, !hisopts[c]);
107727110Sminshall 			if (c == TELOPT_TM) {
107827110Sminshall 				if (flushout) {
107927186Sminshall 					flushout = 0;
108027110Sminshall 				}
108127110Sminshall 			} else if (!hisopts[c]) {
10826000Sroot 				willoption(c);
108327110Sminshall 			}
10846000Sroot 			state = TS_DATA;
10856000Sroot 			continue;
10866000Sroot 
10876000Sroot 		case TS_WONT:
10888345Ssam 			printoption("RCVD", wont, c, hisopts[c]);
108927110Sminshall 			if (c == TELOPT_TM) {
109027110Sminshall 				if (flushout) {
109127186Sminshall 					flushout = 0;
109227110Sminshall 				}
109327110Sminshall 			} else if (hisopts[c]) {
10946000Sroot 				wontoption(c);
109527110Sminshall 			}
10966000Sroot 			state = TS_DATA;
10976000Sroot 			continue;
10986000Sroot 
10996000Sroot 		case TS_DO:
11008345Ssam 			printoption("RCVD", doopt, c, !myopts[c]);
11016000Sroot 			if (!myopts[c])
11026000Sroot 				dooption(c);
11036000Sroot 			state = TS_DATA;
11046000Sroot 			continue;
11056000Sroot 
11066000Sroot 		case TS_DONT:
11078345Ssam 			printoption("RCVD", dont, c, myopts[c]);
11086000Sroot 			if (myopts[c]) {
11096000Sroot 				myopts[c] = 0;
11106000Sroot 				sprintf(nfrontp, wont, c);
11118378Ssam 				nfrontp += sizeof (wont) - 2;
111227110Sminshall 				flushline = 1;
111327110Sminshall 				setconnmode();	/* set new tty mode (maybe) */
11148345Ssam 				printoption("SENT", wont, c);
11156000Sroot 			}
11166000Sroot 			state = TS_DATA;
11176000Sroot 			continue;
11186000Sroot 		}
11196000Sroot 	}
11206000Sroot }
112127110Sminshall 
11226000Sroot willoption(option)
11236000Sroot 	int option;
11246000Sroot {
11256000Sroot 	char *fmt;
11266000Sroot 
11276000Sroot 	switch (option) {
11286000Sroot 
11296000Sroot 	case TELOPT_ECHO:
11306000Sroot 	case TELOPT_SGA:
113127110Sminshall 		settimer(modenegotiated);
11326000Sroot 		hisopts[option] = 1;
11336000Sroot 		fmt = doopt;
113427110Sminshall 		setconnmode();		/* possibly set new tty mode */
11356000Sroot 		break;
11366000Sroot 
11376000Sroot 	case TELOPT_TM:
113827110Sminshall 		return;			/* Never reply to TM will's/wont's */
11396000Sroot 
11406000Sroot 	default:
11416000Sroot 		fmt = dont;
11426000Sroot 		break;
11436000Sroot 	}
11446024Ssam 	sprintf(nfrontp, fmt, option);
11458378Ssam 	nfrontp += sizeof (dont) - 2;
11468345Ssam 	printoption("SENT", fmt, option);
11476000Sroot }
11486000Sroot 
11496000Sroot wontoption(option)
11506000Sroot 	int option;
11516000Sroot {
11526000Sroot 	char *fmt;
11536000Sroot 
11546000Sroot 	switch (option) {
11556000Sroot 
11566000Sroot 	case TELOPT_ECHO:
11576000Sroot 	case TELOPT_SGA:
115827110Sminshall 		settimer(modenegotiated);
11596000Sroot 		hisopts[option] = 0;
11606000Sroot 		fmt = dont;
116127110Sminshall 		setconnmode();			/* Set new tty mode */
11626000Sroot 		break;
11636000Sroot 
116427110Sminshall 	case TELOPT_TM:
116527110Sminshall 		return;		/* Never reply to TM will's/wont's */
116627110Sminshall 
11676000Sroot 	default:
11686000Sroot 		fmt = dont;
11696000Sroot 	}
11706000Sroot 	sprintf(nfrontp, fmt, option);
11718378Ssam 	nfrontp += sizeof (doopt) - 2;
11728345Ssam 	printoption("SENT", fmt, option);
11736000Sroot }
11746000Sroot 
11756000Sroot dooption(option)
11766000Sroot 	int option;
11776000Sroot {
11786000Sroot 	char *fmt;
11796000Sroot 
11806000Sroot 	switch (option) {
11816000Sroot 
11826000Sroot 	case TELOPT_TM:
118313231Ssam 		fmt = will;
118413231Ssam 		break;
118513231Ssam 
118627110Sminshall 	case TELOPT_SGA:		/* no big deal */
11876000Sroot 		fmt = will;
118827110Sminshall 		myopts[option] = 1;
11896000Sroot 		break;
11906000Sroot 
119127110Sminshall 	case TELOPT_ECHO:		/* We're never going to echo... */
11926000Sroot 	default:
11936000Sroot 		fmt = wont;
11946000Sroot 		break;
11956000Sroot 	}
11966000Sroot 	sprintf(nfrontp, fmt, option);
11978378Ssam 	nfrontp += sizeof (doopt) - 2;
11988345Ssam 	printoption("SENT", fmt, option);
11996000Sroot }
120027110Sminshall 
12016000Sroot /*
120227088Sminshall  *	The following are data structures and routines for
120327088Sminshall  *	the "send" command.
120427088Sminshall  *
120527088Sminshall  */
120627088Sminshall 
120727088Sminshall struct sendlist {
120827088Sminshall     char	*name;		/* How user refers to it (case independent) */
120927088Sminshall     int		what;		/* Character to be sent (<0 ==> special) */
121027088Sminshall     char	*help;		/* Help information (0 ==> no help) */
121127088Sminshall     int		(*routine)();	/* Routine to perform (for special ops) */
121227088Sminshall };
121327088Sminshall 
121427186Sminshall /*ARGSUSED*/
121527088Sminshall dosynch(s)
121627088Sminshall struct sendlist *s;
121727088Sminshall {
1218*27261Sminshall     netclear();			/* clear the path to the network */
121927088Sminshall     NET2ADD(IAC, DM);
122027186Sminshall     neturg = NETLOC()-1;	/* Some systems are off by one XXX */
122127088Sminshall }
122227088Sminshall 
122327228Sminshall doflush()
122427228Sminshall {
122527228Sminshall     NET2ADD(IAC, DO);
122627228Sminshall     NETADD(TELOPT_TM);
122727228Sminshall     printoption("SENT", doopt, TELOPT_TM);
122827228Sminshall     flushline = 1;
122927228Sminshall     flushout = 1;
123027228Sminshall     ttyflush();
123127228Sminshall }
123227228Sminshall 
123327088Sminshall intp()
123427088Sminshall {
123527110Sminshall     NET2ADD(IAC, IP);
123627228Sminshall     if (autoflush) {
123727228Sminshall 	doflush();
123827228Sminshall     }
123927228Sminshall     if (autosynch) {
124027228Sminshall 	dosynch();
124127228Sminshall     }
124227088Sminshall }
124327088Sminshall 
124427110Sminshall sendbrk()
124527110Sminshall {
124627186Sminshall     NET2ADD(IAC, BREAK);
124727228Sminshall     if (autoflush) {
124827228Sminshall 	doflush();
124927228Sminshall     }
125027228Sminshall     if (autosynch) {
125127228Sminshall 	dosynch();
125227228Sminshall     }
125327110Sminshall }
125427088Sminshall 
125527110Sminshall 
125627088Sminshall #define	SENDQUESTION	-1
125727088Sminshall #define	SENDESCAPE	-3
125827088Sminshall 
125927088Sminshall struct sendlist Sendlist[] = {
126027088Sminshall     { "ao", AO, "Send Telnet Abort output" },
126127088Sminshall     { "ayt", AYT, "Send Telnet 'Are You There'" },
1262*27261Sminshall     { "brk", BREAK, "Send Telnet Break" },
126327088Sminshall     { "ec", EC, "Send Telnet Erase Character" },
126427088Sminshall     { "el", EL, "Send Telnet Erase Line" },
1265*27261Sminshall     { "escape", SENDESCAPE, "Send current escape character" },
126627088Sminshall     { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
1267*27261Sminshall     { "ip", IP, "Send Telnet Interrupt Process" },
126827088Sminshall     { "nop", NOP, "Send Telnet 'No operation'" },
1269*27261Sminshall     { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
127027088Sminshall     { "?", SENDQUESTION, "Display send options" },
127127088Sminshall     { 0 }
127227088Sminshall };
127327088Sminshall 
1274*27261Sminshall struct sendlist Sendlist2[] = {		/* some synonyms */
1275*27261Sminshall 	{ "break", BREAK, 0 },
1276*27261Sminshall 
1277*27261Sminshall 	{ "intp", IP, 0 },
1278*27261Sminshall 	{ "interrupt", IP, 0 },
1279*27261Sminshall 	{ "intr", IP, 0 },
1280*27261Sminshall 
1281*27261Sminshall 	{ "help", SENDQUESTION, 0 },
1282*27261Sminshall 
1283*27261Sminshall 	{ 0 }
1284*27261Sminshall };
1285*27261Sminshall 
128627088Sminshall char **
128727088Sminshall getnextsend(name)
128827088Sminshall char *name;
128927088Sminshall {
129027088Sminshall     struct sendlist *c = (struct sendlist *) name;
129127088Sminshall 
129227088Sminshall     return (char **) (c+1);
129327088Sminshall }
129427088Sminshall 
129527088Sminshall struct sendlist *
129627088Sminshall getsend(name)
129727088Sminshall char *name;
129827088Sminshall {
1299*27261Sminshall     struct sendlist *sl;
1300*27261Sminshall 
1301*27261Sminshall     if (sl = (struct sendlist *)
1302*27261Sminshall 				genget(name, (char **) Sendlist, getnextsend)) {
1303*27261Sminshall 	return sl;
1304*27261Sminshall     } else {
1305*27261Sminshall 	return (struct sendlist *)
1306*27261Sminshall 				genget(name, (char **) Sendlist2, getnextsend);
1307*27261Sminshall     }
130827088Sminshall }
130927088Sminshall 
131027088Sminshall sendcmd(argc, argv)
131127088Sminshall int	argc;
131227088Sminshall char	**argv;
131327088Sminshall {
131427088Sminshall     int what;		/* what we are sending this time */
131527088Sminshall     int count;		/* how many bytes we are going to need to send */
131627088Sminshall     int hadsynch;	/* are we going to process a "synch"? */
131727088Sminshall     int i;
1318*27261Sminshall     int question = 0;	/* was at least one argument a question */
131927088Sminshall     struct sendlist *s;	/* pointer to current command */
132027088Sminshall 
132127088Sminshall     if (argc < 2) {
132227088Sminshall 	printf("need at least one argument for 'send' command\n");
132327088Sminshall 	printf("'send ?' for help\n");
1324*27261Sminshall 	return 0;
132527088Sminshall     }
132627088Sminshall     /*
132727088Sminshall      * First, validate all the send arguments.
132827088Sminshall      * In addition, we see how much space we are going to need, and
132927088Sminshall      * whether or not we will be doing a "SYNCH" operation (which
133027088Sminshall      * flushes the network queue).
133127088Sminshall      */
133227088Sminshall     count = 0;
133327088Sminshall     hadsynch = 0;
133427088Sminshall     for (i = 1; i < argc; i++) {
133527088Sminshall 	s = getsend(argv[i]);
133627088Sminshall 	if (s == 0) {
133727088Sminshall 	    printf("Unknown send argument '%s'\n'send ?' for help.\n",
133827088Sminshall 			argv[i]);
1339*27261Sminshall 	    return 0;
134027186Sminshall 	} else if (s == Ambiguous(struct sendlist *)) {
134127088Sminshall 	    printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
134227088Sminshall 			argv[i]);
1343*27261Sminshall 	    return 0;
134427088Sminshall 	}
134527088Sminshall 	switch (s->what) {
134627088Sminshall 	case SENDQUESTION:
134727088Sminshall 	    break;
134827088Sminshall 	case SENDESCAPE:
134927088Sminshall 	    count += 1;
135027088Sminshall 	    break;
135127088Sminshall 	case SYNCH:
135227088Sminshall 	    hadsynch = 1;
135327088Sminshall 	    count += 2;
135427088Sminshall 	    break;
135527088Sminshall 	default:
135627088Sminshall 	    count += 2;
135727088Sminshall 	    break;
135827088Sminshall 	}
135927088Sminshall     }
136027088Sminshall     /* Now, do we have enough room? */
136127228Sminshall     if (NETROOM() < count) {
136227088Sminshall 	printf("There is not enough room in the buffer TO the network\n");
136327088Sminshall 	printf("to process your request.  Nothing will be done.\n");
136427088Sminshall 	printf("('send synch' will throw away most data in the network\n");
136527088Sminshall 	printf("buffer, if this might help.)\n");
1366*27261Sminshall 	return 0;
136727088Sminshall     }
136827088Sminshall     /* OK, they are all OK, now go through again and actually send */
136927088Sminshall     for (i = 1; i < argc; i++) {
137027088Sminshall 	if (!(s = getsend(argv[i]))) {
137127088Sminshall 	    fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
137227088Sminshall 	    quit();
137327088Sminshall 	    /*NOTREACHED*/
137427088Sminshall 	}
137527088Sminshall 	if (s->routine) {
137627088Sminshall 	    (*s->routine)(s);
137727088Sminshall 	} else {
137827088Sminshall 	    switch (what = s->what) {
1379*27261Sminshall 	    case SYNCH:
1380*27261Sminshall 		dosynch();
1381*27261Sminshall 		break;
138227088Sminshall 	    case SENDQUESTION:
138327088Sminshall 		for (s = Sendlist; s->name; s++) {
1384*27261Sminshall 		    if (s->help) {
138527088Sminshall 			printf(s->name);
138627088Sminshall 			if (s->help) {
138727088Sminshall 			    printf("\t%s", s->help);
138827088Sminshall 			}
138927088Sminshall 			printf("\n");
139027088Sminshall 		    }
139127088Sminshall 		}
1392*27261Sminshall 		question = 1;
139327088Sminshall 		break;
139427088Sminshall 	    case SENDESCAPE:
139527088Sminshall 		NETADD(escape);
139627088Sminshall 		break;
139727088Sminshall 	    default:
139827088Sminshall 		NET2ADD(IAC, what);
139927088Sminshall 		break;
140027088Sminshall 	    }
140127088Sminshall 	}
140227088Sminshall     }
1403*27261Sminshall     return !question;
140427088Sminshall }
140527088Sminshall 
140627088Sminshall /*
140727088Sminshall  * The following are the routines and data structures referred
140827088Sminshall  * to by the arguments to the "toggle" command.
140927088Sminshall  */
141027088Sminshall 
1411*27261Sminshall lclchars()
141227110Sminshall {
1413*27261Sminshall     donelclchars = 1;
1414*27261Sminshall     return 1;
141527110Sminshall }
141627110Sminshall 
141727178Sminshall togdebug()
141827088Sminshall {
141927110Sminshall     if (net > 0 &&
142027186Sminshall 	setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug))
142127186Sminshall 									< 0) {
142227110Sminshall 	    perror("setsockopt (SO_DEBUG)");
142327186Sminshall     }
1424*27261Sminshall     return 1;
142527088Sminshall }
142627088Sminshall 
142727088Sminshall 
142827088Sminshall 
142927088Sminshall int togglehelp();
143027088Sminshall 
143127178Sminshall struct togglelist {
143227178Sminshall     char	*name;		/* name of toggle */
143327178Sminshall     char	*help;		/* help message */
143427186Sminshall     int		(*handler)();	/* routine to do actual setting */
143527178Sminshall     int		dohelp;		/* should we display help information */
143627178Sminshall     int		*variable;
143727178Sminshall     char	*actionexplanation;
143827178Sminshall };
143927178Sminshall 
144027178Sminshall struct togglelist Togglelist[] = {
1441*27261Sminshall     { "autoflush",
1442*27261Sminshall 	"toggle flushing of output when sending interrupt characters",
144327186Sminshall 	    0,
144427178Sminshall 		1,
1445*27261Sminshall 		    &autoflush,
1446*27261Sminshall 			"flush output when sending interrupt characters" },
144727186Sminshall     { "autosynch",
144827186Sminshall 	"toggle automatic sending of interrupt characters in urgent mode",
144927186Sminshall 	    0,
145027186Sminshall 		1,
145127186Sminshall 		    &autosynch,
145227186Sminshall 			"send interrupt characters in urgent mode" },
145327178Sminshall     { "crmod",
1454*27261Sminshall 	"toggle mapping of received carriage returns",
145527186Sminshall 	    0,
145627178Sminshall 		1,
145727178Sminshall 		    &crmod,
145827178Sminshall 			"map carriage return on output" },
1459*27261Sminshall     { "localchars",
1460*27261Sminshall 	"toggle local recognition of certain control characters",
1461*27261Sminshall 	    lclchars,
1462*27261Sminshall 		1,
1463*27261Sminshall 		    &localchars,
1464*27261Sminshall 			"recognize certain control characters" },
146527110Sminshall     { " ", "", 0, 1 },		/* empty line */
146627178Sminshall     { "debug",
146727178Sminshall 	"(debugging) toggle debugging",
146827178Sminshall 	    togdebug,
146927178Sminshall 		1,
147027178Sminshall 		    &debug,
147127178Sminshall 			"turn on socket level debugging" },
1472*27261Sminshall     { "netdata",
1473*27261Sminshall 	"(debugging) toggle printing of hexadecimal network data",
1474*27261Sminshall 	    0,
1475*27261Sminshall 		1,
1476*27261Sminshall 		    &netdata,
1477*27261Sminshall 			"print hexadecimal representation of network traffic" },
147827178Sminshall     { "options",
147927178Sminshall 	"(debugging) toggle viewing of options processing",
148027186Sminshall 	    0,
148127178Sminshall 		1,
148227178Sminshall 		    &showoptions,
148327178Sminshall 			"show option processing" },
1484*27261Sminshall     { " ", "", 0, 1 },		/* empty line */
148527178Sminshall     { "?",
148627178Sminshall 	"display help information",
148727178Sminshall 	    togglehelp,
148827178Sminshall 		1 },
148927178Sminshall     { "help",
149027178Sminshall 	"display help information",
149127178Sminshall 	    togglehelp,
149227178Sminshall 		0 },
149327088Sminshall     { 0 }
149427088Sminshall };
149527088Sminshall 
149627088Sminshall togglehelp()
149727088Sminshall {
149827178Sminshall     struct togglelist *c;
149927088Sminshall 
150027178Sminshall     for (c = Togglelist; c->name; c++) {
150127088Sminshall 	if (c->dohelp) {
150227088Sminshall 	    printf("%s\t%s\n", c->name, c->help);
150327088Sminshall 	}
150427088Sminshall     }
1505*27261Sminshall     return 0;
150627088Sminshall }
150727088Sminshall 
150827088Sminshall char **
150927088Sminshall getnexttoggle(name)
151027088Sminshall char *name;
151127088Sminshall {
151227178Sminshall     struct togglelist *c = (struct togglelist *) name;
151327088Sminshall 
151427088Sminshall     return (char **) (c+1);
151527088Sminshall }
151627088Sminshall 
151727178Sminshall struct togglelist *
151827088Sminshall gettoggle(name)
151927088Sminshall char *name;
152027088Sminshall {
152127178Sminshall     return (struct togglelist *)
152227178Sminshall 			genget(name, (char **) Togglelist, getnexttoggle);
152327088Sminshall }
152427088Sminshall 
152527088Sminshall toggle(argc, argv)
152627088Sminshall int	argc;
152727088Sminshall char	*argv[];
152827088Sminshall {
1529*27261Sminshall     int retval = 1;
153027088Sminshall     char *name;
153127178Sminshall     struct togglelist *c;
153227088Sminshall 
153327088Sminshall     if (argc < 2) {
153427088Sminshall 	fprintf(stderr,
153527088Sminshall 	    "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
1536*27261Sminshall 	return 0;
153727088Sminshall     }
153827088Sminshall     argc--;
153927088Sminshall     argv++;
154027088Sminshall     while (argc--) {
154127088Sminshall 	name = *argv++;
154227088Sminshall 	c = gettoggle(name);
154327186Sminshall 	if (c == Ambiguous(struct togglelist *)) {
154427088Sminshall 	    fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
154527088Sminshall 					name);
1546*27261Sminshall 	    return 0;
154727088Sminshall 	} else if (c == 0) {
154827088Sminshall 	    fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
154927088Sminshall 					name);
1550*27261Sminshall 	    return 0;
155127088Sminshall 	} else {
155227186Sminshall 	    if (c->variable) {
155327186Sminshall 		*c->variable = !*c->variable;		/* invert it */
155427186Sminshall 		printf("%s %s.\n", *c->variable? "Will" : "Won't",
155527186Sminshall 							c->actionexplanation);
155627186Sminshall 	    }
155727186Sminshall 	    if (c->handler) {
1558*27261Sminshall 		retval &= (*c->handler)(c);
155927186Sminshall 	    }
156027088Sminshall 	}
156127088Sminshall     }
1562*27261Sminshall     return retval;
156327088Sminshall }
156427088Sminshall 
156527088Sminshall /*
156627110Sminshall  * The following perform the "set" command.
156727110Sminshall  */
156827110Sminshall 
156927178Sminshall struct setlist {
157027178Sminshall     char *name;				/* name */
157127110Sminshall     char *help;				/* help information */
157227110Sminshall     char *charp;			/* where it is located at */
157327110Sminshall };
157427110Sminshall 
157527178Sminshall struct setlist Setlist[] = {
157627110Sminshall     { "echo", 	"character to toggle local echoing on/off", &echoc },
157727110Sminshall     { "escape",	"character to escape back to telnet command mode", &escape },
1578*27261Sminshall     { " ", "" },
1579*27261Sminshall     { " ", "The following need 'localchars' to be toggled true", 0 },
1580*27261Sminshall     { "erase",	"character to cause an Erase Character", &nttyb.sg_erase },
1581*27261Sminshall     { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
158227110Sminshall     { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
1583*27261Sminshall     { "kill",	"character to cause an Erase Line", &nttyb.sg_kill },
158427110Sminshall     { "quit",	"character to cause a Break", &ntc.t_quitc },
158527110Sminshall     { 0 }
158627110Sminshall };
158727110Sminshall 
158827110Sminshall char **
158927178Sminshall getnextset(name)
159027110Sminshall char *name;
159127110Sminshall {
159227178Sminshall     struct setlist *c = (struct setlist *)name;
159327110Sminshall 
159427110Sminshall     return (char **) (c+1);
159527110Sminshall }
159627110Sminshall 
159727178Sminshall struct setlist *
159827178Sminshall getset(name)
159927110Sminshall char *name;
160027110Sminshall {
160127178Sminshall     return (struct setlist *) genget(name, (char **) Setlist, getnextset);
160227110Sminshall }
160327110Sminshall 
160427110Sminshall setcmd(argc, argv)
160527110Sminshall int	argc;
160627110Sminshall char	*argv[];
160727110Sminshall {
160827110Sminshall     int value;
160927178Sminshall     struct setlist *ct;
161027110Sminshall 
161127110Sminshall     /* XXX back we go... sigh */
161227110Sminshall     if (argc != 3) {
1613*27261Sminshall 	if ((argc == 2) &&
1614*27261Sminshall 		    ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
1615*27261Sminshall 	    for (ct = Setlist; ct->name; ct++) {
1616*27261Sminshall 		printf("%s\t%s\n", ct->name, ct->help);
1617*27261Sminshall 	    }
1618*27261Sminshall 	    printf("?\tdisplay help information\n");
1619*27261Sminshall 	} else {
1620*27261Sminshall 	    printf("Format is 'set Name Value'\n'set ?' for help.\n");
162127110Sminshall 	}
1622*27261Sminshall 	return 0;
162327110Sminshall     }
162427110Sminshall 
162527178Sminshall     ct = getset(argv[1]);
162627110Sminshall     if (ct == 0) {
162727110Sminshall 	fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
162827110Sminshall 			argv[1]);
1629*27261Sminshall 	return 0;
163027186Sminshall     } else if (ct == Ambiguous(struct setlist *)) {
163127110Sminshall 	fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
163227110Sminshall 			argv[1]);
1633*27261Sminshall 	return 0;
163427110Sminshall     } else {
163527110Sminshall 	if (strcmp("off", argv[2])) {
163627110Sminshall 	    value = special(argv[2]);
163727110Sminshall 	} else {
163827110Sminshall 	    value = -1;
163927110Sminshall 	}
164027110Sminshall 	*(ct->charp) = value;
164127178Sminshall 	printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
164227110Sminshall     }
1643*27261Sminshall     return 1;
164427110Sminshall }
164527110Sminshall 
164627110Sminshall /*
164727110Sminshall  * The following are the data structures and routines for the
164827110Sminshall  * 'mode' command.
164927110Sminshall  */
165027110Sminshall 
165127110Sminshall dolinemode()
165227110Sminshall {
165327110Sminshall     if (hisopts[TELOPT_SGA]) {
165427186Sminshall 	wontoption(TELOPT_SGA);
165527110Sminshall     }
165627110Sminshall     if (hisopts[TELOPT_ECHO]) {
165727186Sminshall 	wontoption(TELOPT_ECHO);
165827110Sminshall     }
165927110Sminshall }
166027110Sminshall 
166127110Sminshall docharmode()
166227110Sminshall {
166327110Sminshall     if (!hisopts[TELOPT_SGA]) {
166427186Sminshall 	willoption(TELOPT_SGA);
166527110Sminshall     }
166627110Sminshall     if (!hisopts[TELOPT_ECHO]) {
166727186Sminshall 	willoption(TELOPT_ECHO);
166827110Sminshall     }
166927110Sminshall }
167027110Sminshall 
167127110Sminshall struct cmd Modelist[] = {
1672*27261Sminshall     { "character",	"character-at-a-time mode",	docharmode, 1, 1 },
167327110Sminshall     { "line",		"line-by-line mode",		dolinemode, 1, 1 },
167427110Sminshall     { 0 },
167527110Sminshall };
167627110Sminshall 
167727110Sminshall char **
167827110Sminshall getnextmode(name)
167927110Sminshall char *name;
168027110Sminshall {
168127110Sminshall     struct cmd *c = (struct cmd *) name;
168227110Sminshall 
168327110Sminshall     return (char **) (c+1);
168427110Sminshall }
168527110Sminshall 
168627110Sminshall struct cmd *
168727110Sminshall getmodecmd(name)
168827110Sminshall char *name;
168927110Sminshall {
169027110Sminshall     return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
169127110Sminshall }
169227110Sminshall 
169327110Sminshall modecmd(argc, argv)
169427110Sminshall int	argc;
169527110Sminshall char	*argv[];
169627110Sminshall {
169727110Sminshall     struct cmd *mt;
169827110Sminshall 
169927110Sminshall     if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
170027110Sminshall 	printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
170127110Sminshall 	for (mt = Modelist; mt->name; mt++) {
170227110Sminshall 	    printf("%s\t%s\n", mt->name, mt->help);
170327110Sminshall 	}
1704*27261Sminshall 	return 0;
170527110Sminshall     }
170627110Sminshall     mt = getmodecmd(argv[1]);
170727110Sminshall     if (mt == 0) {
170827110Sminshall 	fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
1709*27261Sminshall 	return 0;
171027186Sminshall     } else if (mt == Ambiguous(struct cmd *)) {
171127110Sminshall 	fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
1712*27261Sminshall 	return 0;
171327110Sminshall     } else {
171427110Sminshall 	(*mt->handler)();
171527110Sminshall     }
1716*27261Sminshall     return 1;
171727110Sminshall }
171827110Sminshall 
171927110Sminshall /*
172027178Sminshall  * The following data structures and routines implement the
172127178Sminshall  * "display" command.
172227178Sminshall  */
172327178Sminshall 
172427178Sminshall display(argc, argv)
172527178Sminshall int	argc;
172627178Sminshall char	*argv[];
172727178Sminshall {
172827178Sminshall #define	dotog(tl)	if (tl->variable && tl->actionexplanation) { \
172927178Sminshall 			    if (*tl->variable) { \
173027178Sminshall 				printf("will"); \
173127178Sminshall 			    } else { \
173227178Sminshall 				printf("won't"); \
173327178Sminshall 			    } \
173427178Sminshall 			    printf(" %s.\n", tl->actionexplanation); \
173527178Sminshall 			}
173627178Sminshall 
1737*27261Sminshall #define	doset(sl)   if (sl->name && *sl->name != ' ') { \
1738*27261Sminshall 			printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
1739*27261Sminshall 		    }
174027178Sminshall 
174127178Sminshall     struct togglelist *tl;
174227178Sminshall     struct setlist *sl;
174327178Sminshall 
174427178Sminshall     if (argc == 1) {
174527178Sminshall 	for (tl = Togglelist; tl->name; tl++) {
174627178Sminshall 	    dotog(tl);
174727178Sminshall 	}
1748*27261Sminshall 	printf("\n");
174927178Sminshall 	for (sl = Setlist; sl->name; sl++) {
175027178Sminshall 	    doset(sl);
175127178Sminshall 	}
175227178Sminshall     } else {
175327178Sminshall 	int i;
175427178Sminshall 
175527178Sminshall 	for (i = 1; i < argc; i++) {
175627178Sminshall 	    sl = getset(argv[i]);
175727178Sminshall 	    tl = gettoggle(argv[i]);
175827186Sminshall 	    if ((sl == Ambiguous(struct setlist *)) ||
175927186Sminshall 				(tl == Ambiguous(struct togglelist *))) {
176027178Sminshall 		printf("?Ambiguous argument '%s'.\n", argv[i]);
1761*27261Sminshall 		return 0;
176227178Sminshall 	    } else if (!sl && !tl) {
176327178Sminshall 		printf("?Unknown argument '%s'.\n", argv[i]);
1764*27261Sminshall 		return 0;
176527178Sminshall 	    } else {
176627186Sminshall 		if (tl) {
176727186Sminshall 		    dotog(tl);
176827186Sminshall 		}
176927186Sminshall 		if (sl) {
177027186Sminshall 		    doset(sl);
177127186Sminshall 		}
177227178Sminshall 	    }
177327178Sminshall 	}
177427178Sminshall     }
1775*27261Sminshall     return 1;
177627178Sminshall #undef	doset(sl)
177727178Sminshall #undef	dotog(tl)
177827178Sminshall }
177927178Sminshall 
178027178Sminshall /*
178127088Sminshall  * The following are the data structures, and many of the routines,
178227088Sminshall  * relating to command processing.
178327088Sminshall  */
178427088Sminshall 
178527088Sminshall /*
178627088Sminshall  * Set the escape character.
178727088Sminshall  */
178827088Sminshall setescape(argc, argv)
178927088Sminshall 	int argc;
179027088Sminshall 	char *argv[];
179127088Sminshall {
179227088Sminshall 	register char *arg;
179327088Sminshall 	char buf[50];
179427088Sminshall 
179527186Sminshall 	printf(
179627186Sminshall 	    "Deprecated usage - please use 'set escape%s%s' in the future.\n",
179727186Sminshall 				(argc > 2)? " ":"", (argc > 2)? argv[1]: "");
179827088Sminshall 	if (argc > 2)
179927088Sminshall 		arg = argv[1];
180027088Sminshall 	else {
180127088Sminshall 		printf("new escape character: ");
180227088Sminshall 		gets(buf);
180327088Sminshall 		arg = buf;
180427088Sminshall 	}
180527088Sminshall 	if (arg[0] != '\0')
180627088Sminshall 		escape = arg[0];
180727088Sminshall 	printf("Escape character is '%s'.\n", control(escape));
180827088Sminshall 	fflush(stdout);
1809*27261Sminshall 	return 1;
181027088Sminshall }
181127088Sminshall 
181227088Sminshall /*VARARGS*/
1813*27261Sminshall togcrmod()
1814*27261Sminshall {
1815*27261Sminshall     crmod = !crmod;
1816*27261Sminshall     printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
1817*27261Sminshall     printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
1818*27261Sminshall     fflush(stdout);
1819*27261Sminshall     return 1;
1820*27261Sminshall }
1821*27261Sminshall 
1822*27261Sminshall /*VARARGS*/
182327088Sminshall suspend()
182427088Sminshall {
182527110Sminshall 	setcommandmode();
182627088Sminshall 	kill(0, SIGTSTP);
182727088Sminshall 	/* reget parameters in case they were changed */
182827088Sminshall 	ioctl(0, TIOCGETP, (char *)&ottyb);
182927088Sminshall 	ioctl(0, TIOCGETC, (char *)&otc);
183027088Sminshall 	ioctl(0, TIOCGLTC, (char *)&oltc);
1831*27261Sminshall 	return 1;
183227088Sminshall }
183327088Sminshall 
183427088Sminshall /*VARARGS*/
183527088Sminshall bye()
183627088Sminshall {
183727088Sminshall 	register char *op;
183827088Sminshall 
183927088Sminshall 	if (connected) {
184027088Sminshall 		shutdown(net, 2);
184127088Sminshall 		printf("Connection closed.\n");
184227088Sminshall 		close(net);
184327088Sminshall 		connected = 0;
184427088Sminshall 		/* reset his options */
184527088Sminshall 		for (op = hisopts; op < &hisopts[256]; op++)
184627088Sminshall 			*op = 0;
184727088Sminshall 	}
1848*27261Sminshall 	return 1;
184927088Sminshall }
185027088Sminshall 
185127088Sminshall /*VARARGS*/
185227088Sminshall quit()
185327088Sminshall {
1854*27261Sminshall 	(void) call(bye, "bye", 0);
185527088Sminshall 	exit(0);
1856*27261Sminshall 	/*NOTREACHED*/
185727088Sminshall }
185827088Sminshall 
185927088Sminshall /*
186027088Sminshall  * Print status about the connection.
186127088Sminshall  */
186227186Sminshall /*ARGSUSED*/
186327178Sminshall status(argc, argv)
186427178Sminshall int	argc;
186527178Sminshall char	*argv[];
186627088Sminshall {
186727178Sminshall     if (connected) {
186827178Sminshall 	printf("Connected to %s.\n", hostname);
186927178Sminshall 	if (argc < 2) {
187027178Sminshall 	    printf("Operating in %s.\n", modedescriptions[getconnmode()]);
1871*27261Sminshall 	    if (localchars) {
187227178Sminshall 		printf("Catching signals locally.\n");
187327178Sminshall 	    }
187427110Sminshall 	}
187527178Sminshall     } else {
187627178Sminshall 	printf("No connection.\n");
187727178Sminshall     }
187827178Sminshall     printf("Escape character is '%s'.\n", control(escape));
187927178Sminshall     fflush(stdout);
1880*27261Sminshall     return 1;
188127088Sminshall }
188227088Sminshall 
188327088Sminshall tn(argc, argv)
188427088Sminshall 	int argc;
188527088Sminshall 	char *argv[];
188627088Sminshall {
188727088Sminshall 	register struct hostent *host = 0;
188827088Sminshall 
188927088Sminshall 	if (connected) {
189027088Sminshall 		printf("?Already connected to %s\n", hostname);
1891*27261Sminshall 		return 0;
189227088Sminshall 	}
189327088Sminshall 	if (argc < 2) {
189427186Sminshall 		(void) strcpy(line, "Connect ");
189527088Sminshall 		printf("(to) ");
189627088Sminshall 		gets(&line[strlen(line)]);
189727088Sminshall 		makeargv();
189827088Sminshall 		argc = margc;
189927088Sminshall 		argv = margv;
190027088Sminshall 	}
190127088Sminshall 	if (argc > 3) {
190227088Sminshall 		printf("usage: %s host-name [port]\n", argv[0]);
1903*27261Sminshall 		return 0;
190427088Sminshall 	}
190527088Sminshall 	sin.sin_addr.s_addr = inet_addr(argv[1]);
190627088Sminshall 	if (sin.sin_addr.s_addr != -1) {
190727088Sminshall 		sin.sin_family = AF_INET;
190827186Sminshall 		(void) strcpy(hnamebuf, argv[1]);
190927088Sminshall 		hostname = hnamebuf;
191027088Sminshall 	} else {
191127088Sminshall 		host = gethostbyname(argv[1]);
191227088Sminshall 		if (host) {
191327088Sminshall 			sin.sin_family = host->h_addrtype;
191427088Sminshall 			bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
191527088Sminshall 				host->h_length);
191627088Sminshall 			hostname = host->h_name;
191727088Sminshall 		} else {
191827088Sminshall 			printf("%s: unknown host\n", argv[1]);
1919*27261Sminshall 			return 0;
192027088Sminshall 		}
192127088Sminshall 	}
192227088Sminshall 	sin.sin_port = sp->s_port;
192327088Sminshall 	if (argc == 3) {
192427088Sminshall 		sin.sin_port = atoi(argv[2]);
192527186Sminshall 		if (sin.sin_port == 0) {
192627088Sminshall 			sp = getservbyname(argv[2], "tcp");
192727088Sminshall 			if (sp)
192827088Sminshall 				sin.sin_port = sp->s_port;
192927088Sminshall 			else {
193027088Sminshall 				printf("%s: bad port number\n", argv[2]);
1931*27261Sminshall 				return 0;
193227088Sminshall 			}
193327088Sminshall 		} else {
193427088Sminshall 			sin.sin_port = atoi(argv[2]);
193527088Sminshall 			sin.sin_port = htons(sin.sin_port);
193627088Sminshall 		}
193727088Sminshall 		telnetport = 0;
193827110Sminshall 	} else {
193927110Sminshall 		telnetport = 1;
194027088Sminshall 	}
194127088Sminshall 	signal(SIGINT, intr);
194227110Sminshall 	signal(SIGQUIT, intr2);
194327088Sminshall 	signal(SIGPIPE, deadpeer);
194427088Sminshall 	printf("Trying...\n");
194527088Sminshall 	do {
194627088Sminshall 		net = socket(AF_INET, SOCK_STREAM, 0);
194727088Sminshall 		if (net < 0) {
194827088Sminshall 			perror("telnet: socket");
1949*27261Sminshall 			return 0;
195027088Sminshall 		}
195127186Sminshall 		if (debug &&
195227186Sminshall 				setsockopt(net, SOL_SOCKET, SO_DEBUG,
195327186Sminshall 					(char *)&debug, sizeof(debug)) < 0) {
195427088Sminshall 			perror("setsockopt (SO_DEBUG)");
195527186Sminshall 		}
195627186Sminshall 		if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
195727088Sminshall 			if (host && host->h_addr_list[1]) {
195827088Sminshall 				int oerrno = errno;
195927088Sminshall 
196027088Sminshall 				fprintf(stderr,
196127088Sminshall 				    "telnet: connect to address %s: ",
196227088Sminshall 				    inet_ntoa(sin.sin_addr));
196327088Sminshall 				errno = oerrno;
196427186Sminshall 				perror((char *)0);
196527088Sminshall 				host->h_addr_list++;
196627088Sminshall 				bcopy(host->h_addr_list[0],
196727088Sminshall 				    (caddr_t)&sin.sin_addr, host->h_length);
196827088Sminshall 				fprintf(stderr, "Trying %s...\n",
196927088Sminshall 					inet_ntoa(sin.sin_addr));
197027088Sminshall 				(void) close(net);
197127088Sminshall 				continue;
197227088Sminshall 			}
197327088Sminshall 			perror("telnet: connect");
197427088Sminshall 			signal(SIGINT, SIG_DFL);
1975*27261Sminshall 			signal(SIGQUIT, SIG_DFL);
1976*27261Sminshall 			return 0;
197727088Sminshall 		}
197827088Sminshall 		connected++;
197927088Sminshall 	} while (connected == 0);
198027178Sminshall 	call(status, "status", "notmuch", 0);
198127088Sminshall 	if (setjmp(peerdied) == 0)
198227088Sminshall 		telnet();
198327088Sminshall 	fprintf(stderr, "Connection closed by foreign host.\n");
198427088Sminshall 	exit(1);
1985*27261Sminshall 	/*NOTREACHED*/
198627088Sminshall }
198727088Sminshall 
198827088Sminshall 
198927088Sminshall #define HELPINDENT (sizeof ("connect"))
199027088Sminshall 
199127088Sminshall char	openhelp[] =	"connect to a site";
199227088Sminshall char	closehelp[] =	"close current connection";
199327088Sminshall char	quithelp[] =	"exit telnet";
199427088Sminshall char	zhelp[] =	"suspend telnet";
199527088Sminshall char	statushelp[] =	"print status information";
199627088Sminshall char	helphelp[] =	"print help information";
199727110Sminshall char	sendhelp[] =	"transmit special characters ('send ?' for more)";
199827178Sminshall char	sethelp[] = 	"set operating parameters ('set ?' for more)";
199927178Sminshall char	togglestring[] ="toggle operating parameters ('toggle ?' for more)";
200027178Sminshall char	displayhelp[] =	"display operating parameters";
200127178Sminshall char	modehelp[] =
200227178Sminshall 		"try to enter line-by-line or character-at-a-time mode";
200327088Sminshall 
200427088Sminshall int	help();
200527088Sminshall 
200627088Sminshall struct cmd cmdtab[] = {
2007*27261Sminshall 	{ "close",	closehelp,	bye,		1, 1 },
2008*27261Sminshall 	{ "display",	displayhelp,	display,	1, 0 },
2009*27261Sminshall 	{ "mode",	modehelp,	modecmd,	1, 1 },
201027110Sminshall 	{ "open",	openhelp,	tn,		1, 0 },
201127110Sminshall 	{ "quit",	quithelp,	quit,		1, 0 },
201227110Sminshall 	{ "send",	sendhelp,	sendcmd,	1, 1 },
201327178Sminshall 	{ "set",	sethelp,	setcmd,		1, 0 },
2014*27261Sminshall 	{ "status",	statushelp,	status,		1, 0 },
201527110Sminshall 	{ "toggle",	togglestring,	toggle,		1, 0 },
2016*27261Sminshall 	{ "z",		zhelp,		suspend,	1, 0 },
201727110Sminshall 	{ "?",		helphelp,	help,		1, 0 },
2018*27261Sminshall 	0
2019*27261Sminshall };
2020*27261Sminshall 
2021*27261Sminshall char	crmodhelp[] =	"deprecated command -- use 'toggle crmod' instead";
2022*27261Sminshall char	escapehelp[] =	"deprecated command -- use 'set escape' instead";
2023*27261Sminshall 
2024*27261Sminshall struct cmd cmdtab2[] = {
202527110Sminshall 	{ "help",	helphelp,	help,		0, 0 },
2026*27261Sminshall 	{ "escape",	escapehelp,	setescape,	1, 0 },
2027*27261Sminshall 	{ "crmod",	crmodhelp,	togcrmod,	1, 0 },
202827088Sminshall 	0
202927088Sminshall };
203027088Sminshall 
203127088Sminshall /*
203227088Sminshall  * Help command.
203327088Sminshall  */
203427088Sminshall help(argc, argv)
203527088Sminshall 	int argc;
203627088Sminshall 	char *argv[];
203727088Sminshall {
203827088Sminshall 	register struct cmd *c;
203927088Sminshall 
204027088Sminshall 	if (argc == 1) {
204127088Sminshall 		printf("Commands may be abbreviated.  Commands are:\n\n");
204227088Sminshall 		for (c = cmdtab; c->name; c++)
204327088Sminshall 			if (c->dohelp) {
204427088Sminshall 				printf("%-*s\t%s\n", HELPINDENT, c->name,
204527088Sminshall 								    c->help);
204627088Sminshall 			}
2047*27261Sminshall 		return 0;
204827088Sminshall 	}
204927088Sminshall 	while (--argc > 0) {
205027088Sminshall 		register char *arg;
205127088Sminshall 		arg = *++argv;
205227088Sminshall 		c = getcmd(arg);
205327186Sminshall 		if (c == Ambiguous(struct cmd *))
205427088Sminshall 			printf("?Ambiguous help command %s\n", arg);
205527088Sminshall 		else if (c == (struct cmd *)0)
205627088Sminshall 			printf("?Invalid help command %s\n", arg);
205727088Sminshall 		else
205827088Sminshall 			printf("%s\n", c->help);
205927088Sminshall 	}
2060*27261Sminshall 	return 0;
206127088Sminshall }
206227088Sminshall /*
206327088Sminshall  * Call routine with argc, argv set from args (terminated by 0).
206427088Sminshall  * VARARGS2
206527088Sminshall  */
206627088Sminshall call(routine, args)
206727088Sminshall 	int (*routine)();
206827186Sminshall 	char *args;
206927088Sminshall {
207027186Sminshall 	register char **argp;
207127088Sminshall 	register int argc;
207227088Sminshall 
207327088Sminshall 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
207427088Sminshall 		;
2075*27261Sminshall 	return (*routine)(argc, &args);
207627088Sminshall }
207727088Sminshall 
207827088Sminshall makeargv()
207927088Sminshall {
208027088Sminshall 	register char *cp;
208127088Sminshall 	register char **argp = margv;
208227088Sminshall 
208327088Sminshall 	margc = 0;
208427088Sminshall 	for (cp = line; *cp;) {
208527088Sminshall 		while (isspace(*cp))
208627088Sminshall 			cp++;
208727088Sminshall 		if (*cp == '\0')
208827088Sminshall 			break;
208927088Sminshall 		*argp++ = cp;
209027088Sminshall 		margc += 1;
209127088Sminshall 		while (*cp != '\0' && !isspace(*cp))
209227088Sminshall 			cp++;
209327088Sminshall 		if (*cp == '\0')
209427088Sminshall 			break;
209527088Sminshall 		*cp++ = '\0';
209627088Sminshall 	}
209727088Sminshall 	*argp++ = 0;
209827088Sminshall }
209927088Sminshall 
210027088Sminshall char **
210127088Sminshall getnextcmd(name)
210227088Sminshall char *name;
210327088Sminshall {
210427088Sminshall     struct cmd *c = (struct cmd *) name;
210527088Sminshall 
210627088Sminshall     return (char **) (c+1);
210727088Sminshall }
210827088Sminshall 
210927088Sminshall struct cmd *
211027088Sminshall getcmd(name)
211127088Sminshall char *name;
211227088Sminshall {
2113*27261Sminshall     struct cmd *cm;
2114*27261Sminshall 
2115*27261Sminshall     if (cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) {
2116*27261Sminshall 	return cm;
2117*27261Sminshall     } else {
2118*27261Sminshall 	return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
2119*27261Sminshall     }
212027088Sminshall }
212127088Sminshall 
212227088Sminshall command(top)
212327088Sminshall 	int top;
212427088Sminshall {
212527088Sminshall 	register struct cmd *c;
212627088Sminshall 
212727110Sminshall 	setcommandmode();
2128*27261Sminshall 	if (!top) {
212927088Sminshall 		putchar('\n');
2130*27261Sminshall 	} else {
213127088Sminshall 		signal(SIGINT, SIG_DFL);
2132*27261Sminshall 		signal(SIGQUIT, SIG_DFL);
2133*27261Sminshall 	}
213427088Sminshall 	for (;;) {
213527088Sminshall 		printf("%s> ", prompt);
213627088Sminshall 		if (gets(line) == 0) {
213727088Sminshall 			if (feof(stdin))
213827088Sminshall 				quit();
213927088Sminshall 			break;
214027088Sminshall 		}
214127088Sminshall 		if (line[0] == 0)
214227088Sminshall 			break;
214327088Sminshall 		makeargv();
214427088Sminshall 		c = getcmd(margv[0]);
214527186Sminshall 		if (c == Ambiguous(struct cmd *)) {
214627088Sminshall 			printf("?Ambiguous command\n");
214727088Sminshall 			continue;
214827088Sminshall 		}
214927088Sminshall 		if (c == 0) {
215027088Sminshall 			printf("?Invalid command\n");
215127088Sminshall 			continue;
215227088Sminshall 		}
215327088Sminshall 		if (c->needconnect && !connected) {
215427088Sminshall 			printf("?Need to be connected first.\n");
215527088Sminshall 			continue;
215627088Sminshall 		}
2157*27261Sminshall 		if ((*c->handler)(margc, margv)) {
215827088Sminshall 			break;
2159*27261Sminshall 		}
216027088Sminshall 	}
216127088Sminshall 	if (!top) {
216227110Sminshall 		if (!connected) {
216327088Sminshall 			longjmp(toplevel, 1);
216427110Sminshall 			/*NOTREACHED*/
216527110Sminshall 		}
216627110Sminshall 		setconnmode();
216727088Sminshall 	}
216827088Sminshall }
216927186Sminshall 
217027186Sminshall /*
217127186Sminshall  * main.  Parse arguments, invoke the protocol or command parser.
217227186Sminshall  */
217327186Sminshall 
217427186Sminshall 
217527186Sminshall main(argc, argv)
217627186Sminshall 	int argc;
217727186Sminshall 	char *argv[];
217827186Sminshall {
217927186Sminshall 	sp = getservbyname("telnet", "tcp");
218027186Sminshall 	if (sp == 0) {
218127186Sminshall 		fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
218227186Sminshall 		exit(1);
218327186Sminshall 	}
218427186Sminshall 	NetTrace = stdout;
218527186Sminshall 	ioctl(0, TIOCGETP, (char *)&ottyb);
218627186Sminshall 	ioctl(0, TIOCGETC, (char *)&otc);
218727186Sminshall 	ioctl(0, TIOCGLTC, (char *)&oltc);
218827228Sminshall #if	defined(LNOFLSH)
218927228Sminshall 	ioctl(0, TIOCLGET, (char *)&autoflush);
2190*27261Sminshall 	autoflush = !(autoflush&LNOFLSH);	/* if LNOFLSH, no autoflush */
2191*27261Sminshall #else	/* LNOFLSH */
2192*27261Sminshall 	autoflush = 1;
219327228Sminshall #endif	/* LNOFLSH */
219427186Sminshall 	ntc = otc;
219527186Sminshall 	ntc.t_eofc = -1;		/* we don't want to use EOF */
219627228Sminshall 	nltc = oltc;
219727186Sminshall 	nttyb = ottyb;
219827186Sminshall 	setbuf(stdin, (char *)0);
219927186Sminshall 	setbuf(stdout, (char *)0);
220027186Sminshall 	prompt = argv[0];
220127186Sminshall 	if (argc > 1 && !strcmp(argv[1], "-d")) {
220227186Sminshall 		debug = 1;
220327186Sminshall 		argv++;
220427186Sminshall 		argc--;
220527186Sminshall 	}
220627186Sminshall 	if (argc > 1 && !strcmp(argv[1], "-n")) {
220727186Sminshall 	    argv++;
220827186Sminshall 	    argc--;
220927186Sminshall 	    if (argc > 1) {		/* get file name */
221027186Sminshall 		NetTrace = fopen(argv[1], "w");
221127186Sminshall 		argv++;
221227186Sminshall 		argc--;
221327186Sminshall 		if (NetTrace == NULL) {
221427186Sminshall 		    NetTrace = stdout;
221527186Sminshall 		}
221627186Sminshall 	    }
221727186Sminshall 	}
221827186Sminshall 	if (argc != 1) {
221927186Sminshall 		if (setjmp(toplevel) != 0)
222027186Sminshall 			exit(0);
222127186Sminshall 		tn(argc, argv);
222227186Sminshall 	}
222327186Sminshall 	setjmp(toplevel);
222427186Sminshall 	for (;;)
222527186Sminshall 		command(1);
222627186Sminshall }
2227