xref: /csrg-svn/usr.bin/telnet/telnet.c (revision 39529)
133685Sbostic /*
233685Sbostic  * Copyright (c) 1988 Regents of the University of California.
333685Sbostic  * All rights reserved.
433685Sbostic  *
533685Sbostic  * Redistribution and use in source and binary forms are permitted
634898Sbostic  * provided that the above copyright notice and this paragraph are
734898Sbostic  * duplicated in all such forms and that any documentation,
834898Sbostic  * advertising materials, and other materials related to such
934898Sbostic  * distribution and use acknowledge that the software was developed
1034898Sbostic  * by the University of California, Berkeley.  The name of the
1134898Sbostic  * University may not be used to endorse or promote products derived
1234898Sbostic  * from this software without specific prior written permission.
1334898Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434898Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534898Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1633685Sbostic  */
1711758Ssam 
1821580Sdist #ifndef lint
19*39529Sborman static char sccsid[] = "@(#)telnet.c	5.44 (Berkeley) 11/14/89";
2033685Sbostic #endif /* not lint */
2121580Sdist 
229217Ssam #include <sys/types.h>
239217Ssam 
2432377Sminshall #if	defined(unix)
2533804Sminshall #include <signal.h>
2632377Sminshall /* By the way, we need to include curses.h before telnet.h since,
2732377Sminshall  * among other things, telnet.h #defines 'DO', which is a variable
2832377Sminshall  * declared in curses.h.
2932377Sminshall  */
3032377Sminshall #endif	/* defined(unix) */
3132377Sminshall 
3212212Ssam #include <arpa/telnet.h>
3332377Sminshall 
3432377Sminshall #if	defined(unix)
3527186Sminshall #include <strings.h>
3632377Sminshall #else	/* defined(unix) */
3732377Sminshall #include <string.h>
3832377Sminshall #endif	/* defined(unix) */
399217Ssam 
4038908Sborman #include <ctype.h>
4138908Sborman 
4232381Sminshall #include "ring.h"
4332381Sminshall 
4432377Sminshall #include "defines.h"
4532377Sminshall #include "externs.h"
4632377Sminshall #include "types.h"
4732377Sminshall #include "general.h"
4827178Sminshall 
4927178Sminshall 
5027228Sminshall #define	strip(x)	((x)&0x7f)
516000Sroot 
5227088Sminshall 
5332377Sminshall static char	subbuffer[SUBBUFSIZE],
5432377Sminshall 		*subpointer, *subend;	 /* buffer for sub-options */
5527676Sminshall #define	SB_CLEAR()	subpointer = subbuffer;
5627676Sminshall #define	SB_TERM()	subend = subpointer;
5727676Sminshall #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
5827676Sminshall 				*subpointer++ = (c); \
5927676Sminshall 			}
6027676Sminshall 
6137226Sminshall char	options[256];		/* The combined options */
6238689Sborman char	do_dont_resp[256];
6338689Sborman char	will_wont_resp[256];
646000Sroot 
6532377Sminshall int
6632377Sminshall 	connected,
6732377Sminshall 	showoptions,
6832377Sminshall 	In3270,		/* Are we in 3270 mode? */
6932377Sminshall 	ISend,		/* trying to send network data in */
7032377Sminshall 	debug = 0,
7132377Sminshall 	crmod,
7232377Sminshall 	netdata,	/* Print out network data flow */
7332377Sminshall 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
7434848Sminshall #if	defined(TN3270)
7536241Sminshall 	noasynchtty = 0,/* User specified "-noasynch" on command line */
7636241Sminshall 	noasynchnet = 0,/* User specified "-noasynch" on command line */
7732377Sminshall 	askedSGA = 0,	/* We have talked about suppress go ahead */
7834848Sminshall #endif	/* defined(TN3270) */
7933286Sminshall 	telnetport,
8032531Sminshall 	SYNCHing,	/* we are in TELNET SYNCH mode */
8132531Sminshall 	flushout,	/* flush output */
8232531Sminshall 	autoflush = 0,	/* flush output when interrupting? */
8332531Sminshall 	autosynch,	/* send interrupt characters with SYNCH? */
8437219Sminshall 	localflow,	/* we handle flow control locally */
8532531Sminshall 	localchars,	/* we recognize interrupt/quit */
8632531Sminshall 	donelclchars,	/* the user has set "localchars" */
8732531Sminshall 	donebinarytoggle,	/* the user has put us in binary */
8832531Sminshall 	dontlecho,	/* do we suppress local echoing right now? */
8932531Sminshall 	globalmode;
9027088Sminshall 
9132377Sminshall #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
926000Sroot 
93*39529Sborman unsigned char
9432377Sminshall 	*prompt = 0,
9532377Sminshall 	escape,
9632377Sminshall 	echoc;
9727186Sminshall 
9827186Sminshall /*
996000Sroot  * Telnet receiver states for fsm
1006000Sroot  */
1016000Sroot #define	TS_DATA		0
1026000Sroot #define	TS_IAC		1
1036000Sroot #define	TS_WILL		2
1046000Sroot #define	TS_WONT		3
1056000Sroot #define	TS_DO		4
1066000Sroot #define	TS_DONT		5
10727021Sminshall #define	TS_CR		6
10827676Sminshall #define	TS_SB		7		/* sub-option collection */
10927676Sminshall #define	TS_SE		8		/* looking for sub-option end */
1106000Sroot 
11132377Sminshall static int	telrcv_state;
1126000Sroot 
11332377Sminshall jmp_buf	toplevel = { 0 };
11432377Sminshall jmp_buf	peerdied;
1156000Sroot 
11632377Sminshall int	flushline;
11738811Sborman int	linemode;
11827021Sminshall 
11938689Sborman #ifdef	KLUDGELINEMODE
12038689Sborman int	kludgelinemode = 1;
12138689Sborman #endif
12238689Sborman 
12332377Sminshall /*
12432377Sminshall  * The following are some clocks used to decide how to interpret
12532377Sminshall  * the relationship between various variables.
12632377Sminshall  */
1276000Sroot 
12832377Sminshall Clocks clocks;
12932377Sminshall 
13038689Sborman #ifdef	notdef
13132377Sminshall Modelist modelist[] = {
13232377Sminshall 	{ "telnet command mode", COMMAND_LINE },
13332377Sminshall 	{ "character-at-a-time mode", 0 },
13432377Sminshall 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
13532377Sminshall 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
13632377Sminshall 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
13732377Sminshall 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
13832377Sminshall 	{ "3270 mode", 0 },
13932377Sminshall };
14038689Sborman #endif
1416000Sroot 
14232377Sminshall 
14332377Sminshall /*
14432377Sminshall  * Initialize telnet environment.
14532377Sminshall  */
1466000Sroot 
14732377Sminshall init_telnet()
14832377Sminshall {
14932377Sminshall     SB_CLEAR();
15037226Sminshall     ClearArray(options);
1516000Sroot 
15237219Sminshall     connected = In3270 = ISend = localflow = donebinarytoggle = 0;
1536000Sroot 
15432377Sminshall     SYNCHing = 0;
1556000Sroot 
15632377Sminshall     /* Don't change NetTrace */
1576000Sroot 
15832377Sminshall     escape = CONTROL(']');
15932377Sminshall     echoc = CONTROL('E');
1606000Sroot 
16132377Sminshall     flushline = 1;
16232377Sminshall     telrcv_state = TS_DATA;
16332377Sminshall }
16432554Sminshall 
1656000Sroot 
16632554Sminshall #include <varargs.h>
1676000Sroot 
16834848Sminshall /*VARARGS*/
16932554Sminshall static void
17032554Sminshall printring(va_alist)
17132554Sminshall va_dcl
17232554Sminshall {
17332554Sminshall     va_list ap;
17432554Sminshall     char buffer[100];		/* where things go */
17532554Sminshall     char *ptr;
17632554Sminshall     char *format;
17732554Sminshall     char *string;
17832554Sminshall     Ring *ring;
17932554Sminshall     int i;
18032554Sminshall 
18132554Sminshall     va_start(ap);
18232554Sminshall 
18332554Sminshall     ring = va_arg(ap, Ring *);
18432554Sminshall     format = va_arg(ap, char *);
18532554Sminshall     ptr = buffer;
18632554Sminshall 
18732554Sminshall     while ((i = *format++) != 0) {
18832554Sminshall 	if (i == '%') {
18932554Sminshall 	    i = *format++;
19032554Sminshall 	    switch (i) {
19132554Sminshall 	    case 'c':
19232554Sminshall 		*ptr++ = va_arg(ap, int);
19332554Sminshall 		break;
19432554Sminshall 	    case 's':
19532554Sminshall 		string = va_arg(ap, char *);
19632554Sminshall 		ring_supply_data(ring, buffer, ptr-buffer);
19732554Sminshall 		ring_supply_data(ring, string, strlen(string));
19832554Sminshall 		ptr = buffer;
19932554Sminshall 		break;
20032554Sminshall 	    case 0:
20132554Sminshall 		ExitString("printring: trailing %%.\n", 1);
20232554Sminshall 		/*NOTREACHED*/
20332554Sminshall 	    default:
20432554Sminshall 		ExitString("printring: unknown format character.\n", 1);
20532554Sminshall 		/*NOTREACHED*/
20632554Sminshall 	    }
20732554Sminshall 	} else {
20832554Sminshall 	    *ptr++ = i;
20932554Sminshall 	}
21032554Sminshall     }
21132554Sminshall     ring_supply_data(ring, buffer, ptr-buffer);
21232554Sminshall }
21332554Sminshall 
21437226Sminshall /*
21537226Sminshall  * These routines are in charge of sending option negotiations
21637226Sminshall  * to the other side.
21737226Sminshall  *
21837226Sminshall  * The basic idea is that we send the negotiation if either side
21937226Sminshall  * is in disagreement as to what the current state should be.
22037226Sminshall  */
22132554Sminshall 
22238689Sborman send_do(c, init)
22338689Sborman register int c, init;
2246000Sroot {
22538689Sborman     if (init) {
22638689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
22738689Sborman 				my_want_state_is_do(c))
22838689Sborman 	    return;
22938689Sborman 	set_my_want_state_do(c);
23038689Sborman 	do_dont_resp[c]++;
23137226Sminshall     }
23238689Sborman     NET2ADD(IAC, DO);
23338689Sborman     NETADD(c);
23438689Sborman     printoption("SENT", "do", c);
23537226Sminshall }
23637226Sminshall 
23737226Sminshall void
23838689Sborman send_dont(c, init)
23938689Sborman register int c, init;
24037226Sminshall {
24138689Sborman     if (init) {
24238689Sborman 	if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
24338689Sborman 				my_want_state_is_dont(c))
24438689Sborman 	    return;
24538689Sborman 	set_my_want_state_dont(c);
24638689Sborman 	do_dont_resp[c]++;
24737226Sminshall     }
24838689Sborman     NET2ADD(IAC, DONT);
24938689Sborman     NETADD(c);
25038689Sborman     printoption("SENT", "dont", c);
25137226Sminshall }
25237226Sminshall 
25337226Sminshall void
25438689Sborman send_will(c, init)
25538689Sborman register int c, init;
25637226Sminshall {
25738689Sborman     if (init) {
25838689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
25938689Sborman 				my_want_state_is_will(c))
26038689Sborman 	    return;
26138689Sborman 	set_my_want_state_will(c);
26238689Sborman 	will_wont_resp[c]++;
26337226Sminshall     }
26438689Sborman     NET2ADD(IAC, WILL);
26538689Sborman     NETADD(c);
26638689Sborman     printoption("SENT", "will", c);
26737226Sminshall }
26837226Sminshall 
26937226Sminshall void
27038689Sborman send_wont(c, init)
27138689Sborman register int c, init;
27237226Sminshall {
27338689Sborman     if (init) {
27438689Sborman 	if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
27538689Sborman 				my_want_state_is_wont(c))
27638689Sborman 	    return;
27738689Sborman 	set_my_want_state_wont(c);
27838689Sborman 	will_wont_resp[c]++;
27937226Sminshall     }
28038689Sborman     NET2ADD(IAC, WONT);
28138689Sborman     NETADD(c);
28238689Sborman     printoption("SENT", "wont", c);
28337226Sminshall }
28437226Sminshall 
28537226Sminshall 
28637226Sminshall void
28737226Sminshall willoption(option)
28837226Sminshall 	int option;
28937226Sminshall {
2906000Sroot 	char *fmt;
29138689Sborman 	int new_state_ok = 0;
2926000Sroot 
29338689Sborman 	if (do_dont_resp[option]) {
29438689Sborman 	    --do_dont_resp[option];
29538689Sborman 	    if (do_dont_resp[option] && my_state_is_do(option))
29638689Sborman 		--do_dont_resp[option];
29738689Sborman 	}
29837226Sminshall 
29938689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
3006000Sroot 
30138689Sborman 	    switch (option) {
30238689Sborman 
30338689Sborman 	    case TELOPT_ECHO:
30438689Sborman #	    if defined(TN3270)
30538689Sborman 		/*
30638689Sborman 		 * The following is a pain in the rear-end.
30738689Sborman 		 * Various IBM servers (some versions of Wiscnet,
30838689Sborman 		 * possibly Fibronics/Spartacus, and who knows who
30938689Sborman 		 * else) will NOT allow us to send "DO SGA" too early
31038689Sborman 		 * in the setup proceedings.  On the other hand,
31138689Sborman 		 * 4.2 servers (telnetd) won't set SGA correctly.
31238689Sborman 		 * So, we are stuck.  Empirically (but, based on
31338689Sborman 		 * a VERY small sample), the IBM servers don't send
31438689Sborman 		 * out anything about ECHO, so we postpone our sending
31538689Sborman 		 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
31638689Sborman 		 * DO send).
31738689Sborman 		  */
31838689Sborman 		{
31938689Sborman 		    if (askedSGA == 0) {
32038689Sborman 			askedSGA = 1;
32138689Sborman 			if (my_want_state_is_dont(TELOPT_SGA))
32238689Sborman 			    send_do(TELOPT_SGA, 1);
32332377Sminshall 		    }
32432377Sminshall 		}
32538689Sborman 		    /* Fall through */
32638689Sborman 	    case TELOPT_EOR:
32738908Sborman #endif	    /* defined(TN3270) */
32838689Sborman 	    case TELOPT_BINARY:
32938689Sborman 	    case TELOPT_SGA:
33027110Sminshall 		settimer(modenegotiated);
33138908Sborman 		/* FALL THROUGH */
33238908Sborman 	    case TELOPT_STATUS:
33338689Sborman 		new_state_ok = 1;
3346000Sroot 		break;
3356000Sroot 
33638689Sborman 	    case TELOPT_TM:
33738689Sborman 		if (flushout)
33838689Sborman 		    flushout = 0;
33938689Sborman 		/*
34038689Sborman 		 * Special case for TM.  If we get back a WILL,
34138689Sborman 		 * pretend we got back a WONT.
34238689Sborman 		 */
34338689Sborman 		set_my_want_state_dont(option);
34438689Sborman 		set_my_state_dont(option);
34527110Sminshall 		return;			/* Never reply to TM will's/wont's */
3466000Sroot 
34738689Sborman 	    case TELOPT_LINEMODE:
34838689Sborman 	    default:
3496000Sroot 		break;
35038689Sborman 	    }
35138689Sborman 
35238689Sborman 	    if (new_state_ok) {
35338689Sborman 		set_my_want_state_do(option);
35438689Sborman 		send_do(option, 0);
35538689Sborman 		setconnmode(0);		/* possibly set new tty mode */
35638689Sborman 	    } else {
35738689Sborman 		do_dont_resp[option]++;
35838689Sborman 		send_dont(option, 0);
35938689Sborman 	    }
3606000Sroot 	}
36138689Sborman 	set_my_state_do(option);
3626000Sroot }
3636000Sroot 
36432377Sminshall void
36537226Sminshall wontoption(option)
36637226Sminshall 	int option;
3676000Sroot {
3686000Sroot 	char *fmt;
3696000Sroot 
37038689Sborman 	if (do_dont_resp[option]) {
37138689Sborman 	    --do_dont_resp[option];
37238689Sborman 	    if (do_dont_resp[option] && my_state_is_dont(option))
37338689Sborman 		--do_dont_resp[option];
37438689Sborman 	}
37537226Sminshall 
37638689Sborman 	if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
3776000Sroot 
37838689Sborman 	    switch (option) {
37938689Sborman 
38038689Sborman #ifdef	KLUDGELINEMODE
38138689Sborman 	    case TELOPT_SGA:
38238689Sborman 		if (!kludgelinemode)
38338689Sborman 		    break;
38438689Sborman 		/* FALL THROUGH */
38538689Sborman #endif
38638689Sborman 	    case TELOPT_ECHO:
38727110Sminshall 		settimer(modenegotiated);
3886000Sroot 		break;
3896000Sroot 
39038689Sborman 	    case TELOPT_TM:
39138689Sborman 		if (flushout)
39238689Sborman 		    flushout = 0;
39338689Sborman 		set_my_want_state_dont(option);
39438689Sborman 		set_my_state_dont(option);
39527110Sminshall 		return;		/* Never reply to TM will's/wont's */
39627110Sminshall 
39738689Sborman 	    default:
39838689Sborman 		break;
39938689Sborman 	    }
40038689Sborman 	    set_my_want_state_dont(option);
40138689Sborman 	    send_dont(option, 0);
40238689Sborman 	    setconnmode(0);			/* Set new tty mode */
40338689Sborman 	} else if (option == TELOPT_TM) {
40438689Sborman 	    /*
40538689Sborman 	     * Special case for TM.
40638689Sborman 	     */
40738689Sborman 	    if (flushout)
40838689Sborman 		flushout = 0;
40938689Sborman 	    set_my_want_state_dont(option);
4106000Sroot 	}
41138689Sborman 	set_my_state_dont(option);
4126000Sroot }
4136000Sroot 
41432377Sminshall static void
4156000Sroot dooption(option)
4166000Sroot 	int option;
4176000Sroot {
4186000Sroot 	char *fmt;
41938689Sborman 	int new_state_ok = 0;
4206000Sroot 
42138689Sborman 	if (will_wont_resp[option]) {
42238689Sborman 	    --will_wont_resp[option];
42338689Sborman 	    if (will_wont_resp[option] && my_state_is_will(option))
42438689Sborman 		--will_wont_resp[option];
42538689Sborman 	}
42637226Sminshall 
42738689Sborman 	if (will_wont_resp[option] == 0) {
42838689Sborman 	  if (my_want_state_is_wont(option)) {
4296000Sroot 
43038689Sborman 	    switch (option) {
43138689Sborman 
43238689Sborman 	    case TELOPT_TM:
43338689Sborman 		/*
43438689Sborman 		 * Special case for TM.  We send a WILL, but pretend
43538689Sborman 		 * we sent WONT.
43638689Sborman 		 */
43738689Sborman 		send_will(option, 0);
43838689Sborman 		set_my_want_state_wont(TELOPT_TM);
43938689Sborman 		set_my_state_wont(TELOPT_TM);
44038689Sborman 		return;
44138689Sborman 
44232377Sminshall #	if defined(TN3270)
44338689Sborman 	    case TELOPT_EOR:		/* end of record */
44438908Sborman #	endif	/* defined(TN3270) */
44538689Sborman 	    case TELOPT_BINARY:		/* binary mode */
44638689Sborman 	    case TELOPT_NAWS:		/* window size */
44738689Sborman 	    case TELOPT_TSPEED:		/* terminal speed */
44838689Sborman 	    case TELOPT_LFLOW:		/* local flow control */
44938689Sborman 	    case TELOPT_TTYPE:		/* terminal type option */
45038689Sborman 	    case TELOPT_SGA:		/* no big deal */
45138689Sborman 		new_state_ok = 1;
4526000Sroot 		break;
4536000Sroot 
45438689Sborman 	    case TELOPT_LINEMODE:
45538689Sborman #ifdef	KLUDGELINEMODE
45638689Sborman 		kludgelinemode = 0;
45738689Sborman #endif
45838689Sborman 		set_my_want_state_will(TELOPT_LINEMODE);
45938689Sborman 		send_will(option, 0);
46038689Sborman 		set_my_state_will(TELOPT_LINEMODE);
46138689Sborman 		slc_init();
46238689Sborman 		return;
46338689Sborman 
46438689Sborman 	    case TELOPT_ECHO:		/* We're never going to echo... */
46538689Sborman 	    default:
4666000Sroot 		break;
46738689Sborman 	    }
46838689Sborman 
46938689Sborman 	    if (new_state_ok) {
47038689Sborman 		set_my_want_state_will(option);
47138689Sborman 		send_will(option, 0);
47238689Sborman 	    } else {
47338689Sborman 		will_wont_resp[option]++;
47438689Sborman 		send_wont(option, 0);
47538689Sborman 	    }
47638689Sborman 	  } else {
47738689Sborman 	    /*
47838689Sborman 	     * Handle options that need more things done after the
47938689Sborman 	     * other side has acknowledged the option.
48038689Sborman 	     */
48138689Sborman 	    switch (option) {
48238689Sborman 	    case TELOPT_LINEMODE:
48338689Sborman #ifdef	KLUDGELINEMODE
48438689Sborman 		kludgelinemode = 0;
48538689Sborman #endif
48638689Sborman 		set_my_state_will(option);
48738689Sborman 		slc_init();
48838689Sborman 		return;
48938689Sborman 	    }
49038689Sborman 	  }
4916000Sroot 	}
49238689Sborman 	set_my_state_will(option);
4936000Sroot }
49427676Sminshall 
49538689Sborman static void
49638689Sborman dontoption(option)
49738689Sborman 	int option;
49838689Sborman {
49938689Sborman 
50038689Sborman 	if (will_wont_resp[option]) {
50138689Sborman 	    --will_wont_resp[option];
50238689Sborman 	    if (will_wont_resp[option] && my_state_is_wont(option))
50338689Sborman 		--will_wont_resp[option];
50438689Sborman 	}
50538689Sborman 
50638689Sborman 	if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
50738811Sborman 	    switch (option) {
50838811Sborman 	    case TELOPT_LINEMODE:
50938811Sborman 		linemode = 0;	/* put us back to the default state */
51038811Sborman 		break;
51138811Sborman 	    }
51238689Sborman 	    /* we always accept a DONT */
51338689Sborman 	    set_my_want_state_wont(option);
51438689Sborman 	    send_wont(option, 0);
515*39529Sborman 	    setconnmode(0);			/* Set new tty mode */
51638689Sborman 	}
51738689Sborman 	set_my_state_wont(option);
51838689Sborman }
51938689Sborman 
52027676Sminshall /*
52138908Sborman  * Given a buffer returned by tgetent(), this routine will turn
52238908Sborman  * the pipe seperated list of names in the buffer into an array
52338908Sborman  * of pointers to null terminated names.  We toss out any bad,
52438908Sborman  * duplicate, or verbose names (names with spaces).
52538908Sborman  */
52638908Sborman 
52738908Sborman static char *unknown[] = { "UNKNOWN", 0 };
52838908Sborman 
52938908Sborman char **
53038908Sborman mklist(buf, name)
53138908Sborman char *buf, *name;
53238908Sborman {
53338908Sborman 	register int n;
53438908Sborman 	register char c, *cp, **argvp, *cp2, **argv;
53538908Sborman 	char *malloc();
53638908Sborman 
53738908Sborman 	if (name) {
53838908Sborman 		if (strlen(name) > 40)
53938908Sborman 			name = 0;
54038908Sborman 		else {
54138908Sborman 			unknown[0] = name;
54238908Sborman 			upcase(name);
54338908Sborman 		}
54438908Sborman 	}
54538908Sborman 	/*
54638908Sborman 	 * Count up the number of names.
54738908Sborman 	 */
54838908Sborman 	for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
54938908Sborman 		if (*cp == '|')
55038908Sborman 			n++;
55138908Sborman 	}
55238908Sborman 	/*
55338908Sborman 	 * Allocate an array to put the name pointers into
55438908Sborman 	 */
55538908Sborman 	argv = (char **)malloc((n+3)*sizeof(char *));
55638908Sborman 	if (argv == 0)
55738908Sborman 		return(unknown);
55838908Sborman 
55938908Sborman 	/*
56038908Sborman 	 * Fill up the array of pointers to names.
56138908Sborman 	 */
56238908Sborman 	*argv = 0;
56338908Sborman 	argvp = argv+1;
56438908Sborman 	n = 0;
56538908Sborman 	for (cp = cp2 = buf; (c = *cp);  cp++) {
56638908Sborman 		if (c == '|' || c == ':') {
56738908Sborman 			*cp++ = '\0';
56838908Sborman 			/*
56938908Sborman 			 * Skip entries that have spaces or are over 40
57038908Sborman 			 * characters long.  If this is our environment
57138908Sborman 			 * name, then put it up front.  Otherwise, as
57238908Sborman 			 * long as this is not a duplicate name (case
57338908Sborman 			 * insensitive) add it to the list.
57438908Sborman 			 */
57538908Sborman 			if (n || (cp - cp2 > 41))
57638908Sborman 				;
57738908Sborman 			else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
57838908Sborman 				*argv = cp2;
57938908Sborman 			else if (is_unique(cp2, argv+1, argvp))
58038908Sborman 				*argvp++ = cp2;
58138908Sborman 			if (c == ':')
58238908Sborman 				break;
58338908Sborman 			/*
58438908Sborman 			 * Skip multiple delimiters. Reset cp2 to
58538908Sborman 			 * the beginning of the next name. Reset n,
58638908Sborman 			 * the flag for names with spaces.
58738908Sborman 			 */
58838908Sborman 			while ((c = *cp) == '|')
58938908Sborman 				cp++;
59038908Sborman 			cp2 = cp;
59138908Sborman 			n = 0;
59238908Sborman 		}
59338908Sborman 		/*
59438908Sborman 		 * Skip entries with spaces or non-ascii values.
59538908Sborman 		 * Convert lower case letters to upper case.
59638908Sborman 		 */
59738908Sborman 		if ((c == ' ') || !isascii(c))
59838908Sborman 			n = 1;
59938908Sborman 		else if (islower(c))
60038908Sborman 			*cp = toupper(c);
60138908Sborman 	}
60238908Sborman 
60338908Sborman 	/*
60438908Sborman 	 * Check for an old V6 2 character name.  If the second
60538908Sborman 	 * name points to the beginning of the buffer, and is
60638908Sborman 	 * only 2 characters long, move it to the end of the array.
60738908Sborman 	 */
60838908Sborman 	if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
60938908Sborman 		*argvp++ = buf;
61038908Sborman 		cp = *argv++;
61138908Sborman 		*argv = cp;
61238908Sborman 	}
61338908Sborman 
61438908Sborman 	/*
61538908Sborman 	 * Duplicate last name, for TTYPE option, and null
61638908Sborman 	 * terminate the array.  If we didn't find a match on
61738908Sborman 	 * our terminal name, put that name at the beginning.
61838908Sborman 	 */
61938908Sborman 	cp = *(argvp-1);
62038908Sborman 	*argvp++ = cp;
62138908Sborman 	*argvp = 0;
62238908Sborman 
62338908Sborman 	if (*argv == 0) {
62438908Sborman 		if (name)
62538908Sborman 			*argv = name;
62638908Sborman 		else
62738908Sborman 			argv++;
62838908Sborman 	}
62938908Sborman 	if (*argv)
63038908Sborman 		return(argv);
63138908Sborman 	else
63238908Sborman 		return(unknown);
63338908Sborman }
63438908Sborman 
63538908Sborman is_unique(name, as, ae)
63638908Sborman register char *name, **as, **ae;
63738908Sborman {
63838908Sborman 	register char **ap;
63938908Sborman 	register int n;
64038908Sborman 
64138908Sborman 	n = strlen(name) + 1;
64238908Sborman 	for (ap = as; ap < ae; ap++)
64338908Sborman 		if (strncasecmp(*ap, name, n) == 0)
64438908Sborman 			return(0);
64538908Sborman 	return (1);
64638908Sborman }
64738908Sborman 
64838908Sborman #ifdef	TERMCAP
649*39529Sborman char termbuf[1024];
65038908Sborman setupterm(tname, fd, errp)
65138908Sborman char *tname;
65238908Sborman int fd, *errp;
65338908Sborman {
654*39529Sborman 	if (tgetent(termbuf, tname) == 1) {
655*39529Sborman 		termbuf[1023] = '\0';
65638908Sborman 		if (errp)
65738908Sborman 			*errp = 1;
65838908Sborman 		return(0);
65938908Sborman 	}
66038908Sborman 	if (errp)
66138908Sborman 		*errp = 0;
66238908Sborman 	return(-1);
66338908Sborman }
664*39529Sborman #else
665*39529Sborman #define	termbuf	ttytype
666*39529Sborman extern char ttytype[];
66738908Sborman #endif
66838908Sborman 
66938908Sborman char *
67038908Sborman gettermname()
67138908Sborman {
67238908Sborman 	char *tname;
67338908Sborman 	static int first = 1;
67438908Sborman 	static char **tnamep;
67538908Sborman 	static char **next;
67638908Sborman 	char *getenv();
67738908Sborman 	int err;
67838908Sborman 
67938908Sborman 	if (first) {
68038908Sborman 		first = 0;
68138908Sborman 		if ((tname = getenv("TERM")) &&
68238908Sborman 				(setupterm(tname, 1, &err) == 0)) {
683*39529Sborman 			tnamep = mklist(termbuf, tname);
68438908Sborman 		} else {
68538908Sborman 			if (tname && (strlen(tname) <= 40)) {
68638908Sborman 				unknown[0] = tname;
68738908Sborman 				upcase(tname);
68838908Sborman 			}
68938908Sborman 			tnamep = unknown;
69038908Sborman 		}
69138908Sborman 		next = tnamep;
69238908Sborman 	}
69338908Sborman 	if (*next == 0)
69438908Sborman 		next = tnamep;
69538908Sborman 	return(*next++);
69638908Sborman }
69738908Sborman /*
69827676Sminshall  * suboption()
69927676Sminshall  *
70027676Sminshall  *	Look at the sub-option buffer, and try to be helpful to the other
70127676Sminshall  * side.
70227676Sminshall  *
70327676Sminshall  *	Currently we recognize:
70427676Sminshall  *
70527676Sminshall  *		Terminal type, send request.
70637219Sminshall  *		Terminal speed (send request).
70737219Sminshall  *		Local flow control (is request).
70838689Sborman  *		Linemode
70927676Sminshall  */
71027676Sminshall 
71132377Sminshall static void
71227676Sminshall suboption()
71327676Sminshall {
71438689Sborman     printsub('<', subbuffer, subend-subbuffer+2);
71527676Sminshall     switch (subbuffer[0]&0xff) {
71627676Sminshall     case TELOPT_TTYPE:
71738689Sborman 	if (my_want_state_is_wont(TELOPT_TTYPE))
71838689Sborman 	    return;
71927676Sminshall 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
72027676Sminshall 	    ;
72127676Sminshall 	} else {
72227676Sminshall 	    char *name;
72332377Sminshall 	    extern char *getenv();
72438908Sborman 	    char temp[50];
72527676Sminshall 	    int len;
72627676Sminshall 
72732377Sminshall #if	defined(TN3270)
72832531Sminshall 	    if (tn3270_ttype()) {
72932377Sminshall 		return;
73032377Sminshall 	    }
73132377Sminshall #endif	/* defined(TN3270) */
73238908Sborman 	    name = gettermname();
73338908Sborman 	    len = strlen(name) + 4 + 2;
73438908Sborman 	    if (len < NETROOM()) {
73538908Sborman 		sprintf(temp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
73638908Sborman 				TELQUAL_IS, name, IAC, SE);
73738689Sborman 		ring_supply_data(&netoring, temp, len);
73838908Sborman 		printsub('>', &temp[2], len-2);
73932377Sminshall 	    } else {
74037226Sminshall 		ExitString("No room in buffer for terminal type.\n", 1);
74132377Sminshall 		/*NOTREACHED*/
74227676Sminshall 	    }
74327676Sminshall 	}
74437219Sminshall 	break;
74537219Sminshall     case TELOPT_TSPEED:
74638689Sborman 	if (my_want_state_is_wont(TELOPT_TSPEED))
74738689Sborman 	    return;
74837219Sminshall 	if ((subbuffer[1]&0xff) == TELQUAL_SEND) {
74938689Sborman 	    long ospeed,ispeed;
75038689Sborman 	    char temp[50];
75137219Sminshall 	    int len;
75227676Sminshall 
75337219Sminshall 	    TerminalSpeeds(&ispeed, &ospeed);
75437219Sminshall 
75538689Sborman 	    sprintf(temp, "%c%c%c%c%d,%d%c%c", IAC, SB, TELOPT_TSPEED,
75638689Sborman 		    TELQUAL_IS, ospeed, ispeed, IAC, SE);
75738689Sborman 	    len = strlen(temp+4) + 4;	/* temp[3] is 0 ... */
75837219Sminshall 
75938689Sborman 	    if (len < NETROOM()) {
76038689Sborman 		ring_supply_data(&netoring, temp, len);
76138689Sborman 		printsub('>', temp+2, len - 2);
76237219Sminshall 	    }
76337219Sminshall 	}
76437219Sminshall 	break;
76537219Sminshall     case TELOPT_LFLOW:
76638689Sborman 	if (my_want_state_is_wont(TELOPT_LFLOW))
76738689Sborman 	    return;
76837219Sminshall 	if ((subbuffer[1]&0xff) == 1) {
76937219Sminshall 	    localflow = 1;
77037219Sminshall 	} else if ((subbuffer[1]&0xff) == 0) {
77137219Sminshall 	    localflow = 0;
77237219Sminshall 	}
77337219Sminshall 	setcommandmode();
77438689Sborman 	setconnmode(0);
77537219Sminshall 	break;
77638689Sborman 
77738689Sborman     case TELOPT_LINEMODE:
77838689Sborman 	if (my_want_state_is_wont(TELOPT_LINEMODE))
77938689Sborman 	    return;
78038689Sborman 	switch (subbuffer[1]&0xff) {
78138689Sborman 	case WILL:
78238689Sborman 	    lm_will(&subbuffer[2], subend - &subbuffer[2]);
78338689Sborman 	    break;
78438689Sborman 	case WONT:
78538689Sborman 	    lm_wont(&subbuffer[2], subend - &subbuffer[2]);
78638689Sborman 	    break;
78738689Sborman 	case DO:
78838689Sborman 	    lm_do(&subbuffer[2], subend - &subbuffer[2]);
78938689Sborman 	    break;
79038689Sborman 	case DONT:
79138689Sborman 	    lm_dont(&subbuffer[2], subend - &subbuffer[2]);
79238689Sborman 	    break;
79338689Sborman 	case LM_SLC:
79438689Sborman 	    slc(&subbuffer[2], subend - &subbuffer[2]);
79538689Sborman 	    break;
79638689Sborman 	case LM_MODE:
79738689Sborman 	    lm_mode(&subbuffer[2], subend - &subbuffer[2], 0);
79838689Sborman 	    break;
79938689Sborman 	default:
80038689Sborman 		break;
80138689Sborman 	}
80238689Sborman 	break;
80327676Sminshall     default:
80427676Sminshall 	break;
80527676Sminshall     }
80627676Sminshall }
80738689Sborman 
80838689Sborman static char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
80938689Sborman 
81038689Sborman lm_will(cmd, len)
81138689Sborman char *cmd;
81238689Sborman {
81338689Sborman     switch(cmd[0]) {
81438689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
81538689Sborman     default:
81638689Sborman 	str_lm[3] = DONT;
81738689Sborman 	str_lm[4] = cmd[0];
81838689Sborman 	if (NETROOM() > sizeof(str_lm)) {
81938689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
82038689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
82138689Sborman 	}
82238689Sborman /*@*/	else printf("lm_will: not enough room in buffer\n");
82338689Sborman 	break;
82438689Sborman     }
82538689Sborman }
82638689Sborman 
82738689Sborman lm_wont(cmd, len)
82838689Sborman char *cmd;
82938689Sborman {
83038689Sborman     switch(cmd[0]) {
83138689Sborman     case LM_FORWARDMASK:	/* We shouldn't ever get this... */
83238689Sborman     default:
83338689Sborman 	/* We are always DONT, so don't respond */
83438689Sborman 	return;
83538689Sborman     }
83638689Sborman }
83738689Sborman 
83838689Sborman lm_do(cmd, len)
83938689Sborman char *cmd;
84038689Sborman {
84138689Sborman     switch(cmd[0]) {
84238689Sborman     case LM_FORWARDMASK:
84338689Sborman     default:
84438689Sborman 	str_lm[3] = WONT;
84538689Sborman 	str_lm[4] = cmd[0];
84638689Sborman 	if (NETROOM() > sizeof(str_lm)) {
84738689Sborman 	    ring_supply_data(&netoring, str_lm, sizeof(str_lm));
84838689Sborman 	    printsub('>', &str_lm[2], sizeof(str_lm)-2);
84938689Sborman 	}
85038689Sborman /*@*/	else printf("lm_do: not enough room in buffer\n");
85138689Sborman 	break;
85238689Sborman     }
85338689Sborman }
85438689Sborman 
85538689Sborman lm_dont(cmd, len)
85638689Sborman char *cmd;
85738689Sborman {
85838689Sborman     switch(cmd[0]) {
85938689Sborman     case LM_FORWARDMASK:
86038689Sborman     default:
86138689Sborman 	/* we are always WONT, so don't respond */
86238689Sborman 	break;
86338689Sborman     }
86438689Sborman }
86538689Sborman 
86638689Sborman static char str_lm_mode[] = { IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE };
86738689Sborman 
86838689Sborman lm_mode(cmd, len, init)
86938689Sborman char *cmd;
87038689Sborman int len, init;
87138689Sborman {
87238689Sborman 	if (len != 1)
87338689Sborman 		return;
87438689Sborman 	if ((linemode&(MODE_EDIT|MODE_TRAPSIG)) == *cmd)
87538689Sborman 		return;
87638689Sborman 	if (*cmd&MODE_ACK)
87738689Sborman 		return;
87838689Sborman 	linemode = (*cmd&(MODE_EDIT|MODE_TRAPSIG));
87938689Sborman 	str_lm_mode[4] = linemode;
88038689Sborman 	if (!init)
88138689Sborman 	    str_lm_mode[4] |= MODE_ACK;
88238689Sborman 	if (NETROOM() > sizeof(str_lm_mode)) {
88338689Sborman 	    ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
88438689Sborman 	    printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
88538689Sborman 	}
88638689Sborman /*@*/	else printf("lm_mode: not enough room in buffer\n");
88738689Sborman 	setconnmode(0);	/* set changed mode */
88838689Sborman }
88938689Sborman 
89032377Sminshall 
89127088Sminshall 
89238689Sborman /*
89338689Sborman  * slc()
89438689Sborman  * Handle special character suboption of LINEMODE.
89538689Sborman  */
89638689Sborman 
89738689Sborman struct spc {
89838689Sborman 	char val;
89938689Sborman 	char *valp;
90038689Sborman 	char flags;	/* Current flags & level */
90138689Sborman 	char mylevel;	/* Maximum level & flags */
90238689Sborman } spc_data[NSLC+1];
90338689Sborman 
90438689Sborman #define SLC_IMPORT	0
90538689Sborman #define	SLC_EXPORT	1
90638689Sborman #define SLC_RVALUE	2
90738689Sborman static int slc_mode = SLC_EXPORT;
90838689Sborman 
90938689Sborman slc_init()
91038689Sborman {
91138689Sborman 	register struct spc *spcp;
91238689Sborman 	extern char *tcval();
91338689Sborman 
91438689Sborman 	localchars = 1;
91538689Sborman 	for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
91638689Sborman 		spcp->val = 0;
91738689Sborman 		spcp->valp = 0;
91838689Sborman 		spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
91938689Sborman 	}
92038689Sborman 
92138689Sborman #define	initfunc(func, flags) { \
92238689Sborman 					spcp = &spc_data[func]; \
92338689Sborman 					if (spcp->valp = tcval(func)) { \
92438689Sborman 					    spcp->val = *spcp->valp; \
92538689Sborman 					    spcp->mylevel = SLC_VARIABLE|flags; \
92638689Sborman 					} else { \
92738689Sborman 					    spcp->val = 0; \
92838689Sborman 					    spcp->mylevel = SLC_DEFAULT; \
92938689Sborman 					} \
93038689Sborman 				    }
93138689Sborman 
93238689Sborman 	initfunc(SLC_SYNCH, 0);
93338689Sborman 	/* No BRK */
93438689Sborman 	initfunc(SLC_AO, 0);
93538689Sborman 	initfunc(SLC_AYT, 0);
93638689Sborman 	/* No EOR */
93738689Sborman 	initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
93838689Sborman 	initfunc(SLC_EOF, 0);
939*39529Sborman #ifndef	SYSV_TERMIO
94038689Sborman 	initfunc(SLC_SUSP, SLC_FLUSHIN);
94138689Sborman #endif
94238689Sborman 	initfunc(SLC_EC, 0);
94338689Sborman 	initfunc(SLC_EL, 0);
944*39529Sborman #ifndef	SYSV_TERMIO
94538689Sborman 	initfunc(SLC_EW, 0);
94638689Sborman 	initfunc(SLC_RP, 0);
94738689Sborman 	initfunc(SLC_LNEXT, 0);
94838689Sborman #endif
94938689Sborman 	initfunc(SLC_XON, 0);
95038689Sborman 	initfunc(SLC_XOFF, 0);
951*39529Sborman #ifdef	SYSV_TERMIO
95238689Sborman 	spc_data[SLC_XON].mylevel = SLC_CANTCHANGE;
95338689Sborman 	spc_data[SLC_XOFF].mylevel = SLC_CANTCHANGE;
95438689Sborman #endif
95538689Sborman 	/* No FORW1 */
95638689Sborman 	/* No FORW2 */
95738689Sborman 
95838689Sborman 	initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
95938689Sborman #undef	initfunc
96038689Sborman 
96138689Sborman 	if (slc_mode == SLC_EXPORT)
96238689Sborman 		slc_export();
96338689Sborman 	else
96438689Sborman 		slc_import(1);
96538689Sborman 
96638689Sborman }
96738689Sborman 
96838689Sborman slcstate()
96938689Sborman {
97038689Sborman     printf("Special characters are %s values\n",
97138689Sborman 		slc_mode == SLC_IMPORT ? "remote default" :
97238689Sborman 		slc_mode == SLC_EXPORT ? "local" :
97338689Sborman 					 "remote");
97438689Sborman }
97538689Sborman 
97638689Sborman slc_mode_export()
97738689Sborman {
97838689Sborman     slc_mode = SLC_EXPORT;
97938689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
98038689Sborman 	slc_export();
98138689Sborman }
98238689Sborman 
98338689Sborman slc_mode_import(def)
98438689Sborman {
98538689Sborman     slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
98638689Sborman     if (my_state_is_will(TELOPT_LINEMODE))
98738689Sborman 	slc_import(def);
98838689Sborman }
98938689Sborman 
99038689Sborman char slc_import_val[] = {
99138689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
99238689Sborman };
99338689Sborman char slc_import_def[] = {
99438689Sborman 	IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
99538689Sborman };
99638689Sborman 
99738689Sborman slc_import(def)
99838689Sborman int def;
99938689Sborman {
100038689Sborman     if (NETROOM() > sizeof(slc_import_val)) {
100138689Sborman 	if (def) {
100238689Sborman 	    ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
100338689Sborman 	    printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
100438689Sborman 	} else {
100538689Sborman 	    ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
100638689Sborman 	    printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
100738689Sborman 	}
100838689Sborman     }
100938689Sborman /*@*/ else printf("slc_import: not enough room\n");
101038689Sborman }
101138689Sborman 
101238689Sborman slc_export()
101338689Sborman {
101438689Sborman     register struct spc *spcp;
101538689Sborman 
101638689Sborman     TerminalDefaultChars();
101738689Sborman 
101838689Sborman     slc_start_reply();
101938689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
102038689Sborman 	if (spcp->mylevel != SLC_NOSUPPORT) {
102138689Sborman 	    spcp->flags = spcp->mylevel;
102238689Sborman 	    if (spcp->valp)
102338689Sborman 		spcp->val = *spcp->valp;
102438689Sborman 	    slc_add_reply(spcp - spc_data, spcp->mylevel, spcp->val);
102538689Sborman 	}
102638689Sborman     }
102738689Sborman     slc_end_reply();
102838689Sborman     if (slc_update())
102938689Sborman 	setconnmode(1);	/* set the  new character values */
103038689Sborman }
103138689Sborman 
103238689Sborman slc(cp, len)
103338689Sborman register char *cp;
103438689Sborman int len;
103538689Sborman {
103638689Sborman 	register struct spc *spcp;
103738689Sborman 	register int func,level;
103838689Sborman 
103938689Sborman 	slc_start_reply();
104038689Sborman 
104138689Sborman 	for (; len >= 3; len -=3, cp +=3) {
104238689Sborman 
104338689Sborman 		func = cp[SLC_FUNC];
104438689Sborman 
104538689Sborman 		if (func == 0) {
104638689Sborman 			/*
104738689Sborman 			 * Client side: always ignore 0 function.
104838689Sborman 			 */
104938689Sborman 			continue;
105038689Sborman 		}
105138689Sborman 		if (func > NSLC) {
105238689Sborman 			if (cp[SLC_FLAGS] & SLC_LEVELBITS != SLC_NOSUPPORT)
105338689Sborman 				slc_add_reply(func, SLC_NOSUPPORT, 0);
105438689Sborman 			continue;
105538689Sborman 		}
105638689Sborman 
105738689Sborman 		spcp = &spc_data[func];
105838689Sborman 
105938689Sborman 		level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
106038689Sborman 
106138689Sborman 		if ((cp[SLC_VALUE] == spcp->val) &&
106238689Sborman 		    ((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
106338689Sborman 			continue;
106438689Sborman 		}
106538689Sborman 
106638689Sborman 		if (level == (SLC_DEFAULT|SLC_ACK)) {
106738689Sborman 			/*
106838689Sborman 			 * This is an error condition, the SLC_ACK
106938689Sborman 			 * bit should never be set for the SLC_DEFAULT
107038689Sborman 			 * level.  Our best guess to recover is to
107138689Sborman 			 * ignore the SLC_ACK bit.
107238689Sborman 			 */
107338689Sborman 			cp[SLC_FLAGS] &= ~SLC_ACK;
107438689Sborman 		}
107538689Sborman 
107638689Sborman 		if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
107738689Sborman 			spcp->val = cp[SLC_VALUE];
107838689Sborman 			spcp->flags = cp[SLC_FLAGS];	/* include SLC_ACK */
107938689Sborman 			continue;
108038689Sborman 		}
108138689Sborman 
108238689Sborman 		level &= ~SLC_ACK;
108338689Sborman 
108438689Sborman 		if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
108538689Sborman 			spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
108638689Sborman 			spcp->val = cp[SLC_VALUE];
108738689Sborman 		}
108838689Sborman 		if (level == SLC_DEFAULT) {
108938689Sborman 			if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
109038689Sborman 				spcp->flags = spcp->mylevel;
109138689Sborman 			else
109238689Sborman 				spcp->flags = SLC_NOSUPPORT;
109338689Sborman 		}
109438689Sborman 		slc_add_reply(func, spcp->flags, spcp->val);
109538689Sborman 	}
109638689Sborman 	slc_end_reply();
109738689Sborman 	if (slc_update())
109838689Sborman 		setconnmode(1);	/* set the  new character values */
109938689Sborman }
110038689Sborman 
110138689Sborman slc_check()
110238689Sborman {
110338689Sborman     register struct spc *spcp;
110438689Sborman 
110538689Sborman     slc_start_reply();
110638689Sborman     for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
110738689Sborman 	if (spcp->valp && spcp->val != *spcp->valp) {
110838689Sborman 	    spcp->val = *spcp->valp;
110938689Sborman 	    slc_add_reply(spcp - spc_data, spcp->mylevel, spcp->val);
111038689Sborman 	}
111138689Sborman     }
111238689Sborman     slc_end_reply();
111338689Sborman     setconnmode(1);
111438689Sborman }
111538689Sborman 
111638689Sborman 
111738689Sborman unsigned char slc_reply[128];
111838689Sborman unsigned char *slc_replyp;
111938689Sborman slc_start_reply()
112038689Sborman {
112138689Sborman 	slc_replyp = slc_reply;
112238689Sborman 	*slc_replyp++ = IAC;
112338689Sborman 	*slc_replyp++ = SB;
112438689Sborman 	*slc_replyp++ = TELOPT_LINEMODE;
112538689Sborman 	*slc_replyp++ = LM_SLC;
112638689Sborman }
112738689Sborman 
112838689Sborman slc_add_reply(func, flags, value)
112938689Sborman char func;
113038689Sborman char flags;
113138689Sborman char value;
113238689Sborman {
113338689Sborman 	if ((*slc_replyp++ = func) == IAC)
113438689Sborman 		*slc_replyp++ = IAC;
113538689Sborman 	if ((*slc_replyp++ = flags) == IAC)
113638689Sborman 		*slc_replyp++ = IAC;
113738689Sborman 	if ((*slc_replyp++ = value) == IAC)
113838689Sborman 		*slc_replyp++ = IAC;
113938689Sborman }
114038689Sborman 
114138689Sborman slc_end_reply()
114238689Sborman {
114338689Sborman     register char *cp;
114438689Sborman     register int len;
114538689Sborman 
114638689Sborman     *slc_replyp++ = IAC;
114738689Sborman     *slc_replyp++ = SE;
114838689Sborman     len = slc_replyp - slc_reply;
114938689Sborman     if (len <= 6)
115038689Sborman 	return;
115138689Sborman     if (NETROOM() > len) {
115238689Sborman 	ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
115338689Sborman 	printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
115438689Sborman     }
115538689Sborman /*@*/else printf("slc_end_reply: not enough room\n");
115638689Sborman }
115738689Sborman 
115838689Sborman slc_update()
115938689Sborman {
116038689Sborman 	register struct spc *spcp;
116138689Sborman 	int need_update = 0;
116238689Sborman 
116338689Sborman 	for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
116438689Sborman 		if (!(spcp->flags&SLC_ACK))
116538689Sborman 			continue;
116638689Sborman 		spcp->flags &= ~SLC_ACK;
116738689Sborman 		if (spcp->valp && (*spcp->valp != spcp->val)) {
116838689Sborman 			*spcp->valp = spcp->val;
116938689Sborman 			need_update = 1;
117038689Sborman 		}
117138689Sborman 	}
117238689Sborman 	return(need_update);
117338689Sborman }
117438689Sborman 
117538689Sborman 
117638689Sborman 
117733804Sminshall int
117832377Sminshall telrcv()
117927110Sminshall {
118032377Sminshall     register int c;
118132385Sminshall     register int scc;
118232385Sminshall     register char *sbp;
118332385Sminshall     int count;
118432385Sminshall     int returnValue = 0;
118527088Sminshall 
118632385Sminshall     scc = 0;
118732385Sminshall     count = 0;
118832385Sminshall     while (TTYROOM() > 2) {
118932385Sminshall 	if (scc == 0) {
119032385Sminshall 	    if (count) {
119132528Sminshall 		ring_consumed(&netiring, count);
119232385Sminshall 		returnValue = 1;
119332385Sminshall 		count = 0;
119432385Sminshall 	    }
119532528Sminshall 	    sbp = netiring.consume;
119632528Sminshall 	    scc = ring_full_consecutive(&netiring);
119732385Sminshall 	    if (scc == 0) {
119832385Sminshall 		/* No more data coming in */
119932385Sminshall 		break;
120032385Sminshall 	    }
120132385Sminshall 	}
120232385Sminshall 
120332385Sminshall 	c = *sbp++ & 0xff, scc--; count++;
120432385Sminshall 
120532377Sminshall 	switch (telrcv_state) {
120627110Sminshall 
120732377Sminshall 	case TS_CR:
120832377Sminshall 	    telrcv_state = TS_DATA;
120935518Sminshall 	    if (c == '\0') {
121035518Sminshall 		break;	/* Ignore \0 after CR */
1211*39529Sborman 	    }
1212*39529Sborman 	    else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
121335518Sminshall 		TTYADD(c);
121435518Sminshall 		break;
121532377Sminshall 	    }
121635518Sminshall 	    /* Else, fall through */
121727088Sminshall 
121832377Sminshall 	case TS_DATA:
121932377Sminshall 	    if (c == IAC) {
122032377Sminshall 		telrcv_state = TS_IAC;
122133804Sminshall 		break;
122232377Sminshall 	    }
122332377Sminshall #	    if defined(TN3270)
122432377Sminshall 	    if (In3270) {
122532377Sminshall 		*Ifrontp++ = c;
122632385Sminshall 		while (scc > 0) {
122732385Sminshall 		    c = *sbp++ & 0377, scc--; count++;
122832377Sminshall 		    if (c == IAC) {
122932377Sminshall 			telrcv_state = TS_IAC;
123034304Sminshall 			break;
123132377Sminshall 		    }
123232377Sminshall 		    *Ifrontp++ = c;
123332377Sminshall 		}
123432377Sminshall 	    } else
123532377Sminshall #	    endif /* defined(TN3270) */
123635518Sminshall 		    /*
123735518Sminshall 		     * The 'crmod' hack (see following) is needed
123835518Sminshall 		     * since we can't * set CRMOD on output only.
123935518Sminshall 		     * Machines like MULTICS like to send \r without
124035518Sminshall 		     * \n; since we must turn off CRMOD to get proper
124135518Sminshall 		     * input, the mapping is done here (sigh).
124235518Sminshall 		     */
124338689Sborman 	    if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
124435518Sminshall 		if (scc > 0) {
124535518Sminshall 		    c = *sbp&0xff;
124635518Sminshall 		    if (c == 0) {
124735518Sminshall 			sbp++, scc--; count++;
124835518Sminshall 			/* a "true" CR */
124932377Sminshall 			TTYADD('\r');
125038689Sborman 		    } else if (my_want_state_is_dont(TELOPT_ECHO) &&
125135518Sminshall 					(c == '\n')) {
125235518Sminshall 			sbp++, scc--; count++;
125332377Sminshall 			TTYADD('\n');
125435518Sminshall 		    } else {
125535518Sminshall 			TTYADD('\r');
125635518Sminshall 			if (crmod) {
125735518Sminshall 				TTYADD('\n');
125832377Sminshall 			}
125932377Sminshall 		    }
126035518Sminshall 		} else {
126135518Sminshall 		    telrcv_state = TS_CR;
126235518Sminshall 		    TTYADD('\r');
126335518Sminshall 		    if (crmod) {
126435518Sminshall 			    TTYADD('\n');
126535518Sminshall 		    }
126632377Sminshall 		}
126732377Sminshall 	    } else {
126832377Sminshall 		TTYADD(c);
126932377Sminshall 	    }
127032377Sminshall 	    continue;
127127088Sminshall 
127232377Sminshall 	case TS_IAC:
127338689Sborman process_iac:
127432377Sminshall 	    switch (c) {
127532377Sminshall 
127632377Sminshall 	    case WILL:
127732377Sminshall 		telrcv_state = TS_WILL;
127832377Sminshall 		continue;
127927261Sminshall 
128032377Sminshall 	    case WONT:
128132377Sminshall 		telrcv_state = TS_WONT;
128232377Sminshall 		continue;
128327261Sminshall 
128432377Sminshall 	    case DO:
128532377Sminshall 		telrcv_state = TS_DO;
128632377Sminshall 		continue;
128727261Sminshall 
128832377Sminshall 	    case DONT:
128932377Sminshall 		telrcv_state = TS_DONT;
129032377Sminshall 		continue;
129127261Sminshall 
129232377Sminshall 	    case DM:
129332377Sminshall 		    /*
129432377Sminshall 		     * We may have missed an urgent notification,
129532377Sminshall 		     * so make sure we flush whatever is in the
129632377Sminshall 		     * buffer currently.
129732377Sminshall 		     */
129832377Sminshall 		SYNCHing = 1;
129932377Sminshall 		ttyflush(1);
130032554Sminshall 		SYNCHing = stilloob();
130132377Sminshall 		settimer(gotDM);
130232377Sminshall 		break;
130327088Sminshall 
130432377Sminshall 	    case NOP:
130532377Sminshall 	    case GA:
130632377Sminshall 		break;
130727088Sminshall 
130832377Sminshall 	    case SB:
130932377Sminshall 		SB_CLEAR();
131032377Sminshall 		telrcv_state = TS_SB;
131138689Sborman 		printoption("RCVD", "IAC", SB);
131232377Sminshall 		continue;
131327261Sminshall 
131432377Sminshall #	    if defined(TN3270)
131532377Sminshall 	    case EOR:
131632377Sminshall 		if (In3270) {
131732377Sminshall 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
131832377Sminshall 		    if (Ibackp == Ifrontp) {
131932377Sminshall 			Ibackp = Ifrontp = Ibuf;
132032377Sminshall 			ISend = 0;	/* should have been! */
132132377Sminshall 		    } else {
132232377Sminshall 			ISend = 1;
132327088Sminshall 		    }
132427088Sminshall 		}
132527088Sminshall 		break;
132632377Sminshall #	    endif /* defined(TN3270) */
132732377Sminshall 
132832377Sminshall 	    case IAC:
132932377Sminshall #	    if !defined(TN3270)
133032377Sminshall 		TTYADD(IAC);
133132377Sminshall #	    else /* !defined(TN3270) */
133232377Sminshall 		if (In3270) {
133332377Sminshall 		    *Ifrontp++ = IAC;
133432377Sminshall 		} else {
133532377Sminshall 		    TTYADD(IAC);
133632377Sminshall 		}
133732377Sminshall #	    endif /* !defined(TN3270) */
133827088Sminshall 		break;
133932377Sminshall 
134027088Sminshall 	    default:
134127088Sminshall 		break;
134227088Sminshall 	    }
134332377Sminshall 	    telrcv_state = TS_DATA;
134432377Sminshall 	    continue;
134527088Sminshall 
134632377Sminshall 	case TS_WILL:
134737226Sminshall 	    printoption("RCVD", "will", c);
134838689Sborman 	    willoption(c);
134932377Sminshall 	    SetIn3270();
135032377Sminshall 	    telrcv_state = TS_DATA;
135132377Sminshall 	    continue;
135227110Sminshall 
135332377Sminshall 	case TS_WONT:
135437226Sminshall 	    printoption("RCVD", "wont", c);
135538689Sborman 	    wontoption(c);
135632377Sminshall 	    SetIn3270();
135732377Sminshall 	    telrcv_state = TS_DATA;
135832377Sminshall 	    continue;
135927088Sminshall 
136032377Sminshall 	case TS_DO:
136137226Sminshall 	    printoption("RCVD", "do", c);
136237226Sminshall 	    dooption(c);
136332377Sminshall 	    SetIn3270();
136437219Sminshall 	    if (c == TELOPT_NAWS) {
136537219Sminshall 		sendnaws();
136637219Sminshall 	    } else if (c == TELOPT_LFLOW) {
136737219Sminshall 		localflow = 1;
136837219Sminshall 		setcommandmode();
136938689Sborman 		setconnmode(0);
137037219Sminshall 	    }
137132377Sminshall 	    telrcv_state = TS_DATA;
137232377Sminshall 	    continue;
137327088Sminshall 
137432377Sminshall 	case TS_DONT:
137537226Sminshall 	    printoption("RCVD", "dont", c);
137638689Sborman 	    dontoption(c);
137737226Sminshall 	    flushline = 1;
137838689Sborman 	    setconnmode(0);	/* set new tty mode (maybe) */
137932377Sminshall 	    SetIn3270();
138032377Sminshall 	    telrcv_state = TS_DATA;
138132377Sminshall 	    continue;
138227088Sminshall 
138332377Sminshall 	case TS_SB:
138432377Sminshall 	    if (c == IAC) {
138532377Sminshall 		telrcv_state = TS_SE;
138632377Sminshall 	    } else {
138732377Sminshall 		SB_ACCUM(c);
138832377Sminshall 	    }
138932377Sminshall 	    continue;
139027088Sminshall 
139132377Sminshall 	case TS_SE:
139232377Sminshall 	    if (c != SE) {
139332377Sminshall 		if (c != IAC) {
139438689Sborman 		    /*
139538689Sborman 		     * This is an error.  We only expect to get
139638689Sborman 		     * "IAC IAC" or "IAC SE".  Several things may
139738689Sborman 		     * have happend.  An IAC was not doubled, the
139838689Sborman 		     * IAC SE was left off, or another option got
139938689Sborman 		     * inserted into the suboption are all possibilities.
140038689Sborman 		     * If we assume that the IAC was not doubled,
140138689Sborman 		     * and really the IAC SE was left off, we could
140238689Sborman 		     * get into an infinate loop here.  So, instead,
140338689Sborman 		     * we terminate the suboption, and process the
140438689Sborman 		     * partial suboption if we can.
140538689Sborman 		     */
140638689Sborman 		    SB_TERM();
140732377Sminshall 		    SB_ACCUM(IAC);
140838689Sborman 		    SB_ACCUM(c);
140938689Sborman 		    printoption("In SUBOPTION processing, RCVD", "IAC", c);
141038689Sborman 		    suboption();	/* handle sub-option */
141138689Sborman 		    SetIn3270();
141238689Sborman 		    telrcv_state = TS_IAC;
141338689Sborman 		    goto process_iac;
141432377Sminshall 		}
141532377Sminshall 		SB_ACCUM(c);
141632377Sminshall 		telrcv_state = TS_SB;
141732377Sminshall 	    } else {
141832377Sminshall 		SB_TERM();
141938689Sborman 		SB_ACCUM(IAC);
142038689Sborman 		SB_ACCUM(SE);
142132377Sminshall 		suboption();	/* handle sub-option */
142232377Sminshall 		SetIn3270();
142332377Sminshall 		telrcv_state = TS_DATA;
142432377Sminshall 	    }
142527088Sminshall 	}
142627088Sminshall     }
142732667Sminshall     if (count)
142832667Sminshall 	ring_consumed(&netiring, count);
142932385Sminshall     return returnValue||count;
143027088Sminshall }
143132385Sminshall 
143232385Sminshall static int
143332554Sminshall telsnd()
143432385Sminshall {
143532385Sminshall     int tcc;
143632385Sminshall     int count;
143732385Sminshall     int returnValue = 0;
143832385Sminshall     char *tbp;
143932385Sminshall 
144032385Sminshall     tcc = 0;
144132385Sminshall     count = 0;
144232385Sminshall     while (NETROOM() > 2) {
144332385Sminshall 	register int sc;
144432385Sminshall 	register int c;
144532385Sminshall 
144632385Sminshall 	if (tcc == 0) {
144732385Sminshall 	    if (count) {
144832528Sminshall 		ring_consumed(&ttyiring, count);
144932385Sminshall 		returnValue = 1;
145032385Sminshall 		count = 0;
145132385Sminshall 	    }
145232528Sminshall 	    tbp = ttyiring.consume;
145332528Sminshall 	    tcc = ring_full_consecutive(&ttyiring);
145432385Sminshall 	    if (tcc == 0) {
145532385Sminshall 		break;
145632385Sminshall 	    }
145732385Sminshall 	}
145832385Sminshall 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
145932385Sminshall 	if (sc == escape) {
146038689Sborman 	    /*
146138689Sborman 	     * Double escape is a pass through of a single escape character.
146238689Sborman 	     */
146338689Sborman 	    if (tcc && strip(*tbp) == escape) {
146438689Sborman 		tbp++;
146538689Sborman 		tcc--;
146638689Sborman 		count++;
146738689Sborman 	    } else {
146838689Sborman 		command(0, tbp, tcc);
146938689Sborman 		count += tcc;
147038689Sborman 		tcc = 0;
147138689Sborman 		flushline = 1;
147238689Sborman 		break;
147338689Sborman 	    }
147438689Sborman 	}
147538689Sborman #ifdef	KLUDGELINEMODE
147638689Sborman 	if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
147732385Sminshall 	    if (tcc > 0 && strip(*tbp) == echoc) {
147832385Sminshall 		tcc--; tbp++; count++;
147932385Sminshall 	    } else {
148032385Sminshall 		dontlecho = !dontlecho;
148132385Sminshall 		settimer(echotoggle);
148238689Sborman 		setconnmode(0);
148332385Sminshall 		flushline = 1;
148432385Sminshall 		break;
148532385Sminshall 	    }
148632385Sminshall 	}
148738689Sborman #endif
148838689Sborman 	if (MODE_LOCAL_CHARS(globalmode)) {
148932385Sminshall 	    if (TerminalSpecialChars(sc) == 0) {
149032385Sminshall 		break;
149132385Sminshall 	    }
149232385Sminshall 	}
149338689Sborman 	if (my_want_state_is_wont(TELOPT_BINARY)) {
149432385Sminshall 	    switch (c) {
149532385Sminshall 	    case '\n':
149632385Sminshall 		    /*
149732385Sminshall 		     * If we are in CRMOD mode (\r ==> \n)
149832385Sminshall 		     * on our local machine, then probably
149932385Sminshall 		     * a newline (unix) is CRLF (TELNET).
150032385Sminshall 		     */
150132385Sminshall 		if (MODE_LOCAL_CHARS(globalmode)) {
150232385Sminshall 		    NETADD('\r');
150332385Sminshall 		}
150432385Sminshall 		NETADD('\n');
150532385Sminshall 		flushline = 1;
150632385Sminshall 		break;
150732385Sminshall 	    case '\r':
150832385Sminshall 		if (!crlf) {
150932385Sminshall 		    NET2ADD('\r', '\0');
151032385Sminshall 		} else {
151132385Sminshall 		    NET2ADD('\r', '\n');
151232385Sminshall 		}
151332385Sminshall 		flushline = 1;
151432385Sminshall 		break;
151532385Sminshall 	    case IAC:
151632385Sminshall 		NET2ADD(IAC, IAC);
151732385Sminshall 		break;
151832385Sminshall 	    default:
151932385Sminshall 		NETADD(c);
152032385Sminshall 		break;
152132385Sminshall 	    }
152232385Sminshall 	} else if (c == IAC) {
152332385Sminshall 	    NET2ADD(IAC, IAC);
152432385Sminshall 	} else {
152532385Sminshall 	    NETADD(c);
152632385Sminshall 	}
152732385Sminshall     }
152832667Sminshall     if (count)
152932667Sminshall 	ring_consumed(&ttyiring, count);
153032385Sminshall     return returnValue||count;		/* Non-zero if we did anything */
153132385Sminshall }
153232377Sminshall 
153327088Sminshall /*
153432377Sminshall  * Scheduler()
153532377Sminshall  *
153632377Sminshall  * Try to do something.
153732377Sminshall  *
153832377Sminshall  * If we do something useful, return 1; else return 0.
153932377Sminshall  *
154027110Sminshall  */
154127110Sminshall 
154227110Sminshall 
154332377Sminshall int
154432377Sminshall Scheduler(block)
154532377Sminshall int	block;			/* should we block in the select ? */
154627110Sminshall {
154732377Sminshall 		/* One wants to be a bit careful about setting returnValue
154832377Sminshall 		 * to one, since a one implies we did some useful work,
154932377Sminshall 		 * and therefore probably won't be called to block next
155032377Sminshall 		 * time (TN3270 mode only).
155132377Sminshall 		 */
155232531Sminshall     int returnValue;
155332531Sminshall     int netin, netout, netex, ttyin, ttyout;
155427110Sminshall 
155532531Sminshall     /* Decide which rings should be processed */
155632531Sminshall 
155732531Sminshall     netout = ring_full_count(&netoring) &&
155838689Sborman 	    (flushline ||
155938689Sborman 		(my_want_state_is_wont(TELOPT_LINEMODE)
156038689Sborman #ifdef	KLUDGELINEMODE
156138689Sborman 			&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
156238689Sborman #endif
156338689Sborman 		) ||
156438689Sborman 			my_want_state_is_will(TELOPT_BINARY));
156532531Sminshall     ttyout = ring_full_count(&ttyoring);
156632531Sminshall 
156732377Sminshall #if	defined(TN3270)
156832531Sminshall     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
156932377Sminshall #else	/* defined(TN3270) */
157032531Sminshall     ttyin = ring_empty_count(&ttyiring);
157132377Sminshall #endif	/* defined(TN3270) */
157232531Sminshall 
157332531Sminshall #if	defined(TN3270)
157432531Sminshall     netin = ring_empty_count(&netiring);
157532377Sminshall #   else /* !defined(TN3270) */
157632531Sminshall     netin = !ISend && ring_empty_count(&netiring);
157732377Sminshall #   endif /* !defined(TN3270) */
157832531Sminshall 
157932531Sminshall     netex = !SYNCHing;
158032531Sminshall 
158132531Sminshall     /* If we have seen a signal recently, reset things */
158232377Sminshall #   if defined(TN3270) && defined(unix)
158332377Sminshall     if (HaveInput) {
158432377Sminshall 	HaveInput = 0;
158532377Sminshall 	signal(SIGIO, inputAvailable);
158632377Sminshall     }
158732377Sminshall #endif	/* defined(TN3270) && defined(unix) */
158832377Sminshall 
158932531Sminshall     /* Call to system code to process rings */
159027178Sminshall 
159132531Sminshall     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
159227178Sminshall 
159332531Sminshall     /* Now, look at the input rings, looking for work to do. */
159432377Sminshall 
159532531Sminshall     if (ring_full_count(&ttyiring)) {
159632377Sminshall #   if defined(TN3270)
159732377Sminshall 	if (In3270) {
159834848Sminshall 	    int c;
159934848Sminshall 
160033804Sminshall 	    c = DataFromTerminal(ttyiring.consume,
160132528Sminshall 					ring_full_consecutive(&ttyiring));
160232377Sminshall 	    if (c) {
160332377Sminshall 		returnValue = 1;
160432667Sminshall 	        ring_consumed(&ttyiring, c);
160532377Sminshall 	    }
160632377Sminshall 	} else {
160732377Sminshall #   endif /* defined(TN3270) */
160832554Sminshall 	    returnValue |= telsnd();
160932377Sminshall #   if defined(TN3270)
161027178Sminshall 	}
161132531Sminshall #   endif /* defined(TN3270) */
161227178Sminshall     }
161332377Sminshall 
161432528Sminshall     if (ring_full_count(&netiring)) {
161532377Sminshall #	if !defined(TN3270)
161632385Sminshall 	returnValue |= telrcv();
161732377Sminshall #	else /* !defined(TN3270) */
161832377Sminshall 	returnValue = Push3270();
161932377Sminshall #	endif /* !defined(TN3270) */
162032377Sminshall     }
162132377Sminshall     return returnValue;
162227178Sminshall }
162327178Sminshall 
162427178Sminshall /*
162532377Sminshall  * Select from tty and network...
162627088Sminshall  */
162732377Sminshall void
162832377Sminshall telnet()
162927088Sminshall {
163032531Sminshall     sys_telnet_init();
163127088Sminshall 
163232377Sminshall #   if !defined(TN3270)
163332377Sminshall     if (telnetport) {
163438689Sborman 	send_do(TELOPT_SGA, 1);
163538689Sborman 	send_will(TELOPT_TTYPE, 1);
163638689Sborman 	send_will(TELOPT_NAWS, 1);
163738689Sborman 	send_will(TELOPT_TSPEED, 1);
163838689Sborman 	send_will(TELOPT_LFLOW, 1);
163938689Sborman 	send_will(TELOPT_LINEMODE, 1);
164038908Sborman 	send_do(TELOPT_STATUS, 1);
164127178Sminshall     }
164232377Sminshall #   endif /* !defined(TN3270) */
164327088Sminshall 
164432377Sminshall #   if !defined(TN3270)
164532377Sminshall     for (;;) {
164632385Sminshall 	int schedValue;
164732385Sminshall 
164832385Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
164932385Sminshall 	    if (schedValue == -1) {
165032385Sminshall 		setcommandmode();
165132385Sminshall 		return;
165232385Sminshall 	    }
165332385Sminshall 	}
165432385Sminshall 
165532531Sminshall 	if (Scheduler(1) == -1) {
165632377Sminshall 	    setcommandmode();
165732377Sminshall 	    return;
165832377Sminshall 	}
165932377Sminshall     }
166032377Sminshall #   else /* !defined(TN3270) */
166132377Sminshall     for (;;) {
166232377Sminshall 	int schedValue;
166327088Sminshall 
166432377Sminshall 	while (!In3270 && !shell_active) {
166532531Sminshall 	    if (Scheduler(1) == -1) {
166632377Sminshall 		setcommandmode();
166732377Sminshall 		return;
166832377Sminshall 	    }
166927088Sminshall 	}
167032377Sminshall 
167132377Sminshall 	while ((schedValue = Scheduler(0)) != 0) {
167232377Sminshall 	    if (schedValue == -1) {
167332377Sminshall 		setcommandmode();
167432377Sminshall 		return;
167532377Sminshall 	    }
167627088Sminshall 	}
167732377Sminshall 		/* If there is data waiting to go out to terminal, don't
167832377Sminshall 		 * schedule any more data for the terminal.
167932377Sminshall 		 */
168034304Sminshall 	if (ring_full_count(&ttyoring)) {
168132377Sminshall 	    schedValue = 1;
168227088Sminshall 	} else {
168332377Sminshall 	    if (shell_active) {
168432377Sminshall 		if (shell_continue() == 0) {
168532377Sminshall 		    ConnectScreen();
168627088Sminshall 		}
168732377Sminshall 	    } else if (In3270) {
168832377Sminshall 		schedValue = DoTerminalOutput();
168932377Sminshall 	    }
169027088Sminshall 	}
169132377Sminshall 	if (schedValue && (shell_active == 0)) {
169232531Sminshall 	    if (Scheduler(1) == -1) {
169332377Sminshall 		setcommandmode();
169432377Sminshall 		return;
169532377Sminshall 	    }
169627088Sminshall 	}
169732377Sminshall     }
169832377Sminshall #   endif /* !defined(TN3270) */
169927088Sminshall }
170032377Sminshall 
170134848Sminshall #if	0	/* XXX - this not being in is a bug */
170227088Sminshall /*
170332554Sminshall  * nextitem()
170432554Sminshall  *
170532554Sminshall  *	Return the address of the next "item" in the TELNET data
170632554Sminshall  * stream.  This will be the address of the next character if
170732554Sminshall  * the current address is a user data character, or it will
170832554Sminshall  * be the address of the character following the TELNET command
170932554Sminshall  * if the current address is a TELNET IAC ("I Am a Command")
171032554Sminshall  * character.
171132554Sminshall  */
171232554Sminshall 
171332554Sminshall static char *
171432554Sminshall nextitem(current)
171532554Sminshall char	*current;
171632554Sminshall {
171732554Sminshall     if ((*current&0xff) != IAC) {
171832554Sminshall 	return current+1;
171932554Sminshall     }
172032554Sminshall     switch (*(current+1)&0xff) {
172132554Sminshall     case DO:
172232554Sminshall     case DONT:
172332554Sminshall     case WILL:
172432554Sminshall     case WONT:
172532554Sminshall 	return current+3;
172632554Sminshall     case SB:		/* loop forever looking for the SE */
172732554Sminshall 	{
172832554Sminshall 	    register char *look = current+2;
172932554Sminshall 
173032554Sminshall 	    for (;;) {
173132554Sminshall 		if ((*look++&0xff) == IAC) {
173232554Sminshall 		    if ((*look++&0xff) == SE) {
173332554Sminshall 			return look;
173432554Sminshall 		    }
173532554Sminshall 		}
173632554Sminshall 	    }
173732554Sminshall 	}
173832554Sminshall     default:
173932554Sminshall 	return current+2;
174032554Sminshall     }
174132554Sminshall }
174234848Sminshall #endif	/* 0 */
174332554Sminshall 
174432554Sminshall /*
174532554Sminshall  * netclear()
174632554Sminshall  *
174732554Sminshall  *	We are about to do a TELNET SYNCH operation.  Clear
174832554Sminshall  * the path to the network.
174932554Sminshall  *
175032554Sminshall  *	Things are a bit tricky since we may have sent the first
175132554Sminshall  * byte or so of a previous TELNET command into the network.
175232554Sminshall  * So, we have to scan the network buffer from the beginning
175332554Sminshall  * until we are up to where we want to be.
175432554Sminshall  *
175532554Sminshall  *	A side effect of what we do, just to keep things
175632554Sminshall  * simple, is to clear the urgent data pointer.  The principal
175732554Sminshall  * caller should be setting the urgent data pointer AFTER calling
175832554Sminshall  * us in any case.
175932554Sminshall  */
176032554Sminshall 
176132554Sminshall static void
176232554Sminshall netclear()
176332554Sminshall {
176432554Sminshall #if	0	/* XXX */
176532554Sminshall     register char *thisitem, *next;
176632554Sminshall     char *good;
176732554Sminshall #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
176832554Sminshall 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
176932554Sminshall 
177032554Sminshall     thisitem = netobuf;
177132554Sminshall 
177232554Sminshall     while ((next = nextitem(thisitem)) <= netobuf.send) {
177332554Sminshall 	thisitem = next;
177432554Sminshall     }
177532554Sminshall 
177632554Sminshall     /* Now, thisitem is first before/at boundary. */
177732554Sminshall 
177832554Sminshall     good = netobuf;	/* where the good bytes go */
177932554Sminshall 
178032554Sminshall     while (netoring.add > thisitem) {
178132554Sminshall 	if (wewant(thisitem)) {
178232554Sminshall 	    int length;
178332554Sminshall 
178432554Sminshall 	    next = thisitem;
178532554Sminshall 	    do {
178632554Sminshall 		next = nextitem(next);
178732554Sminshall 	    } while (wewant(next) && (nfrontp > next));
178832554Sminshall 	    length = next-thisitem;
178932554Sminshall 	    memcpy(good, thisitem, length);
179032554Sminshall 	    good += length;
179132554Sminshall 	    thisitem = next;
179232554Sminshall 	} else {
179332554Sminshall 	    thisitem = nextitem(thisitem);
179432554Sminshall 	}
179532554Sminshall     }
179632554Sminshall 
179732554Sminshall #endif	/* 0 */
179832554Sminshall }
179932554Sminshall 
180032554Sminshall /*
180132377Sminshall  * These routines add various telnet commands to the data stream.
180227088Sminshall  */
180332377Sminshall 
180432554Sminshall static void
180532554Sminshall doflush()
180632554Sminshall {
180732554Sminshall     NET2ADD(IAC, DO);
180832554Sminshall     NETADD(TELOPT_TM);
180932554Sminshall     flushline = 1;
181032554Sminshall     flushout = 1;
181132554Sminshall     ttyflush(1);			/* Flush/drop output */
181232554Sminshall     /* do printoption AFTER flush, otherwise the output gets tossed... */
181337226Sminshall     printoption("SENT", "do", TELOPT_TM);
181432554Sminshall }
181532554Sminshall 
181632377Sminshall void
181732377Sminshall xmitAO()
181827088Sminshall {
181932377Sminshall     NET2ADD(IAC, AO);
182038908Sborman     printoption("SENT", "IAC", AO);
182132377Sminshall     if (autoflush) {
182232377Sminshall 	doflush();
182332377Sminshall     }
182432377Sminshall }
182527088Sminshall 
182632377Sminshall 
182732377Sminshall void
182832377Sminshall xmitEL()
182927088Sminshall {
183032377Sminshall     NET2ADD(IAC, EL);
183138908Sborman     printoption("SENT", "IAC", EL);
183227088Sminshall }
183327088Sminshall 
183432377Sminshall void
183532377Sminshall xmitEC()
183627088Sminshall {
183732377Sminshall     NET2ADD(IAC, EC);
183838908Sborman     printoption("SENT", "IAC", EC);
183927088Sminshall }
184027088Sminshall 
184132377Sminshall 
184232377Sminshall #if	defined(NOT43)
184332377Sminshall int
184432377Sminshall #else	/* defined(NOT43) */
184532377Sminshall void
184632377Sminshall #endif	/* defined(NOT43) */
184732377Sminshall dosynch()
184827088Sminshall {
184932377Sminshall     netclear();			/* clear the path to the network */
185033294Sminshall     NETADD(IAC);
185133294Sminshall     setneturg();
185233294Sminshall     NETADD(DM);
185338908Sborman     printoption("SENT", "IAC", DM);
185427088Sminshall 
185532377Sminshall #if	defined(NOT43)
185632377Sminshall     return 0;
185732377Sminshall #endif	/* defined(NOT43) */
185827088Sminshall }
185927088Sminshall 
186032377Sminshall void
186138908Sborman get_status()
186238908Sborman {
186338908Sborman     char tmp[16];
186438908Sborman     register char *cp;
186538908Sborman 
186638908Sborman     if (my_want_state_is_dont(TELOPT_STATUS)) {
186738908Sborman 	printf("Remote side does not support STATUS option\n");
186838908Sborman 	return;
186938908Sborman     }
187038908Sborman     if (!showoptions)
187138908Sborman 	printf("You will not see the response unless you set \"options\"\n");
187238908Sborman 
187338908Sborman     cp = tmp;
187438908Sborman 
187538908Sborman     *cp++ = IAC;
187638908Sborman     *cp++ = SB;
187738908Sborman     *cp++ = TELOPT_STATUS;
187838908Sborman     *cp++ = TELQUAL_SEND;
187938908Sborman     *cp++ = IAC;
188038908Sborman     *cp++ = SE;
188138908Sborman     if (NETROOM() >= cp - tmp) {
188238908Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
188338908Sborman 	printsub('>', tmp+2, cp - tmp - 2);
188438908Sborman     }
188538908Sborman }
188638908Sborman 
188738908Sborman void
188832377Sminshall intp()
188927088Sminshall {
189032377Sminshall     NET2ADD(IAC, IP);
189138908Sborman     printoption("SENT", "IAC", IP);
189232377Sminshall     flushline = 1;
189332377Sminshall     if (autoflush) {
189432377Sminshall 	doflush();
189532377Sminshall     }
189632377Sminshall     if (autosynch) {
189732377Sminshall 	dosynch();
189832377Sminshall     }
189927088Sminshall }
190027186Sminshall 
190132377Sminshall void
190232377Sminshall sendbrk()
190327186Sminshall {
190432377Sminshall     NET2ADD(IAC, BREAK);
190538908Sborman     printoption("SENT", "IAC", BREAK);
190632377Sminshall     flushline = 1;
190732377Sminshall     if (autoflush) {
190832377Sminshall 	doflush();
190932377Sminshall     }
191032377Sminshall     if (autosynch) {
191132377Sminshall 	dosynch();
191232377Sminshall     }
191327186Sminshall }
191438689Sborman 
191538689Sborman void
191638689Sborman sendabort()
191738689Sborman {
191838689Sborman     NET2ADD(IAC, ABORT);
191938908Sborman     printoption("SENT", "IAC", ABORT);
192038689Sborman     flushline = 1;
192138689Sborman     if (autoflush) {
192238689Sborman 	doflush();
192338689Sborman     }
192438689Sborman     if (autosynch) {
192538689Sborman 	dosynch();
192638689Sborman     }
192738689Sborman }
192838689Sborman 
192938689Sborman void
193038689Sborman sendsusp()
193138689Sborman {
193238689Sborman     NET2ADD(IAC, SUSP);
193338908Sborman     printoption("SENT", "IAC", SUSP);
193438689Sborman     flushline = 1;
193538689Sborman     if (autoflush) {
193638689Sborman 	doflush();
193738689Sborman     }
193838689Sborman     if (autosynch) {
193938689Sborman 	dosynch();
194038689Sborman     }
194138689Sborman }
194238689Sborman 
194338689Sborman void
194438689Sborman sendeof()
194538689Sborman {
194638908Sborman     NET2ADD(IAC, xEOF);
194738908Sborman     printoption("SENT", "IAC", xEOF);
194838689Sborman }
194938689Sborman 
195037219Sminshall /*
195137219Sminshall  * Send a window size update to the remote system.
195237219Sminshall  */
195337219Sminshall 
195437219Sminshall void
195537219Sminshall sendnaws()
195637219Sminshall {
195737219Sminshall     long rows, cols;
195838689Sborman     unsigned char tmp[16];
195938689Sborman     register unsigned char *cp;
196037219Sminshall 
196138689Sborman     if (my_state_is_wont(TELOPT_NAWS))
196238689Sborman 	return;
196337219Sminshall 
196438689Sborman #define	PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
196538689Sborman 			    if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
196638689Sborman 
196737219Sminshall     if (TerminalWindowSize(&rows, &cols) == 0) {	/* Failed */
196837219Sminshall 	return;
196937219Sminshall     }
197037219Sminshall 
197138689Sborman     cp = tmp;
197238689Sborman 
197338689Sborman     *cp++ = IAC;
197438689Sborman     *cp++ = SB;
197538689Sborman     *cp++ = TELOPT_NAWS;
197638689Sborman     PUTSHORT(cp, cols);
197738689Sborman     PUTSHORT(cp, rows);
197838689Sborman     *cp++ = IAC;
197938689Sborman     *cp++ = SE;
198038689Sborman     if (NETROOM() >= cp - tmp) {
198138689Sborman 	ring_supply_data(&netoring, tmp, cp-tmp);
198238689Sborman 	printsub('>', tmp+2, cp - tmp - 2);
198337219Sminshall     }
198437219Sminshall }
198537226Sminshall 
198638908Sborman tel_enter_binary(rw)
198738908Sborman int rw;
198837226Sminshall {
198938908Sborman     if (rw&1)
199038908Sborman 	send_do(TELOPT_BINARY, 1);
199138908Sborman     if (rw&2)
199238908Sborman 	send_will(TELOPT_BINARY, 1);
199337226Sminshall }
199437226Sminshall 
199538908Sborman tel_leave_binary(rw)
199638908Sborman int rw;
199737226Sminshall {
199838908Sborman     if (rw&1)
199938908Sborman 	send_dont(TELOPT_BINARY, 1);
200038908Sborman     if (rw&2)
200138908Sborman 	send_wont(TELOPT_BINARY, 1);
200237226Sminshall }
2003