xref: /original-bsd/usr.bin/tn3270/telnet.c (revision 71b05459)
1 /*
2  *	Copyright (c) 1984-1987 by the Regents of the
3  *	University of California and by Gregory Glenn Minshall.
4  *
5  *	Permission to use, copy, modify, and distribute these
6  *	programs and their documentation for any purpose and
7  *	without fee is hereby granted, provided that this
8  *	copyright and permission appear on all copies and
9  *	supporting documentation, the name of the Regents of
10  *	the University of California not be used in advertising
11  *	or publicity pertaining to distribution of the programs
12  *	without specific prior permission, and notice be given in
13  *	supporting documentation that copying and distribution is
14  *	by permission of the Regents of the University of California
15  *	and by Gregory Glenn Minshall.  Neither the Regents of the
16  *	University of California nor Gregory Glenn Minshall make
17  *	representations about the suitability of this software
18  *	for any purpose.  It is provided "as is" without
19  *	express or implied warranty.
20  */
21 
22 #ifndef lint
23 static char copyright[] =
24 "@(#) Copyright (c) 1984-1987 Regents of the University of California.\n\
25  All rights reserved.\n";
26 #endif	/* not lint */
27 
28 #ifndef lint
29 static char sccsid[] = "@(#)telnet.c	1.18.1.1 (Berkeley) 12/04/88";
30 #endif	/* not lint */
31 
32 /*
33  * User telnet program, modified for use by tn3270.c.
34  *
35  * Many of the FUNCTIONAL changes in this newest version of TELNET
36  * were suggested by Dave Borman of Cray Research, Inc.
37  *
38  * Other changes in the tn3270 side come from Alan Crosswell (Columbia),
39  * Bob Braden (ISI), Steve Jacobson (Berkeley), and Cliff Frost (Berkeley).
40  *
41  * This code is common between telnet(1c) and tn3270(1c).  There are the
42  * following defines used to generate the various versions:
43  *
44  *	TN3270		- 	This is to be linked with tn3270.
45  *
46  *	NOT43		-	Allows the program to compile and run on
47  *				a 4.2BSD system.
48  *
49  *	PUTCHAR		-	Within tn3270, on a NOT43 system,
50  *				allows the use of the 4.3 curses
51  *				(greater speed updating the screen).
52  *				You need the 4.3 curses for this to work.
53  *
54  *	FD_SETSIZE	-	On whichever system, if this isn't defined,
55  *				we patch over the FD_SET, etc., macros with
56  *				some homebrewed ones.
57  *
58  *	SO_OOBINLINE	-	This is a socket option which we would like
59  *				to set to allow TCP urgent data to come
60  *				to us "inline".  This is NECESSARY for
61  *				CORRECT operation, and desireable for
62  *				simpler operation.
63  *
64  *	LNOFLSH		-	Detects the presence of the LNOFLSH bit
65  *				in the tty structure.
66  *
67  *	unix		-	Compiles in unix specific stuff.
68  *
69  *	MSDOS		-	Compiles in MSDOS specific stuff.
70  *
71  */
72 
73 #if	!defined(TN3270)
74 #define	ExitString(f,s,r)	{ fprintf(f, s); exit(r); }
75 #define	Exit(x)			exit(x)
76 #define	SetIn3270()
77 
78 void	setcommandmode(), command();	/* forward declarations */
79 #endif	/* !defined(TN3270) */
80 
81 #include <sys/types.h>
82 #include <sys/socket.h>
83 
84 #include <netinet/in.h>
85 
86 #if	defined(unix)
87 /* By the way, we need to include curses.h before telnet.h since,
88  * among other things, telnet.h #defines 'DO', which is a variable
89  * declared in curses.h.
90  */
91 #include <curses.h>
92 #endif	/* defined(unix) */
93 
94 #define	TELOPTS
95 #include <arpa/telnet.h>
96 
97 #if	!defined(NOT43)
98 #include <arpa/inet.h>
99 #else	/* !defined(NOT43) */
100 extern unsigned long inet_addr();
101 extern char	*inet_ntoa();
102 #endif	/* !defined(NOT43) */
103 
104 #include <stdio.h>
105 #include <ctype.h>
106 #include <errno.h>
107 #include <setjmp.h>
108 #include <netdb.h>
109 
110 #if	defined(unix)
111 #include <strings.h>
112 #else	/* defined(unix) */
113 #include <string.h>
114 #endif	/* defined(unix) */
115 
116 #if	defined(TN3270)
117 #include "ascii/termin.ext"
118 #include "ctlr/screen.h"
119 #include "ctlr/oia.h"
120 #include "ctlr/options.ext"
121 #include "ctlr/outbound.ext"
122 #include "general/globals.h"
123 #include "telnet.ext"
124 #endif	/* defined(TN3270) */
125 
126 #include "general/general.h"
127 
128 
129 
130 #ifndef	FD_SETSIZE
131 /*
132  * The following is defined just in case someone should want to run
133  * this telnet on a 4.2 system.
134  *
135  */
136 
137 #define	FD_SET(n, p)	((p)->fds_bits[0] |= (1<<(n)))
138 #define	FD_CLR(n, p)	((p)->fds_bits[0] &= ~(1<<(n)))
139 #define	FD_ISSET(n, p)	((p)->fds_bits[0] & (1<<(n)))
140 #define FD_ZERO(p)	((p)->fds_bits[0] = 0)
141 
142 #endif
143 
144 #define	strip(x)	((x)&0x7f)
145 #define min(x,y)	((x<y)? x:y)
146 
147 #if	defined(TN3270)
148 static char	Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp;
149 #endif	/* defined(TN3270) */
150 
151 static char	ttyobuf[2*BUFSIZ], *tfrontp, *tbackp;
152 #define	TTYADD(c)	{ if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
153 #define	TTYLOC()	(tfrontp)
154 #define	TTYMAX()	(ttyobuf+sizeof ttyobuf-1)
155 #define	TTYMIN()	(netobuf)
156 #define	TTYBYTES()	(tfrontp-tbackp)
157 #define	TTYROOM()	(TTYMAX()-TTYLOC()+1)
158 
159 static char	netobuf[2*BUFSIZ], *nfrontp, *nbackp;
160 #define	NETADD(c)	{ *nfrontp++ = c; }
161 #define	NET2ADD(c1,c2)	{ NETADD(c1); NETADD(c2); }
162 #define NETLOC()	(nfrontp)
163 #define	NETMAX()	(netobuf+sizeof netobuf-1)
164 #define	NETBYTES()	(nfrontp-nbackp)
165 #define	NETROOM()	(NETMAX()-NETLOC()+1)
166 static char	*neturg;		/* one past last byte of urgent data */
167 
168 static char	subbuffer[100],
169 		*subpointer, *subend;	 /* buffer for sub-options */
170 #define	SB_CLEAR()	subpointer = subbuffer;
171 #define	SB_TERM()	subend = subpointer;
172 #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
173 				*subpointer++ = (c); \
174 			}
175 
176 static char	sb_terminal[] = { IAC, SB,
177 			TELOPT_TTYPE, TELQUAL_IS,
178 			'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2',
179 			IAC, SE };
180 #define	SBTERMMODEL	13
181 
182 
183 static char	hisopts[256];
184 static char	myopts[256];
185 
186 static char	doopt[] = { IAC, DO, '%', 'c', 0 };
187 static char	dont[] = { IAC, DONT, '%', 'c', 0 };
188 static char	will[] = { IAC, WILL, '%', 'c', 0 };
189 static char	wont[] = { IAC, WONT, '%', 'c', 0 };
190 
191 struct cmd {
192 	char	*name;		/* command name */
193 	char	*help;		/* help string */
194 	int	(*handler)();	/* routine which executes command */
195 	int	dohelp;		/* Should we give general help information? */
196 	int	needconnect;	/* Do we need to be connected to execute? */
197 };
198 
199 static char	sibuf[BUFSIZ], *sbp;
200 static char	tibuf[BUFSIZ], *tbp;
201 static fd_set ibits, obits, xbits;
202 
203 
204 static int
205 	connected,
206 	net,
207 	scc,
208 	tcc,
209 	showoptions,
210 	In3270,		/* Are we in 3270 mode? */
211 	ISend,		/* trying to send network data in */
212 	debug = 0,
213 	crmod,
214 	netdata,
215 	noasynch = 0,	/* User specified "-noasynch" on command line */
216 	askedSGA = 0,	/* We have talked about suppress go ahead */
217 	telnetport = 1;
218 
219 static FILE	*NetTrace = 0;		/* Not in bss, since needs to stay */
220 
221 #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
222 
223 static char
224 	*prompt = 0,
225 	escape,
226 	echoc;
227 
228 static int
229 	SYNCHing,		/* we are in TELNET SYNCH mode */
230 	flushout,		/* flush output */
231 	autoflush = 0,		/* flush output when interrupting? */
232 	autosynch,		/* send interrupt characters with SYNCH? */
233 	localchars,		/* we recognize interrupt/quit */
234 	donelclchars,		/* the user has set "localchars" */
235 	donebinarytoggle,	/* the user has put us in binary */
236 	dontlecho,		/* do we suppress local echoing right now? */
237 	globalmode;
238 
239 /*	The following are some tn3270 specific flags */
240 #if	defined(TN3270)
241 
242 static int
243 	Sent3270TerminalType;	/* Have we said we are a 3270? */
244 
245 /* Some real, live, globals. */
246 int
247 	tout,			/* Output file descriptor */
248 	tin;			/* Input file descriptor */
249 
250 #else	/* defined(TN3270) */
251 static int tin, tout;		/* file descriptors */
252 #endif	/* defined(TN3270) */
253 
254 
255 /*
256  * Telnet receiver states for fsm
257  */
258 #define	TS_DATA		0
259 #define	TS_IAC		1
260 #define	TS_WILL		2
261 #define	TS_WONT		3
262 #define	TS_DO		4
263 #define	TS_DONT		5
264 #define	TS_CR		6
265 #define	TS_SB		7		/* sub-option collection */
266 #define	TS_SE		8		/* looking for sub-option end */
267 
268 static int	telrcv_state = TS_DATA;
269 
270 static char	line[200];
271 static int	margc;
272 static char	*margv[20];
273 
274 static jmp_buf	toplevel = { 0 };
275 static jmp_buf	peerdied;
276 
277 extern	int errno;
278 
279 
280 static struct sockaddr_in sin;
281 
282 static struct	servent *sp = 0;
283 
284 static int	flushline;
285 
286 static char	*hostname;
287 static char	hnamebuf[32];
288 
289 /*
290  * The following are some clocks used to decide how to interpret
291  * the relationship between various variables.
292  */
293 
294 static struct {
295     int
296 	system,			/* what the current time is */
297 	echotoggle,		/* last time user entered echo character */
298 	modenegotiated,		/* last time operating mode negotiated */
299 	didnetreceive,		/* last time we read data from network */
300 	gotDM;			/* when did we last see a data mark */
301 } clocks;
302 
303 #define	settimer(x)	clocks.x = clocks.system++
304 
305 /*	Various modes */
306 #define	MODE_LINE(m)	(modelist[m].modetype & LINE)
307 #define	MODE_LOCAL_CHARS(m)	(modelist[m].modetype &  LOCAL_CHARS)
308 #define	MODE_LOCAL_ECHO(m)	(modelist[m].modetype & LOCAL_ECHO)
309 #define	MODE_COMMAND_LINE(m)	(modelist[m].modetype & COMMAND_LINE)
310 
311 #define	LOCAL_CHARS	0x01		/* Characters processed locally */
312 #define	LINE		0x02		/* Line-by-line mode of operation */
313 #define	LOCAL_ECHO	0x04		/* Echoing locally */
314 #define	COMMAND_LINE	0x08		/* Command line mode */
315 
316 static struct {
317     char *modedescriptions;
318     char modetype;
319 } modelist[] = {
320 	{ "telnet command mode", COMMAND_LINE },
321 	{ "character-at-a-time mode", 0 },
322 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
323 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
324 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
325 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
326 	{ "3270 mode", 0 },
327 };
328 
329 
330 /*
331  * The following routines try to encapsulate what is system dependent
332  * (at least between 4.x and dos) which is used in telnet.c.
333  */
334 
335 #if	defined(unix)
336 #include <sys/ioctl.h>
337 #include <sys/time.h>
338 #include <signal.h>
339 
340 int
341 	HaveInput;		/* There is input available to scan */
342 
343 #if	defined(TN3270)
344 static char	tline[200];
345 char	*transcom = 0;	/* transparent mode command (default: none) */
346 #endif	/* defined(TN3270) */
347 
348 static struct	tchars otc = { 0 }, ntc = { 0 };
349 static struct	ltchars oltc = { 0 }, nltc = { 0 };
350 static struct	sgttyb ottyb = { 0 }, nttyb = { 0 };
351 
352 
353 #define	TerminalWrite(fd,buf,n)	write(fd,buf,n)
354 #define	TerminalRead(fd,buf,n)	read(fd,buf,n)
355 
356 /*
357  *
358  */
359 
360 static int
TerminalAutoFlush()361 TerminalAutoFlush()					/* unix */
362 {
363 #if	defined(LNOFLSH)
364     ioctl(0, TIOCLGET, (char *)&autoflush);
365     return !(autoflush&LNOFLSH);	/* if LNOFLSH, no autoflush */
366 #else	/* LNOFLSH */
367     return 1;
368 #endif	/* LNOFLSH */
369 }
370 
371 /*
372  * TerminalSpecialChars()
373  *
374  * Look at an input character to see if it is a special character
375  * and decide what to do.
376  *
377  * Output:
378  *
379  *	0	Don't add this character.
380  *	1	Do add this character
381  */
382 
383 int
TerminalSpecialChars(c)384 TerminalSpecialChars(c)			/* unix */
385 int	c;
386 {
387     void doflush(), intp(), sendbrk();
388 
389     if (c == ntc.t_intrc) {
390 	intp();
391 	return 0;
392     } else if (c == ntc.t_quitc) {
393 	sendbrk();
394 	return 0;
395     } else if (c == nltc.t_flushc) {
396 	NET2ADD(IAC, AO);
397 	if (autoflush) {
398 	    doflush();
399 	}
400 	return 0;
401     } else if (!MODE_LOCAL_CHARS(globalmode)) {
402 	if (c == nttyb.sg_kill) {
403 	    NET2ADD(IAC, EL);
404 	    return 0;
405 	} else if (c == nttyb.sg_erase) {
406 	    NET2ADD(IAC, EC);
407 	    return 0;
408 	}
409     }
410     return 1;
411 }
412 
413 
414 /*
415  * Flush output to the terminal
416  */
417 
418 static void
TerminalFlushOutput()419 TerminalFlushOutput()				/* unix */
420 {
421     (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
422 }
423 
424 static void
TerminalSaveState()425 TerminalSaveState()				/* unix */
426 {
427     ioctl(0, TIOCGETP, (char *)&ottyb);
428     ioctl(0, TIOCGETC, (char *)&otc);
429     ioctl(0, TIOCGLTC, (char *)&oltc);
430 
431     ntc = otc;
432     nltc = oltc;
433     nttyb = ottyb;
434 }
435 
436 static void
TerminalRestoreState()437 TerminalRestoreState()				/* unix */
438 {
439 }
440 
441 /*
442  * TerminalNewMode - set up terminal to a specific mode.
443  */
444 
445 
446 static void
TerminalNewMode(f)447 TerminalNewMode(f)					/* unix */
448 register int f;
449 {
450     static int prevmode = 0;
451     struct tchars *tc;
452     struct tchars tc3;
453     struct ltchars *ltc;
454     struct sgttyb sb;
455     int onoff;
456     int old;
457     struct	tchars notc2;
458     struct	ltchars noltc2;
459     static struct	tchars notc =	{ -1, -1, -1, -1, -1, -1 };
460     static struct	ltchars noltc =	{ -1, -1, -1, -1, -1, -1 };
461 
462     globalmode = f;
463     if (prevmode == f)
464 	return;
465     old = prevmode;
466     prevmode = f;
467     sb = nttyb;
468 
469     switch (f) {
470 
471     case 0:
472 	onoff = 0;
473 	tc = &otc;
474 	ltc = &oltc;
475 	break;
476 
477     case 1:		/* remote character processing, remote echo */
478     case 2:		/* remote character processing, local echo */
479     case 6:		/* 3270 mode - like 1, but with xon/xoff local */
480 		    /* (might be nice to have "6" in telnet also...) */
481 	    sb.sg_flags |= CBREAK;
482 	    if ((f == 1) || (f == 6)) {
483 		sb.sg_flags &= ~(ECHO|CRMOD);
484 	    } else {
485 		sb.sg_flags |= ECHO|CRMOD;
486 	    }
487 	    sb.sg_erase = sb.sg_kill = -1;
488 	    if (f == 6) {
489 		tc = &tc3;
490 		tc3 = notc;
491 		    /* get XON, XOFF characters */
492 		tc3.t_startc = otc.t_startc;
493 		tc3.t_stopc = otc.t_stopc;
494 	    } else {
495 		/*
496 		 * If user hasn't specified one way or the other,
497 		 * then default to not trapping signals.
498 		 */
499 		if (!donelclchars) {
500 		    localchars = 0;
501 		}
502 		if (localchars) {
503 		    notc2 = notc;
504 		    notc2.t_intrc = ntc.t_intrc;
505 		    notc2.t_quitc = ntc.t_quitc;
506 		    tc = &notc2;
507 		} else {
508 		    tc = &notc;
509 		}
510 	    }
511 	    ltc = &noltc;
512 	    onoff = 1;
513 	    break;
514     case 3:		/* local character processing, remote echo */
515     case 4:		/* local character processing, local echo */
516     case 5:		/* local character processing, no echo */
517 	    sb.sg_flags &= ~CBREAK;
518 	    sb.sg_flags |= CRMOD;
519 	    if (f == 4)
520 		sb.sg_flags |= ECHO;
521 	    else
522 		sb.sg_flags &= ~ECHO;
523 	    notc2 = ntc;
524 	    tc = &notc2;
525 	    noltc2 = oltc;
526 	    ltc = &noltc2;
527 	    /*
528 	     * If user hasn't specified one way or the other,
529 	     * then default to trapping signals.
530 	     */
531 	    if (!donelclchars) {
532 		localchars = 1;
533 	    }
534 	    if (localchars) {
535 		notc2.t_brkc = nltc.t_flushc;
536 		noltc2.t_flushc = -1;
537 	    } else {
538 		notc2.t_intrc = notc2.t_quitc = -1;
539 	    }
540 	    noltc2.t_suspc = escape;
541 	    noltc2.t_dsuspc = -1;
542 	    onoff = 1;
543 	    break;
544 
545     default:
546 	    return;
547     }
548     ioctl(tin, TIOCSLTC, (char *)ltc);
549     ioctl(tin, TIOCSETC, (char *)tc);
550     ioctl(tin, TIOCSETP, (char *)&sb);
551 #if	(!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR))
552     ioctl(tin, FIONBIO, (char *)&onoff);
553     ioctl(tout, FIONBIO, (char *)&onoff);
554 #endif	/* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */
555 #if	defined(TN3270)
556     if (noasynch == 0) {
557 	ioctl(tin, FIOASYNC, (char *)&onoff);
558     }
559 #endif	/* defined(TN3270) */
560 
561     if (MODE_LINE(f)) {
562 	void doescape();
563 
564 	signal(SIGTSTP, doescape);
565     } else if (MODE_LINE(old)) {
566 	signal(SIGTSTP, SIG_DFL);
567 	sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1)));
568     }
569 }
570 
571 
572 int
NetClose(net)573 NetClose(net)
574 int	net;
575 {
576     return close(net);
577 }
578 
579 
580 static void
NetNonblockingIO(fd,onoff)581 NetNonblockingIO(fd, onoff)				/* unix */
582 int
583 	fd,
584 	onoff;
585 {
586     ioctl(net, FIONBIO, (char *)&onoff);
587 }
588 
589 static void
NetSigIO(fd,onoff)590 NetSigIO(fd, onoff)				/* unix */
591 int
592 	fd,
593 	onoff;
594 {
595     ioctl(net, FIOASYNC, (char *)&onoff);	/* hear about input */
596 }
597 
598 static void
NetSetPgrp(fd)599 NetSetPgrp(fd)				/* unix */
600 int fd;
601 {
602     int myPid;
603 
604     myPid = getpid();
605 #if	defined(NOT43)
606     myPid = -myPid;
607 #endif	/* defined(NOT43) */
608     ioctl(net, SIOCSPGRP, (char *)&myPid);	/* set my pid */
609 }
610 
611 
612 #endif	/* defined(unix) */
613 
614 #if	defined(MSDOS)
615 #include <time.h>
616 #include <signal.h>
617 #include <process.h>
618 #include <fcntl.h>
619 #include <io.h>
620 #include <dos.h>
621 
622 #if	!defined(SO_OOBINLINE)
623 #define	SO_OOBINLINE
624 #endif	/* !defined(SO_OOBINLINE) */
625 
626 
627 static char
628     termEofChar,
629     termEraseChar,
630     termFlushChar,
631     termIntChar,
632     termKillChar,
633     termLiteralNextChar,
634     termQuitChar;
635 
636 
637 /*
638  * MSDOS doesn't have anyway of deciding whether a full-edited line
639  * is ready to be read in, so we need to do character-by-character
640  * reads, and then do the editing in the program (in the case where
641  * we are supporting line-by-line mode).
642  *
643  * The following routines, which are internal to the MSDOS-specific
644  * code, accomplish this miracle.
645  */
646 
647 #define Hex(c)	HEX[(c)&0xff]
648 
649 static survivorSetup = 0;		/* Do we have ^C hooks in? */
650 
651 static int
652 	lineend = 0,		/* There is a line terminator */
653 	ctrlCCount = 0;
654 
655 static char	linein[200],		/* Where input line is assembled */
656 		*nextin = linein,	/* Next input character */
657 		*nextout = linein;	/* Next character to be consumed */
658 
659 #define consumechar() \
660     if ((++nextout) >= nextin) { \
661 	nextout = nextin = linein; \
662 	lineend = 0; \
663     }
664 
665 #define	characteratatime()	(!MODE_LINE(globalmode))	/* one by one */
666 
667 
668 /*
669  * killone()
670  *
671  *  Erase the last character on the line.
672  */
673 
674 static void
killone()675 killone()
676 {
677     if (lineend) {
678 	return;			/* ??? XXX */
679     }
680     if (nextin == linein) {
681 	return;			/* Nothing to do */
682     }
683     nextin--;
684     if (!(isspace(*nextin) || isprint(*nextin))) {
685 	putchar('\b');
686 	putchar(' ');
687 	putchar('\b');
688     }
689     putchar('\b');
690     putchar(' ');
691     putchar('\b');
692 }
693 
694 
695 /*
696  * setlineend()
697  *
698  *  Decide if it's time to send the current line up to the user
699  * process.
700  */
701 
702 static void
setlineend()703 setlineend()
704 {
705     if (nextin == nextout) {
706 	return;
707     }
708     if (characteratatime()) {
709 	lineend = 1;
710     } else if (nextin >= (linein+sizeof linein)) {
711 	lineend = 1;
712     } else {
713 	int c = *(nextin-1);
714 	if ((c == termIntChar)
715 		|| (c == termQuitChar)
716 		|| (c == termEofChar)) {
717 	    lineend = 1;
718 	} else if (c == termFlushChar) {
719 	    lineend = 1;
720 	} else if ((c == '\n') || (c == '\r')) {
721 	    lineend = 1;
722 	}
723     }
724     /* Otherwise, leave it alone (reset by 'consumechar') */
725 }
726 
727 /*
728  * OK, what we do here is:
729  *
730  *   o  If we are echoing, then
731  *	o  Look for character erase, line kill characters
732  *	o  Echo the character (using '^' if a control character)
733  *   o  Put the character in the input buffer
734  *   o  Set 'lineend' as necessary
735  */
736 
737 static void
DoNextChar(c)738 DoNextChar(c)
739 int	c;			/* Character to process */
740 {
741     static char literalnextcharacter = 0;
742 
743     if (nextin >= (linein+sizeof linein)) {
744 	putchar('\7');		/* Ring bell */
745 	setlineend();
746 	return;
747     }
748     if (MODE_LOCAL_CHARS(globalmode)) {
749 	/* Look for some special character */
750 	if (!literalnextcharacter) {
751 	    if (c == termEraseChar) {
752 		killone();
753 		setlineend();
754 		return;
755 	    } else if (c == termKillChar) {
756 		while (nextin != linein) {
757 		    killone();
758 		}
759 		setlineend();
760 		return;
761 	    } else if (c == termLiteralNextChar) {
762 		literalnextcharacter = 1;
763 		return;
764 	    }
765 	}
766 
767 	if (MODE_LOCAL_ECHO(globalmode)) {
768 	    if ((literalnextcharacter == 0) && ((c == '\r') || (c == '\n'))) {
769 		putchar('\r');
770 		putchar('\n');
771 		c = '\n';
772 	    } else if (!isprint(c) && !isspace(c)) {
773 		putchar('^');
774 		putchar(c^0x40);
775 	    } else {
776 		putchar(c);
777 	    }
778 	}
779 	literalnextcharacter = 0;
780     }
781     *nextin++ = c;
782     setlineend();
783 }
784 
785 static int
inputExists()786 inputExists()
787 {
788     int input;
789     static state = 0;
790 
791     while (ctrlCCount) {
792 	DoNextChar(0x03);
793 	ctrlCCount--;
794     }
795     if (lineend) {
796 	return 1;
797     }
798 #if	1	/* For BIOS variety of calls */
799     if (kbhit() == 0) {
800 	return lineend;
801     }
802     input = getch();			/* MSC - get console character */
803     if ((input&0xff) == 0) {
804 	DoNextChar(0x01);		/* ^A */
805     } else {
806 	DoNextChar(input&0xff);
807     }
808 #else	/* 0 */
809     if ((input = dirconio()) == -1) {
810 	return lineend;
811     }
812     if ((input&0xff) == 0) {
813 	if ((input&0xff00) == 0x0300) {		/* Null */
814 	    DoNextChar(0);
815 	} else {
816 	    DoNextChar(0x01);
817 	    if (input&0x8000) {
818 		DoNextChar(0x01);
819 		DoNextChar((input>>8)&0x7f);
820 	    } else {
821 		DoNextChar((input>>8)&0xff);
822 	    }
823 	}
824     } else {
825 	DoNextChar(input&0xff);
826     }
827 #endif	/* 0 */
828     return lineend;
829 }
830 
831 
832 void
CtrlCInterrupt()833 CtrlCInterrupt()
834 {
835     if (!MODE_COMMAND_LINE(globalmode)) {
836 	ctrlCCount++;		/* XXX */
837 	signal(SIGINT, CtrlCInterrupt);
838     } else {
839 	closeallsockets();
840 	exit(1);
841     }
842 }
843 
844 /*
845  * The MSDOS routines, called from elsewhere.
846  */
847 
848 
849 static int
TerminalAutoFlush()850 TerminalAutoFlush()				/* MSDOS */
851 {
852     return 1;
853 }
854 
855 static int
TerminalCanRead()856 TerminalCanRead()
857 {
858     return inputExists();
859 }
860 
861 
862 /*
863  * Flush output to the terminal
864  */
865 
866 static void
TerminalFlushOutput()867 TerminalFlushOutput()				/* MSDOS */
868 {
869 }
870 
871 
872 static void
TerminalNewMode(f)873 TerminalNewMode(f)				/* MSDOS */
874 register int f;
875 {
876     union REGS inregs;
877     struct SREGS segregs;
878     static old_1b_offset = 0, old_1b_segment = 0;
879 
880     globalmode = f;
881     if (MODE_COMMAND_LINE(f)) {
882 	signal(SIGINT, SIG_DFL);
883 	if (old_1b_segment|old_1b_offset) {
884 	    inregs.h.ah = 0x25;
885 	    inregs.h.al = 0x1b;
886 	    inregs.x.dx = old_1b_offset;
887 	    segregs.ds = old_1b_segment;
888 	    intdosx(&inregs, &inregs, &segregs);
889 	    old_1b_segment = old_1b_offset = 0;
890 	}
891 	if (setmode(fileno(stdout), O_TEXT) == -1) {
892 	    ExitPerror("setmode (text)", 1);
893 	}
894 	if (setmode(fileno(stdin), O_TEXT) == -1) {
895 	    ExitPerror("setmode (text)", 1);
896 	}
897     } else {
898 	signal(SIGINT, CtrlCInterrupt);
899 	if ((old_1b_segment|old_1b_offset) == 0) {
900 	    extern void iret_subr();
901 	    void (far *foo_subr)() = iret_subr;
902 
903 	    inregs.h.ah = 0x35;
904 	    inregs.h.al = 0x1b;
905 	    intdosx(&inregs, &inregs, &segregs);
906 	    old_1b_segment = segregs.es;
907 	    old_1b_offset = inregs.x.bx;
908 	    inregs.h.ah = 0x25;
909 	    inregs.h.al = 0x1b;
910 	    inregs.x.dx = FP_OFF(foo_subr);
911 	    segregs.ds = FP_SEG(foo_subr);
912 	    intdosx(&inregs, &inregs, &segregs);
913 	}
914 	if (setmode(fileno(stdout), O_BINARY) == -1) {
915 	    ExitPerror("setmode (binary)", 1);
916 	}
917 	if (setmode(fileno(stdin), O_BINARY) == -1) {
918 	    ExitPerror("setmode (binary)", 1);
919 	}
920     }
921 }
922 
923 
924 int
TerminalRead(fd,buffer,count)925 TerminalRead(fd, buffer, count)
926 int	fd;
927 char	*buffer;
928 int	count;
929 {
930     int done = 0;
931 
932     for (;;) {
933 	while (inputExists() && (done < count)) {
934 	    *buffer++ = *nextout;
935 	    consumechar();
936 	    done++;
937 	}
938 	if (done) {
939 	    return(done);
940 	} else {
941 	    return 0;
942 	}
943     }
944 }
945 
946 
947 static void
TerminalSaveState()948 TerminalSaveState()				/* MSDOS */
949 {
950 }
951 
952 int
TerminalSpecialChars(c)953 TerminalSpecialChars(c)			/* MSDOS */
954 {
955     return 1;
956 }
957 
958 
959 static void
TerminalRestoreState()960 TerminalRestoreState()				/* MSDOS */
961 {
962 }
963 
964 
965 static int
TerminalWrite(fd,buffer,count)966 TerminalWrite(fd, buffer, count)		/* MSDOS */
967 int	fd;
968 char	*buffer;
969 int	count;
970 {
971     return fwrite(buffer, sizeof (char), count, stdout);
972 }
973 
974 
975 static int
NetClose(fd)976 NetClose(fd)
977 {
978     return closesocket(fd);
979 }
980 
981 static void
NetNonblockingIO(fd,onoff)982 NetNonblockingIO(fd, onoff)				/* MSDOS */
983 int
984 	fd,
985 	onoff;
986 {
987     if (SetSockOpt(net, SOL_SOCKET, SO_NONBLOCKING, onoff)) {
988 	perror("setsockop (SO_NONBLOCKING) ");
989 	ExitString(stderr, "exiting\n", 1);
990     }
991 }
992 
993 static void
NetSigIO(fd)994 NetSigIO(fd)				/* MSDOS */
995 int fd;
996 {
997 }
998 
999 static void
NetSetPgrp(fd)1000 NetSetPgrp(fd)				/* MSDOS */
1001 int fd;
1002 {
1003 }
1004 
1005 
1006 #endif	/* defined(MSDOS) */
1007 
1008 /*
1009  * Initialize variables.
1010  */
1011 
1012 static void
tninit()1013 tninit()
1014 {
1015 #if	defined(TN3270)
1016     Sent3270TerminalType = 0;
1017     Ifrontp = Ibackp = Ibuf;
1018 #endif	/* defined(TN3270) */
1019 
1020     tfrontp = tbackp = ttyobuf;
1021     nfrontp = nbackp = netobuf;
1022 
1023     /* Don't change telnetport */
1024     SB_CLEAR();
1025     ClearArray(hisopts);
1026     ClearArray(myopts);
1027     sbp = sibuf;
1028     tbp = tibuf;
1029 
1030     connected = net = scc = tcc = In3270 = ISend = donebinarytoggle = 0;
1031     telnetport = 0;
1032 #if	defined(unix)
1033     HaveInput = 0;
1034 #endif	/* defined(unix) */
1035 
1036     SYNCHing = 0;
1037 
1038     errno = 0;
1039 
1040     flushline = 0;
1041 
1042     /* Don't change NetTrace */
1043 
1044     escape = CONTROL(']');
1045     echoc = CONTROL('E');
1046 
1047     flushline = 1;
1048     sp = getservbyname("telnet", "tcp");
1049     if (sp == 0) {
1050 	ExitString(stderr, "telnet: tcp/telnet: unknown service\n",1);
1051 	/*NOTREACHED*/
1052     }
1053 
1054 #if	defined(TN3270)
1055     init_ctlr();		/* Initialize some things */
1056     init_keyboard();
1057     init_screen();
1058     init_system();
1059 #endif	/* defined(TN3270) */
1060 }
1061 
1062 /*
1063  * Various utility routines.
1064  */
1065 
1066 static void
makeargv()1067 makeargv()
1068 {
1069     register char *cp;
1070     register char **argp = margv;
1071 
1072     margc = 0;
1073     cp = line;
1074     if (*cp == '!') {		/* Special case shell escape */
1075 	*argp++ = "!";		/* No room in string to get this */
1076 	margc++;
1077 	cp++;
1078     }
1079     while (*cp) {
1080 	while (isspace(*cp))
1081 	    cp++;
1082 	if (*cp == '\0')
1083 	    break;
1084 	*argp++ = cp;
1085 	margc += 1;
1086 	while (*cp != '\0' && !isspace(*cp))
1087 	    cp++;
1088 	if (*cp == '\0')
1089 	    break;
1090 	*cp++ = '\0';
1091     }
1092     *argp++ = 0;
1093 }
1094 
1095 static char *ambiguous;		/* special return value */
1096 #define Ambiguous(t)	((t)&ambiguous)
1097 
1098 
1099 static char **
genget(name,table,next)1100 genget(name, table, next)
1101 char	*name;		/* name to match */
1102 char	**table;		/* name entry in table */
1103 char	**(*next)();	/* routine to return next entry in table */
1104 {
1105 	register char *p, *q;
1106 	register char **c, **found;
1107 	register int nmatches, longest;
1108 
1109 	if (name == 0) {
1110 	    return 0;
1111 	}
1112 	longest = 0;
1113 	nmatches = 0;
1114 	found = 0;
1115 	for (c = table; (p = *c) != 0; c = (*next)(c)) {
1116 		for (q = name;
1117 		    (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++)
1118 			if (*q == 0)		/* exact match? */
1119 				return (c);
1120 		if (!*q) {			/* the name was a prefix */
1121 			if (q - name > longest) {
1122 				longest = q - name;
1123 				nmatches = 1;
1124 				found = c;
1125 			} else if (q - name == longest)
1126 				nmatches++;
1127 		}
1128 	}
1129 	if (nmatches > 1)
1130 		return Ambiguous(char **);
1131 	return (found);
1132 }
1133 
1134 /*
1135  * Make a character string into a number.
1136  *
1137  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
1138  */
1139 
1140 static
special(s)1141 special(s)
1142 register char *s;
1143 {
1144 	register char c;
1145 	char b;
1146 
1147 	switch (*s) {
1148 	case '^':
1149 		b = *++s;
1150 		if (b == '?') {
1151 		    c = b | 0x40;		/* DEL */
1152 		} else {
1153 		    c = b & 0x1f;
1154 		}
1155 		break;
1156 	default:
1157 		c = *s;
1158 		break;
1159 	}
1160 	return c;
1161 }
1162 
1163 /*
1164  * Construct a control character sequence
1165  * for a special character.
1166  */
1167 static char *
control(c)1168 control(c)
1169 	register int c;
1170 {
1171 	static char buf[3];
1172 
1173 	if (c == 0x7f)
1174 		return ("^?");
1175 	if (c == '\377') {
1176 		return "off";
1177 	}
1178 	if (c >= 0x20) {
1179 		buf[0] = c;
1180 		buf[1] = 0;
1181 	} else {
1182 		buf[0] = '^';
1183 		buf[1] = '@'+c;
1184 		buf[2] = 0;
1185 	}
1186 	return (buf);
1187 }
1188 
1189 
1190 /*
1191  * upcase()
1192  *
1193  *	Upcase (in place) the argument.
1194  */
1195 
1196 static void
upcase(argument)1197 upcase(argument)
1198 register char *argument;
1199 {
1200     register int c;
1201 
1202     while ((c = *argument) != 0) {
1203 	if (islower(c)) {
1204 	    *argument = toupper(c);
1205 	}
1206 	argument++;
1207     }
1208 }
1209 
1210 /*
1211  * SetSockOpt()
1212  *
1213  * Compensate for differences in 4.2 and 4.3 systems.
1214  */
1215 
1216 static int
SetSockOpt(fd,level,option,yesno)1217 SetSockOpt(fd, level, option, yesno)
1218 int
1219 	fd,
1220 	level,
1221 	option,
1222 	yesno;
1223 {
1224 #ifndef	NOT43
1225     return setsockopt(fd, level, option,
1226 				(char *)&yesno, sizeof yesno);
1227 #else	/* NOT43 */
1228     if (yesno == 0) {		/* Can't do that in 4.2! */
1229 	fprintf(stderr, "Error: attempt to turn off an option 0x%x.\n",
1230 				option);
1231 	return -1;
1232     }
1233     return setsockopt(fd, level, option, 0, 0);
1234 #endif	/* NOT43 */
1235 }
1236 
1237 /*
1238  * The following are routines used to print out debugging information.
1239  */
1240 
1241 
1242 static void
Dump(direction,buffer,length)1243 Dump(direction, buffer, length)
1244 char	direction;
1245 char	*buffer;
1246 int	length;
1247 {
1248 #   define BYTES_PER_LINE	32
1249 #   define min(x,y)	((x<y)? x:y)
1250     char *pThis;
1251     int offset;
1252 
1253     offset = 0;
1254 
1255     while (length) {
1256 	/* print one line */
1257 	fprintf(NetTrace, "%c 0x%x\t", direction, offset);
1258 	pThis = buffer;
1259 	buffer = buffer+min(length, BYTES_PER_LINE);
1260 	while (pThis < buffer) {
1261 	    fprintf(NetTrace, "%.2x", (*pThis)&0xff);
1262 	    pThis++;
1263 	}
1264 	fprintf(NetTrace, "\n");
1265 	length -= BYTES_PER_LINE;
1266 	offset += BYTES_PER_LINE;
1267 	if (length < 0) {
1268 	    return;
1269 	}
1270 	/* find next unique line */
1271     }
1272 }
1273 
1274 
1275 /*VARARGS*/
1276 static void
printoption(direction,fmt,option,what)1277 printoption(direction, fmt, option, what)
1278 	char *direction, *fmt;
1279 	int option, what;
1280 {
1281 	if (!showoptions)
1282 		return;
1283 	fprintf(NetTrace, "%s ", direction+1);
1284 	if (fmt == doopt)
1285 		fmt = "do";
1286 	else if (fmt == dont)
1287 		fmt = "dont";
1288 	else if (fmt == will)
1289 		fmt = "will";
1290 	else if (fmt == wont)
1291 		fmt = "wont";
1292 	else
1293 		fmt = "???";
1294 	if (option < (sizeof telopts/sizeof telopts[0]))
1295 		fprintf(NetTrace, "%s %s", fmt, telopts[option]);
1296 	else
1297 		fprintf(NetTrace, "%s %d", fmt, option);
1298 	if (*direction == '<') {
1299 		fprintf(NetTrace, "\r\n");
1300 		return;
1301 	}
1302 	fprintf(NetTrace, " (%s)\r\n", what ? "reply" : "don't reply");
1303 }
1304 
1305 static void
printsub(direction,pointer,length)1306 printsub(direction, pointer, length)
1307 char	*direction,		/* "<" or ">" */
1308 	*pointer;		/* where suboption data sits */
1309 int	length;			/* length of suboption data */
1310 {
1311     if (showoptions) {
1312 	fprintf(NetTrace, "%s suboption ",
1313 				(direction[0] == '<')? "Received":"Sent");
1314 	switch (pointer[0]) {
1315 	case TELOPT_TTYPE:
1316 	    fprintf(NetTrace, "Terminal type ");
1317 	    switch (pointer[1]) {
1318 	    case TELQUAL_IS:
1319 		{
1320 		    char tmpbuf[sizeof subbuffer];
1321 		    int minlen = min(length, sizeof tmpbuf);
1322 
1323 		    memcpy(tmpbuf, pointer+2, minlen);
1324 		    tmpbuf[minlen-1] = 0;
1325 		    fprintf(NetTrace, "is %s.\n", tmpbuf);
1326 		}
1327 		break;
1328 	    case TELQUAL_SEND:
1329 		fprintf(NetTrace, "- request to send.\n");
1330 		break;
1331 	    default:
1332 		fprintf(NetTrace,
1333 				"- unknown qualifier %d (0x%x).\n", pointer[1]);
1334 	    }
1335 	    break;
1336 	default:
1337 	    fprintf(NetTrace, "Unknown option %d (0x%x)\n",
1338 					pointer[0], pointer[0]);
1339 	}
1340     }
1341 }
1342 
1343 /*
1344  * Check to see if any out-of-band data exists on a socket (for
1345  * Telnet "synch" processing).
1346  */
1347 
1348 static int
stilloob(s)1349 stilloob(s)
1350 int	s;		/* socket number */
1351 {
1352     static struct timeval timeout = { 0 };
1353     fd_set	excepts;
1354     int value;
1355 
1356     do {
1357 	FD_ZERO(&excepts);
1358 	FD_SET(s, &excepts);
1359 	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
1360     } while ((value == -1) && (errno == EINTR));
1361 
1362     if (value < 0) {
1363 	perror("select");
1364 	quit();
1365     }
1366     if (FD_ISSET(s, &excepts)) {
1367 	return 1;
1368     } else {
1369 	return 0;
1370     }
1371 }
1372 
1373 
1374 /*
1375  *  netflush
1376  *		Send as much data as possible to the network,
1377  *	handling requests for urgent data.
1378  *
1379  *		The return value indicates whether we did any
1380  *	useful work.
1381  */
1382 
1383 
1384 int
netflush()1385 netflush()
1386 {
1387     int n;
1388 
1389     if ((n = nfrontp - nbackp) > 0) {
1390 	if (!neturg) {
1391 	    n = send(net, nbackp, n, 0);	/* normal write */
1392 	} else {
1393 	    n = neturg - nbackp;
1394 	    /*
1395 	     * In 4.2 (and 4.3) systems, there is some question about
1396 	     * what byte in a sendOOB operation is the "OOB" data.
1397 	     * To make ourselves compatible, we only send ONE byte
1398 	     * out of band, the one WE THINK should be OOB (though
1399 	     * we really have more the TCP philosophy of urgent data
1400 	     * rather than the Unix philosophy of OOB data).
1401 	     */
1402 	    if (n > 1) {
1403 		n = send(net, nbackp, n-1, 0);	/* send URGENT all by itself */
1404 	    } else {
1405 		n = send(net, nbackp, n, MSG_OOB);	/* URGENT data */
1406 	    }
1407 	}
1408     }
1409     if (n < 0) {
1410 	if (errno != ENOBUFS && errno != EWOULDBLOCK) {
1411 	    setcommandmode();
1412 	    perror(hostname);
1413 	    NetClose(net);
1414 	    neturg = 0;
1415 	    longjmp(peerdied, -1);
1416 	    /*NOTREACHED*/
1417 	}
1418 	n = 0;
1419     }
1420     if (netdata && n) {
1421 	Dump('>', nbackp, n);
1422     }
1423     nbackp += n;
1424     if (nbackp >= neturg) {
1425 	neturg = 0;
1426     }
1427     if (nbackp == nfrontp) {
1428 	nbackp = nfrontp = netobuf;
1429     }
1430     return n > 0;
1431 }
1432 
1433 /*
1434  * nextitem()
1435  *
1436  *	Return the address of the next "item" in the TELNET data
1437  * stream.  This will be the address of the next character if
1438  * the current address is a user data character, or it will
1439  * be the address of the character following the TELNET command
1440  * if the current address is a TELNET IAC ("I Am a Command")
1441  * character.
1442  */
1443 
1444 static char *
nextitem(current)1445 nextitem(current)
1446 char	*current;
1447 {
1448     if ((*current&0xff) != IAC) {
1449 	return current+1;
1450     }
1451     switch (*(current+1)&0xff) {
1452     case DO:
1453     case DONT:
1454     case WILL:
1455     case WONT:
1456 	return current+3;
1457     case SB:		/* loop forever looking for the SE */
1458 	{
1459 	    register char *look = current+2;
1460 
1461 	    for (;;) {
1462 		if ((*look++&0xff) == IAC) {
1463 		    if ((*look++&0xff) == SE) {
1464 			return look;
1465 		    }
1466 		}
1467 	    }
1468 	}
1469     default:
1470 	return current+2;
1471     }
1472 }
1473 /*
1474  * netclear()
1475  *
1476  *	We are about to do a TELNET SYNCH operation.  Clear
1477  * the path to the network.
1478  *
1479  *	Things are a bit tricky since we may have sent the first
1480  * byte or so of a previous TELNET command into the network.
1481  * So, we have to scan the network buffer from the beginning
1482  * until we are up to where we want to be.
1483  *
1484  *	A side effect of what we do, just to keep things
1485  * simple, is to clear the urgent data pointer.  The principal
1486  * caller should be setting the urgent data pointer AFTER calling
1487  * us in any case.
1488  */
1489 
1490 static void
netclear()1491 netclear()
1492 {
1493     register char *thisitem, *next;
1494     char *good;
1495 #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
1496 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
1497 
1498     thisitem = netobuf;
1499 
1500     while ((next = nextitem(thisitem)) <= nbackp) {
1501 	thisitem = next;
1502     }
1503 
1504     /* Now, thisitem is first before/at boundary. */
1505 
1506     good = netobuf;	/* where the good bytes go */
1507 
1508     while (nfrontp > thisitem) {
1509 	if (wewant(thisitem)) {
1510 	    int length;
1511 
1512 	    next = thisitem;
1513 	    do {
1514 		next = nextitem(next);
1515 	    } while (wewant(next) && (nfrontp > next));
1516 	    length = next-thisitem;
1517 	    memcpy(good, thisitem, length);
1518 	    good += length;
1519 	    thisitem = next;
1520 	} else {
1521 	    thisitem = nextitem(thisitem);
1522 	}
1523     }
1524 
1525     nbackp = netobuf;
1526     nfrontp = good;		/* next byte to be sent */
1527     neturg = 0;
1528 }
1529 
1530 /*
1531  * These routines add various telnet commands to the data stream.
1532  */
1533 
1534 #if	defined(NOT43)
1535 static int
1536 #else	/* defined(NOT43) */
1537 static void
1538 #endif	/* defined(NOT43) */
dosynch()1539 dosynch()
1540 {
1541     netclear();			/* clear the path to the network */
1542     NET2ADD(IAC, DM);
1543     neturg = NETLOC()-1;	/* Some systems are off by one XXX */
1544 
1545 #if	defined(NOT43)
1546     return 0;
1547 #endif	/* defined(NOT43) */
1548 }
1549 
1550 static void
doflush()1551 doflush()
1552 {
1553     NET2ADD(IAC, DO);
1554     NETADD(TELOPT_TM);
1555     flushline = 1;
1556     flushout = 1;
1557     ttyflush();
1558     /* do printoption AFTER flush, otherwise the output gets tossed... */
1559     printoption("<SENT", doopt, TELOPT_TM, 0);
1560 }
1561 
1562 static void
intp()1563 intp()
1564 {
1565     NET2ADD(IAC, IP);
1566     if (autoflush) {
1567 	doflush();
1568     }
1569     if (autosynch) {
1570 	dosynch();
1571     }
1572 }
1573 
1574 static void
sendbrk()1575 sendbrk()
1576 {
1577     NET2ADD(IAC, BREAK);
1578     if (autoflush) {
1579 	doflush();
1580     }
1581     if (autosynch) {
1582 	dosynch();
1583     }
1584 }
1585 
1586 /*
1587  *		Send as much data as possible to the terminal.
1588  *
1589  *		The return value indicates whether we did any
1590  *	useful work.
1591  */
1592 
1593 
1594 static int
ttyflush()1595 ttyflush()
1596 {
1597     int n;
1598 
1599     if ((n = tfrontp - tbackp) > 0) {
1600 	if (!(SYNCHing||flushout)) {
1601 	    n = TerminalWrite(tout, tbackp, n);
1602 	} else {
1603 	    TerminalFlushOutput();
1604 	    /* we leave 'n' alone! */
1605 	}
1606     }
1607     if (n >= 0) {
1608 	tbackp += n;
1609 	if (tbackp == tfrontp) {
1610 	    tbackp = tfrontp = ttyobuf;
1611 	}
1612     }
1613     return n > 0;
1614 }
1615 
1616 #if	defined(TN3270)
1617 
1618 #if	defined(unix)
1619 static void
inputAvailable()1620 inputAvailable()
1621 {
1622     HaveInput = 1;
1623 }
1624 #endif	/* defined(unix) */
1625 
1626 void
outputPurge()1627 outputPurge()
1628 {
1629     int tmp = flushout;
1630 
1631     flushout = 1;
1632 
1633     ttyflush();
1634 
1635     flushout = tmp;
1636 }
1637 
1638 #endif	/* defined(TN3270) */
1639 
1640 #if	defined(unix)
1641 /*
1642  * Various signal handling routines.
1643  */
1644 
1645 static void
deadpeer()1646 deadpeer()
1647 {
1648 	setcommandmode();
1649 	longjmp(peerdied, -1);
1650 }
1651 
1652 static void
intr()1653 intr()
1654 {
1655     if (localchars) {
1656 	intp();
1657 	return;
1658     }
1659     setcommandmode();
1660     longjmp(toplevel, -1);
1661 }
1662 
1663 static void
intr2()1664 intr2()
1665 {
1666     if (localchars) {
1667 	sendbrk();
1668 	return;
1669     }
1670 }
1671 
1672 static void
doescape()1673 doescape()
1674 {
1675     command(0);
1676 }
1677 #endif	/* defined(unix) */
1678 
1679 /*
1680  * These routines decides on what the mode should be (based on the values
1681  * of various global variables).
1682  */
1683 
1684 
1685 static
getconnmode()1686 getconnmode()
1687 {
1688     static char newmode[16] =
1689 			{ 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 };
1690     int modeindex = 0;
1691 
1692     if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
1693 	modeindex += 1;
1694     }
1695     if (hisopts[TELOPT_ECHO]) {
1696 	modeindex += 2;
1697     }
1698     if (hisopts[TELOPT_SGA]) {
1699 	modeindex += 4;
1700     }
1701     if (In3270) {
1702 	modeindex += 8;
1703     }
1704     return newmode[modeindex];
1705 }
1706 
1707 void
setconnmode()1708 setconnmode()
1709 {
1710     TerminalNewMode(getconnmode());
1711 }
1712 
1713 
1714 void
setcommandmode()1715 setcommandmode()
1716 {
1717     TerminalNewMode(0);
1718 }
1719 
1720 static void
willoption(option,reply)1721 willoption(option, reply)
1722 	int option, reply;
1723 {
1724 	char *fmt;
1725 
1726 	switch (option) {
1727 
1728 	case TELOPT_ECHO:
1729 #	if defined(TN3270)
1730 	    /*
1731 	     * The following is a pain in the rear-end.
1732 	     * Various IBM servers (some versions of Wiscnet,
1733 	     * possibly Fibronics/Spartacus, and who knows who
1734 	     * else) will NOT allow us to send "DO SGA" too early
1735 	     * in the setup proceedings.  On the other hand,
1736 	     * 4.2 servers (telnetd) won't set SGA correctly.
1737 	     * So, we are stuck.  Empirically (but, based on
1738 	     * a VERY small sample), the IBM servers don't send
1739 	     * out anything about ECHO, so we postpone our sending
1740 	     * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
1741 	     * DO send).
1742 	     */
1743 	    {
1744 		if (askedSGA == 0) {
1745 		    askedSGA = 1;
1746 		    if (!hisopts[TELOPT_SGA]) {
1747 			willoption(TELOPT_SGA, 0);
1748 		    }
1749 		}
1750 	    }
1751 		/* Fall through */
1752 	case TELOPT_EOR:
1753 	case TELOPT_BINARY:
1754 #endif	/* defined(TN3270) */
1755 	case TELOPT_SGA:
1756 		settimer(modenegotiated);
1757 		hisopts[option] = 1;
1758 		fmt = doopt;
1759 		setconnmode();		/* possibly set new tty mode */
1760 		break;
1761 
1762 	case TELOPT_TM:
1763 		return;			/* Never reply to TM will's/wont's */
1764 
1765 	default:
1766 		fmt = dont;
1767 		break;
1768 	}
1769 	sprintf(nfrontp, fmt, option);
1770 	nfrontp += sizeof (dont) - 2;
1771 	if (reply)
1772 		printoption(">SENT", fmt, option, reply);
1773 	else
1774 		printoption("<SENT", fmt, option, reply);
1775 }
1776 
1777 static void
wontoption(option,reply)1778 wontoption(option, reply)
1779 	int option, reply;
1780 {
1781 	char *fmt;
1782 
1783 	switch (option) {
1784 
1785 	case TELOPT_ECHO:
1786 	case TELOPT_SGA:
1787 		settimer(modenegotiated);
1788 		hisopts[option] = 0;
1789 		fmt = dont;
1790 		setconnmode();			/* Set new tty mode */
1791 		break;
1792 
1793 	case TELOPT_TM:
1794 		return;		/* Never reply to TM will's/wont's */
1795 
1796 	default:
1797 		fmt = dont;
1798 	}
1799 	sprintf(nfrontp, fmt, option);
1800 	nfrontp += sizeof (doopt) - 2;
1801 	if (reply)
1802 		printoption(">SENT", fmt, option, reply);
1803 	else
1804 		printoption("<SENT", fmt, option, reply);
1805 }
1806 
1807 static void
dooption(option)1808 dooption(option)
1809 	int option;
1810 {
1811 	char *fmt;
1812 
1813 	switch (option) {
1814 
1815 	case TELOPT_TM:
1816 		fmt = will;
1817 		break;
1818 
1819 #	if defined(TN3270)
1820 	case TELOPT_EOR:
1821 	case TELOPT_BINARY:
1822 #	endif	/* defined(TN3270) */
1823 	case TELOPT_TTYPE:		/* terminal type option */
1824 	case TELOPT_SGA:		/* no big deal */
1825 		fmt = will;
1826 		myopts[option] = 1;
1827 		break;
1828 
1829 	case TELOPT_ECHO:		/* We're never going to echo... */
1830 	default:
1831 		fmt = wont;
1832 		break;
1833 	}
1834 	sprintf(nfrontp, fmt, option);
1835 	nfrontp += sizeof (doopt) - 2;
1836 	printoption(">SENT", fmt, option, 0);
1837 }
1838 
1839 /*
1840  * suboption()
1841  *
1842  *	Look at the sub-option buffer, and try to be helpful to the other
1843  * side.
1844  *
1845  *	Currently we recognize:
1846  *
1847  *		Terminal type, send request.
1848  */
1849 
1850 static void
suboption()1851 suboption()
1852 {
1853     printsub("<", subbuffer, subend-subbuffer+1);
1854     switch (subbuffer[0]&0xff) {
1855     case TELOPT_TTYPE:
1856 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
1857 	    ;
1858 	} else {
1859 	    char *name;
1860 	    char namebuf[41];
1861 	    extern char *getenv();
1862 	    int len;
1863 
1864 #if	defined(TN3270)
1865 	    /*
1866 	     * Try to send a 3270 type terminal name.  Decide which one based
1867 	     * on the format of our screen, and (in the future) color
1868 	     * capaiblities.
1869 	     */
1870 #if	defined(unix)
1871 	    if (initscr() != ERR) {	/* Initialize curses to get line size */
1872 		MaxNumberLines = LINES;
1873 		MaxNumberColumns = COLS;
1874 	    }
1875 #else	/* defined(unix) */
1876 	    InitTerminal();
1877 #endif	/* defined(unix) */
1878 	    if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) {
1879 		Sent3270TerminalType = 1;
1880 		if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) {
1881 		    MaxNumberLines = 27;
1882 		    MaxNumberColumns = 132;
1883 		    sb_terminal[SBTERMMODEL] = '5';
1884 		} else if (MaxNumberLines >= 43) {
1885 		    MaxNumberLines = 43;
1886 		    MaxNumberColumns = 80;
1887 		    sb_terminal[SBTERMMODEL] = '4';
1888 		} else if (MaxNumberLines >= 32) {
1889 		    MaxNumberLines = 32;
1890 		    MaxNumberColumns = 80;
1891 		    sb_terminal[SBTERMMODEL] = '3';
1892 		} else {
1893 		    MaxNumberLines = 24;
1894 		    MaxNumberColumns = 80;
1895 		    sb_terminal[SBTERMMODEL] = '2';
1896 		}
1897 		NumberLines = 24;		/* before we start out... */
1898 		NumberColumns = 80;
1899 		ScreenSize = NumberLines*NumberColumns;
1900 		if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) {
1901 		    ExitString(stderr,
1902 			"Programming error:  MAXSCREENSIZE too small.\n", 1);
1903 		    /*NOTREACHED*/
1904 		}
1905 		memcpy(nfrontp, sb_terminal, sizeof sb_terminal);
1906 		printsub(">", nfrontp+2, sizeof sb_terminal-2);
1907 		nfrontp += sizeof sb_terminal;
1908 		return;
1909 	    }
1910 #endif	/* defined(TN3270) */
1911 
1912 	    name = getenv("TERM");
1913 	    if ((name == 0) || ((len = strlen(name)) > 40)) {
1914 		name = "UNKNOWN";
1915 	    }
1916 	    if ((len + 4+2) < NETROOM()) {
1917 		strcpy(namebuf, name);
1918 		upcase(namebuf);
1919 		sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
1920 				    TELQUAL_IS, namebuf, IAC, SE);
1921 		printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2);
1922 		nfrontp += 4+strlen(namebuf)+2;
1923 	    } else {
1924 		ExitString(stderr, "No room in buffer for terminal type.\n",
1925 							1);
1926 		/*NOTREACHED*/
1927 	    }
1928 	}
1929 
1930     default:
1931 	break;
1932     }
1933 }
1934 
1935 #if	defined(TN3270)
1936 static void
SetIn3270()1937 SetIn3270()
1938 {
1939     if (Sent3270TerminalType && myopts[TELOPT_BINARY]
1940 			    && hisopts[TELOPT_BINARY] && !donebinarytoggle) {
1941 	if (!In3270) {
1942 	    In3270 = 1;
1943 	    Init3270();		/* Initialize 3270 functions */
1944 	    /* initialize terminal key mapping */
1945 	    InitTerminal();	/* Start terminal going */
1946 	    setconnmode();
1947 	}
1948     } else {
1949 	if (In3270) {
1950 	    StopScreen(1);
1951 	    In3270 = 0;
1952 	    Stop3270();		/* Tell 3270 we aren't here anymore */
1953 	    setconnmode();
1954 	}
1955     }
1956 }
1957 #endif	/* defined(TN3270) */
1958 
1959 
1960 static void
telrcv()1961 telrcv()
1962 {
1963     register int c;
1964     static int telrcv_state = TS_DATA;
1965 #   if defined(TN3270)
1966     register int Scc;
1967     register char *Sbp;
1968 #   endif /* defined(TN3270) */
1969 
1970     while ((scc > 0) && (TTYROOM() > 2)) {
1971 	c = *sbp++ & 0xff, scc--;
1972 	switch (telrcv_state) {
1973 
1974 	case TS_CR:
1975 	    telrcv_state = TS_DATA;
1976 	    if (c == '\0') {
1977 		break;	/* Ignore \0 after CR */
1978 	    } else if (c == '\n') {
1979 		if (hisopts[TELOPT_ECHO] && !crmod) {
1980 		    TTYADD(c);
1981 		}
1982 		break;
1983 	    }
1984 	    /* Else, fall through */
1985 
1986 	case TS_DATA:
1987 	    if (c == IAC) {
1988 		telrcv_state = TS_IAC;
1989 		continue;
1990 	    }
1991 #	    if defined(TN3270)
1992 	    if (In3270) {
1993 		*Ifrontp++ = c;
1994 		Sbp = sbp;
1995 		Scc = scc;
1996 		while (Scc > 0) {
1997 		    c = *Sbp++ & 0377, Scc--;
1998 		    if (c == IAC) {
1999 			telrcv_state = TS_IAC;
2000 			break;
2001 		    }
2002 		    *Ifrontp++ = c;
2003 		}
2004 		sbp = Sbp;
2005 		scc = Scc;
2006 	    } else
2007 #	    endif /* defined(TN3270) */
2008 		    /*
2009 		     * The 'crmod' hack (see following) is needed
2010 		     * since we can't * set CRMOD on output only.
2011 		     * Machines like MULTICS like to send \r without
2012 		     * \n; since we must turn off CRMOD to get proper
2013 		     * input, the mapping is done here (sigh).
2014 		     */
2015 	    if (c == '\r') {
2016 		if (scc > 0) {
2017 		    c = *sbp&0xff;
2018 		    if (c == 0) {
2019 			sbp++, scc--;
2020 			/* a "true" CR */
2021 			TTYADD('\r');
2022 		    } else if (!hisopts[TELOPT_ECHO] &&
2023 					(c == '\n')) {
2024 			sbp++, scc--;
2025 			TTYADD('\n');
2026 		    } else {
2027 			TTYADD('\r');
2028 			if (crmod) {
2029 				TTYADD('\n');
2030 			}
2031 		    }
2032 		} else {
2033 		    telrcv_state = TS_CR;
2034 		    TTYADD('\r');
2035 		    if (crmod) {
2036 			    TTYADD('\n');
2037 		    }
2038 		}
2039 	    } else {
2040 		TTYADD(c);
2041 	    }
2042 	    continue;
2043 
2044 	case TS_IAC:
2045 	    switch (c) {
2046 
2047 	    case WILL:
2048 		telrcv_state = TS_WILL;
2049 		continue;
2050 
2051 	    case WONT:
2052 		telrcv_state = TS_WONT;
2053 		continue;
2054 
2055 	    case DO:
2056 		telrcv_state = TS_DO;
2057 		continue;
2058 
2059 	    case DONT:
2060 		telrcv_state = TS_DONT;
2061 		continue;
2062 
2063 	    case DM:
2064 		    /*
2065 		     * We may have missed an urgent notification,
2066 		     * so make sure we flush whatever is in the
2067 		     * buffer currently.
2068 		     */
2069 		SYNCHing = 1;
2070 		ttyflush();
2071 		SYNCHing = stilloob(net);
2072 		settimer(gotDM);
2073 		break;
2074 
2075 	    case NOP:
2076 	    case GA:
2077 		break;
2078 
2079 	    case SB:
2080 		SB_CLEAR();
2081 		telrcv_state = TS_SB;
2082 		continue;
2083 
2084 #	    if defined(TN3270)
2085 	    case EOR:
2086 		if (In3270) {
2087 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
2088 		    if (Ibackp == Ifrontp) {
2089 			Ibackp = Ifrontp = Ibuf;
2090 			ISend = 0;	/* should have been! */
2091 		    } else {
2092 			ISend = 1;
2093 		    }
2094 		}
2095 		break;
2096 #	    endif /* defined(TN3270) */
2097 
2098 	    case IAC:
2099 #	    if !defined(TN3270)
2100 		TTYADD(IAC);
2101 #	    else /* !defined(TN3270) */
2102 		if (In3270) {
2103 		    *Ifrontp++ = IAC;
2104 		} else {
2105 		    TTYADD(IAC);
2106 		}
2107 #	    endif /* !defined(TN3270) */
2108 		break;
2109 
2110 	    default:
2111 		break;
2112 	    }
2113 	    telrcv_state = TS_DATA;
2114 	    continue;
2115 
2116 	case TS_WILL:
2117 	    printoption(">RCVD", will, c, !hisopts[c]);
2118 	    if (c == TELOPT_TM) {
2119 		if (flushout) {
2120 		    flushout = 0;
2121 		}
2122 	    } else if (!hisopts[c]) {
2123 		willoption(c, 1);
2124 	    }
2125 	    SetIn3270();
2126 	    telrcv_state = TS_DATA;
2127 	    continue;
2128 
2129 	case TS_WONT:
2130 	    printoption(">RCVD", wont, c, hisopts[c]);
2131 	    if (c == TELOPT_TM) {
2132 		if (flushout) {
2133 		    flushout = 0;
2134 		}
2135 	    } else if (hisopts[c]) {
2136 		wontoption(c, 1);
2137 	    }
2138 	    SetIn3270();
2139 	    telrcv_state = TS_DATA;
2140 	    continue;
2141 
2142 	case TS_DO:
2143 	    printoption(">RCVD", doopt, c, !myopts[c]);
2144 	    if (!myopts[c])
2145 		dooption(c);
2146 	    SetIn3270();
2147 	    telrcv_state = TS_DATA;
2148 	    continue;
2149 
2150 	case TS_DONT:
2151 	    printoption(">RCVD", dont, c, myopts[c]);
2152 	    if (myopts[c]) {
2153 		myopts[c] = 0;
2154 		sprintf(nfrontp, wont, c);
2155 		nfrontp += sizeof (wont) - 2;
2156 		flushline = 1;
2157 		setconnmode();	/* set new tty mode (maybe) */
2158 		printoption(">SENT", wont, c, 0);
2159 	    }
2160 	    SetIn3270();
2161 	    telrcv_state = TS_DATA;
2162 	    continue;
2163 
2164 	case TS_SB:
2165 	    if (c == IAC) {
2166 		telrcv_state = TS_SE;
2167 	    } else {
2168 		SB_ACCUM(c);
2169 	    }
2170 	    continue;
2171 
2172 	case TS_SE:
2173 	    if (c != SE) {
2174 		if (c != IAC) {
2175 		    SB_ACCUM(IAC);
2176 		}
2177 		SB_ACCUM(c);
2178 		telrcv_state = TS_SB;
2179 	    } else {
2180 		SB_TERM();
2181 		suboption();	/* handle sub-option */
2182 		SetIn3270();
2183 		telrcv_state = TS_DATA;
2184 	    }
2185 	}
2186     }
2187 }
2188 
2189 #if	defined(TN3270)
2190 
2191 /*
2192  * The following routines are places where the various tn3270
2193  * routines make calls into telnet.c.
2194  */
2195 
2196 /* TtyChars() - returns the number of characters in the TTY buffer */
TtyChars()2197 TtyChars()
2198 {
2199     return(tfrontp-tbackp);
2200 }
2201 
2202 /*
2203  * DataToNetwork - queue up some data to go to network.  If "done" is set,
2204  * then when last byte is queued, we add on an IAC EOR sequence (so,
2205  * don't call us with "done" until you want that done...)
2206  *
2207  * We actually do send all the data to the network buffer, since our
2208  * only client needs for us to do that.
2209  */
2210 
2211 int
DataToNetwork(buffer,count,done)2212 DataToNetwork(buffer, count, done)
2213 register char	*buffer;	/* where the data is */
2214 register int	count;		/* how much to send */
2215 int		done;		/* is this the last of a logical block */
2216 {
2217     register int c;
2218     int origCount;
2219     fd_set o;
2220 
2221     origCount = count;
2222     FD_ZERO(&o);
2223 
2224     while (count) {
2225 	if ((netobuf+sizeof netobuf - nfrontp) < 6) {
2226 	    netflush();
2227 	    while ((netobuf+sizeof netobuf - nfrontp) < 6) {
2228 		FD_SET(net, &o);
2229 		(void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0,
2230 						(struct timeval *) 0);
2231 		netflush();
2232 	    }
2233 	}
2234 	c = *buffer++;
2235 	count--;
2236 	if (c == IAC) {
2237 	    *nfrontp++ = IAC;
2238 	    *nfrontp++ = IAC;
2239 	} else {
2240 	    *nfrontp++ = c;
2241 	}
2242     }
2243 
2244     if (done && !count) {
2245 	*nfrontp++ = IAC;
2246 	*nfrontp++ = EOR;
2247 	netflush();		/* try to move along as quickly as ... */
2248     }
2249     return(origCount - count);
2250 }
2251 
2252 /* DataToTerminal - queue up some data to go to terminal. */
2253 
2254 int
DataToTerminal(buffer,count)2255 DataToTerminal(buffer, count)
2256 register char	*buffer;		/* where the data is */
2257 register int	count;			/* how much to send */
2258 {
2259     int origCount;
2260 #if	defined(unix)
2261     fd_set	o;
2262 
2263     FD_ZERO(&o);
2264 #endif	/* defined(unix) */
2265     origCount = count;
2266 
2267     while (count) {
2268 	if (tfrontp >= ttyobuf+sizeof ttyobuf) {
2269 	    ttyflush();
2270 	    while (tfrontp >= ttyobuf+sizeof ttyobuf) {
2271 #if	defined(unix)
2272 		FD_SET(tout, &o);
2273 		(void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0,
2274 						(struct timeval *) 0);
2275 #endif	/* defined(unix) */
2276 		ttyflush();
2277 	    }
2278 	}
2279 	*tfrontp++ = *buffer++;
2280 	count--;
2281     }
2282     return(origCount - count);
2283 }
2284 
2285 /* EmptyTerminal - called to make sure that the terminal buffer is empty.
2286  *			Note that we consider the buffer to run all the
2287  *			way to the kernel (thus the select).
2288  */
2289 
2290 void
EmptyTerminal()2291 EmptyTerminal()
2292 {
2293 #if	defined(unix)
2294     fd_set	o;
2295 
2296     FD_ZERO(&o);
2297 #endif	/* defined(unix) */
2298 
2299     if (tfrontp == tbackp) {
2300 #if	defined(unix)
2301 	FD_SET(tout, &o);
2302 	(void) select(tout+1, (int *) 0, &o, (int *) 0,
2303 			(struct timeval *) 0);	/* wait for TTLOWAT */
2304 #endif	/* defined(unix) */
2305     } else {
2306 	while (tfrontp != tbackp) {
2307 	    ttyflush();
2308 #if	defined(unix)
2309 	    FD_SET(tout, &o);
2310 	    (void) select(tout+1, (int *) 0, &o, (int *) 0,
2311 				(struct timeval *) 0);	/* wait for TTLOWAT */
2312 #endif	/* defined(unix) */
2313 	}
2314     }
2315 }
2316 
2317 /*
2318  * Push3270 - Try to send data along the 3270 output (to screen) direction.
2319  */
2320 
2321 static int
Push3270()2322 Push3270()
2323 {
2324     int save = scc;
2325 
2326     if (scc) {
2327 	if (Ifrontp+scc > Ibuf+sizeof Ibuf) {
2328 	    if (Ibackp != Ibuf) {
2329 		memcpy(Ibuf, Ibackp, Ifrontp-Ibackp);
2330 		Ifrontp -= (Ibackp-Ibuf);
2331 		Ibackp = Ibuf;
2332 	    }
2333 	}
2334 	if (Ifrontp+scc < Ibuf+sizeof Ibuf) {
2335 	    telrcv();
2336 	}
2337     }
2338     return save != scc;
2339 }
2340 
2341 
2342 /*
2343  * Finish3270 - get the last dregs of 3270 data out to the terminal
2344  *		before quitting.
2345  */
2346 
2347 static void
Finish3270()2348 Finish3270()
2349 {
2350     while (Push3270() || !DoTerminalOutput()) {
2351 #if	defined(unix)
2352 	HaveInput = 0;
2353 #endif	/* defined(unix) */
2354 	;
2355     }
2356 }
2357 
2358 
2359 
2360 /* StringToTerminal - output a null terminated string to the terminal */
2361 
2362 void
StringToTerminal(s)2363 StringToTerminal(s)
2364 char *s;
2365 {
2366     int count;
2367 
2368     count = strlen(s);
2369     if (count) {
2370 	(void) DataToTerminal(s, count);	/* we know it always goes... */
2371     }
2372 }
2373 
2374 
2375 #if	defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR))
2376 /* _putchar - output a single character to the terminal.  This name is so that
2377  *	curses(3x) can call us to send out data.
2378  */
2379 
2380 void
_putchar(c)2381 _putchar(c)
2382 char c;
2383 {
2384     if (tfrontp >= ttyobuf+sizeof ttyobuf) {
2385 	(void) DataToTerminal(&c, 1);
2386     } else {
2387 	*tfrontp++ = c;		/* optimize if possible. */
2388     }
2389 }
2390 #endif	/* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */
2391 
2392 static void
SetForExit()2393 SetForExit()
2394 {
2395     setconnmode();
2396     if (In3270) {
2397 	Finish3270();
2398     }
2399     setcommandmode();
2400     fflush(stdout);
2401     fflush(stderr);
2402     if (In3270) {
2403 	StopScreen(1);
2404     }
2405     setconnmode();
2406     setcommandmode();
2407 }
2408 
2409 static void
Exit(returnCode)2410 Exit(returnCode)
2411 int returnCode;
2412 {
2413     SetForExit();
2414     exit(returnCode);
2415 }
2416 
2417 void
ExitString(file,string,returnCode)2418 ExitString(file, string, returnCode)
2419 FILE *file;
2420 char *string;
2421 int returnCode;
2422 {
2423     SetForExit();
2424     fwrite(string, 1, strlen(string), file);
2425     exit(returnCode);
2426 }
2427 
2428 void
ExitPerror(string,returnCode)2429 ExitPerror(string, returnCode)
2430 char *string;
2431 int returnCode;
2432 {
2433     SetForExit();
2434     perror(string);
2435     exit(returnCode);
2436 }
2437 
2438 #endif	/* defined(TN3270) */
2439 
2440 /*
2441  * Scheduler()
2442  *
2443  * Try to do something.
2444  *
2445  * If we do something useful, return 1; else return 0.
2446  *
2447  */
2448 
2449 
2450 int
Scheduler(block)2451 Scheduler(block)
2452 int	block;			/* should we block in the select ? */
2453 {
2454     register int c;
2455 		/* One wants to be a bit careful about setting returnValue
2456 		 * to one, since a one implies we did some useful work,
2457 		 * and therefore probably won't be called to block next
2458 		 * time (TN3270 mode only).
2459 		 */
2460     int returnValue = 0;
2461     static struct timeval TimeValue = { 0 };
2462 
2463     if (scc < 0 && tcc < 0) {
2464 	return -1;
2465     }
2466 
2467     if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) {
2468 	FD_SET(net, &obits);
2469     }
2470 #if	!defined(MSDOS)
2471     if (TTYBYTES()) {
2472 	FD_SET(tout, &obits);
2473     }
2474     if ((tcc == 0) && NETROOM() && (shell_active == 0)) {
2475 	FD_SET(tin, &ibits);
2476     }
2477 #endif	/* !defined(MSDOS) */
2478 #   if !defined(TN3270)
2479     if (TTYROOM()) {
2480 	FD_SET(net, &ibits);
2481     }
2482 #   else /* !defined(TN3270) */
2483     if (!ISend && TTYROOM()) {
2484 	FD_SET(net, &ibits);
2485     }
2486 #   endif /* !defined(TN3270) */
2487     if (!SYNCHing) {
2488 	FD_SET(net, &xbits);
2489     }
2490 #   if defined(TN3270) && defined(unix)
2491     if (HaveInput) {
2492 	HaveInput = 0;
2493 	signal(SIGIO, inputAvailable);
2494     }
2495 #endif	/* defined(TN3270) && defined(unix) */
2496     if ((c = select(16, &ibits, &obits, &xbits,
2497 			block? (struct timeval *)0 : &TimeValue)) < 0) {
2498 	if (c == -1) {
2499 		    /*
2500 		     * we can get EINTR if we are in line mode,
2501 		     * and the user does an escape (TSTP), or
2502 		     * some other signal generator.
2503 		     */
2504 	    if (errno == EINTR) {
2505 		return 0;
2506 	    }
2507 #	    if defined(TN3270)
2508 		    /*
2509 		     * we can get EBADF if we were in transparent
2510 		     * mode, and the transcom process died.
2511 		    */
2512 	    if (errno == EBADF) {
2513 			/*
2514 			 * zero the bits (even though kernel does it)
2515 			 * to make sure we are selecting on the right
2516 			 * ones.
2517 			*/
2518 		FD_ZERO(&ibits);
2519 		FD_ZERO(&obits);
2520 		FD_ZERO(&xbits);
2521 		return 0;
2522 	    }
2523 #	    endif /* defined(TN3270) */
2524 		    /* I don't like this, does it ever happen? */
2525 	    printf("sleep(5) from telnet, after select\r\n");
2526 #if	defined(unix)
2527 	    sleep(5);
2528 #endif	/* defined(unix) */
2529 	}
2530 	return 0;
2531     }
2532 
2533     /*
2534      * Any urgent data?
2535      */
2536     if (FD_ISSET(net, &xbits)) {
2537 	FD_CLR(net, &xbits);
2538 	SYNCHing = 1;
2539 	ttyflush();	/* flush already enqueued data */
2540     }
2541 
2542     /*
2543      * Something to read from the network...
2544      */
2545     if (FD_ISSET(net, &ibits)) {
2546 	int canread;
2547 
2548 	FD_CLR(net, &ibits);
2549 	if (scc == 0) {
2550 	    sbp = sibuf;
2551 	}
2552 	canread = sibuf + sizeof sibuf - (sbp+scc);
2553 #if	!defined(SO_OOBINLINE)
2554 	    /*
2555 	     * In 4.2 (and some early 4.3) systems, the
2556 	     * OOB indication and data handling in the kernel
2557 	     * is such that if two separate TCP Urgent requests
2558 	     * come in, one byte of TCP data will be overlaid.
2559 	     * This is fatal for Telnet, but we try to live
2560 	     * with it.
2561 	     *
2562 	     * In addition, in 4.2 (and...), a special protocol
2563 	     * is needed to pick up the TCP Urgent data in
2564 	     * the correct sequence.
2565 	     *
2566 	     * What we do is:  if we think we are in urgent
2567 	     * mode, we look to see if we are "at the mark".
2568 	     * If we are, we do an OOB receive.  If we run
2569 	     * this twice, we will do the OOB receive twice,
2570 	     * but the second will fail, since the second
2571 	     * time we were "at the mark", but there wasn't
2572 	     * any data there (the kernel doesn't reset
2573 	     * "at the mark" until we do a normal read).
2574 	     * Once we've read the OOB data, we go ahead
2575 	     * and do normal reads.
2576 	     *
2577 	     * There is also another problem, which is that
2578 	     * since the OOB byte we read doesn't put us
2579 	     * out of OOB state, and since that byte is most
2580 	     * likely the TELNET DM (data mark), we would
2581 	     * stay in the TELNET SYNCH (SYNCHing) state.
2582 	     * So, clocks to the rescue.  If we've "just"
2583 	     * received a DM, then we test for the
2584 	     * presence of OOB data when the receive OOB
2585 	     * fails (and AFTER we did the normal mode read
2586 	     * to clear "at the mark").
2587 	     */
2588 	if (SYNCHing) {
2589 	    int atmark;
2590 
2591 	    ioctl(net, SIOCATMARK, (char *)&atmark);
2592 	    if (atmark) {
2593 		c = recv(net, sbp+scc, canread, MSG_OOB);
2594 		if ((c == -1) && (errno == EINVAL)) {
2595 		    c = recv(net, sbp+scc, canread, 0);
2596 		    if (clocks.didnetreceive < clocks.gotDM) {
2597 			SYNCHing = stilloob(net);
2598 		    }
2599 		}
2600 	    } else {
2601 		c = recv(net, sbp+scc, canread, 0);
2602 	    }
2603 	} else {
2604 	    c = recv(net, sbp+scc, canread, 0);
2605 	}
2606 	settimer(didnetreceive);
2607 #else	/* !defined(SO_OOBINLINE) */
2608 	c = recv(net, sbp+scc, canread, 0);
2609 #endif	/* !defined(SO_OOBINLINE) */
2610 	if (c < 0 && errno == EWOULDBLOCK) {
2611 	    c = 0;
2612 	} else if (c <= 0) {
2613 	    return -1;
2614 	}
2615 	if (netdata) {
2616 	    Dump('<', sbp+scc, c);
2617 	}
2618 	scc += c;
2619 	returnValue = 1;
2620     }
2621 
2622     /*
2623      * Something to read from the tty...
2624      */
2625 #if	defined(MSDOS)
2626     if ((tcc == 0) && NETROOM() && (shell_active == 0) && TerminalCanRead())
2627 #else	/* defined(MSDOS) */
2628     if (FD_ISSET(tin, &ibits))
2629 #endif	/* defined(MSDOS) */
2630 				    {
2631 	FD_CLR(tin, &ibits);
2632 	if (tcc == 0) {
2633 	    tbp = tibuf;	/* nothing left, reset */
2634 	}
2635 	c = TerminalRead(tin, tbp, tibuf+sizeof tibuf - tbp);
2636 	if (c < 0 && errno == EWOULDBLOCK) {
2637 	    c = 0;
2638 	} else {
2639 #if	defined(unix)
2640 	    /* EOF detection for line mode!!!! */
2641 	    if (c == 0 && MODE_LOCAL_CHARS(globalmode)) {
2642 			/* must be an EOF... */
2643 		*tbp = ntc.t_eofc;
2644 		c = 1;
2645 	    }
2646 #endif	/* defined(unix) */
2647 	    if (c <= 0) {
2648 		tcc = c;
2649 		return -1;
2650 	    }
2651 	}
2652 	tcc += c;
2653 	returnValue = 1;		/* did something useful */
2654     }
2655 
2656 #   if defined(TN3270)
2657     if (tcc > 0) {
2658 	if (In3270) {
2659 	    c = DataFromTerminal(tbp, tcc);
2660 	    if (c) {
2661 		returnValue = 1;
2662 	    }
2663 	    tcc -= c;
2664 	    tbp += c;
2665 	} else {
2666 #   endif /* defined(TN3270) */
2667 	    returnValue = 1;
2668 	    while (tcc > 0) {
2669 		register int sc;
2670 
2671 		if (NETROOM() < 2) {
2672 		    flushline = 1;
2673 		    break;
2674 		}
2675 		c = *tbp++ & 0xff, sc = strip(c), tcc--;
2676 		if (sc == escape) {
2677 		    command(0);
2678 		    tcc = 0;
2679 		    flushline = 1;
2680 		    break;
2681 		} else if (MODE_LINE(globalmode) && (sc == echoc)) {
2682 		    if (tcc > 0 && strip(*tbp) == echoc) {
2683 			tbp++;
2684 			tcc--;
2685 		    } else {
2686 			dontlecho = !dontlecho;
2687 			settimer(echotoggle);
2688 			setconnmode();
2689 			tcc = 0;
2690 			flushline = 1;
2691 			break;
2692 		    }
2693 		}
2694 		if (localchars) {
2695 		    if (TerminalSpecialChars(sc) == 0) {
2696 			break;
2697 		    }
2698 		}
2699 		switch (c) {
2700 		case '\n':
2701 			/*
2702 			 * If we are in CRMOD mode (\r ==> \n)
2703 			 * on our local machine, then probably
2704 			 * a newline (unix) is CRLF (TELNET).
2705 			 */
2706 		    if (MODE_LOCAL_CHARS(globalmode)) {
2707 			NETADD('\r');
2708 		    }
2709 		    NETADD('\n');
2710 		    flushline = 1;
2711 		    break;
2712 		case '\r':
2713 		    NET2ADD('\r', '\0');
2714 		    flushline = 1;
2715 		    break;
2716 		case IAC:
2717 		    NET2ADD(IAC, IAC);
2718 		    break;
2719 		default:
2720 		    NETADD(c);
2721 		    break;
2722 		}
2723 	    }
2724 #   if defined(TN3270)
2725 	}
2726     }
2727 #   endif /* defined(TN3270) */
2728 
2729     if ((!MODE_LINE(globalmode) || flushline) &&
2730 	FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
2731 	FD_CLR(net, &obits);
2732 	returnValue = netflush();
2733     }
2734     if (scc > 0) {
2735 #	if !defined(TN3270)
2736 	telrcv();
2737 	returnValue = 1;
2738 #	else /* !defined(TN3270) */
2739 	returnValue = Push3270();
2740 #	endif /* !defined(TN3270) */
2741     }
2742 #if	defined(MSDOS)
2743     if (TTYBYTES())
2744 #else	/* defined(MSDOS) */
2745     if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0))
2746 #endif	/* defined(MSDOS) */
2747 						    {
2748 	FD_CLR(tout, &obits);
2749 	returnValue = ttyflush();
2750     }
2751     return returnValue;
2752 }
2753 
2754 /*
2755  * Select from tty and network...
2756  */
2757 static void
telnet()2758 telnet()
2759 {
2760 #if	defined(MSDOS)
2761 #define	SCHED_BLOCK	0		/* Don't block in MSDOS */
2762 #else	/* defined(MSDOS) */
2763 #define	SCHED_BLOCK	1
2764 #endif	/* defined(MSDOS) */
2765 
2766 #if	defined(TN3270) && defined(unix)
2767     int myPid;
2768 #endif	/* defined(TN3270) */
2769 
2770     tout = fileno(stdout);
2771     tin = fileno(stdin);
2772     setconnmode();
2773     scc = 0;
2774     tcc = 0;
2775     FD_ZERO(&ibits);
2776     FD_ZERO(&obits);
2777     FD_ZERO(&xbits);
2778 
2779     NetNonblockingIO(net, 1);
2780 
2781 #if	defined(TN3270)
2782     if (noasynch == 0) {			/* DBX can't handle! */
2783 	NetSigIO(net, 1);
2784     }
2785     NetSetPgrp(net);
2786 #endif	/* defined(TN3270) */
2787 
2788 
2789 #if	defined(SO_OOBINLINE) && !defined(MSDOS)
2790     SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1);
2791 #endif	/* defined(SO_OOBINLINE) && !defined(MSDOS) */
2792 
2793 #   if !defined(TN3270)
2794     if (telnetport) {
2795 	if (!hisopts[TELOPT_SGA]) {
2796 	    willoption(TELOPT_SGA, 0);
2797 	}
2798 	if (!myopts[TELOPT_TTYPE]) {
2799 	    dooption(TELOPT_TTYPE, 0);
2800 	}
2801     }
2802 #   endif /* !defined(TN3270) */
2803 
2804 #   if !defined(TN3270)
2805     for (;;) {
2806 	if (Scheduler(SCHED_BLOCK) == -1) {
2807 	    setcommandmode();
2808 	    return;
2809 	}
2810     }
2811 #   else /* !defined(TN3270) */
2812     for (;;) {
2813 	int schedValue;
2814 
2815 	while (!In3270 && !shell_active) {
2816 	    if (Scheduler(SCHED_BLOCK) == -1) {
2817 		setcommandmode();
2818 		return;
2819 	    }
2820 	}
2821 
2822 	while ((schedValue = Scheduler(0)) != 0) {
2823 	    if (schedValue == -1) {
2824 		setcommandmode();
2825 		return;
2826 	    }
2827 	}
2828 		/* If there is data waiting to go out to terminal, don't
2829 		 * schedule any more data for the terminal.
2830 		 */
2831 	if (tfrontp-tbackp) {
2832 	    schedValue = 1;
2833 	} else {
2834 	    if (shell_active) {
2835 		if (shell_continue() == 0) {
2836 		    ConnectScreen();
2837 		}
2838 	    } else if (In3270) {
2839 		schedValue = DoTerminalOutput();
2840 	    }
2841 	}
2842 	if (schedValue && (shell_active == 0)) {
2843 	    if (Scheduler(SCHED_BLOCK) == -1) {
2844 		setcommandmode();
2845 		return;
2846 	    }
2847 	}
2848     }
2849 #   endif /* !defined(TN3270) */
2850 }
2851 
2852 /*
2853  *	The following are data structures and routines for
2854  *	the "send" command.
2855  *
2856  */
2857 
2858 struct sendlist {
2859     char	*name;		/* How user refers to it (case independent) */
2860     int		what;		/* Character to be sent (<0 ==> special) */
2861     char	*help;		/* Help information (0 ==> no help) */
2862 #if	defined(NOT43)
2863     int		(*routine)();	/* Routine to perform (for special ops) */
2864 #else	/* defined(NOT43) */
2865     void	(*routine)();	/* Routine to perform (for special ops) */
2866 #endif	/* defined(NOT43) */
2867 };
2868 
2869 #define	SENDQUESTION	-1
2870 #define	SENDESCAPE	-3
2871 
2872 static struct sendlist Sendlist[] = {
2873     { "ao", AO, "Send Telnet Abort output" },
2874     { "ayt", AYT, "Send Telnet 'Are You There'" },
2875     { "brk", BREAK, "Send Telnet Break" },
2876     { "ec", EC, "Send Telnet Erase Character" },
2877     { "el", EL, "Send Telnet Erase Line" },
2878     { "escape", SENDESCAPE, "Send current escape character" },
2879     { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
2880     { "ip", IP, "Send Telnet Interrupt Process" },
2881     { "nop", NOP, "Send Telnet 'No operation'" },
2882     { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
2883     { "?", SENDQUESTION, "Display send options" },
2884     { 0 }
2885 };
2886 
2887 static struct sendlist Sendlist2[] = {		/* some synonyms */
2888 	{ "break", BREAK, 0 },
2889 
2890 	{ "intp", IP, 0 },
2891 	{ "interrupt", IP, 0 },
2892 	{ "intr", IP, 0 },
2893 
2894 	{ "help", SENDQUESTION, 0 },
2895 
2896 	{ 0 }
2897 };
2898 
2899 static char **
getnextsend(name)2900 getnextsend(name)
2901 char *name;
2902 {
2903     struct sendlist *c = (struct sendlist *) name;
2904 
2905     return (char **) (c+1);
2906 }
2907 
2908 static struct sendlist *
getsend(name)2909 getsend(name)
2910 char *name;
2911 {
2912     struct sendlist *sl;
2913 
2914     if ((sl = (struct sendlist *)
2915 			genget(name, (char **) Sendlist, getnextsend)) != 0) {
2916 	return sl;
2917     } else {
2918 	return (struct sendlist *)
2919 				genget(name, (char **) Sendlist2, getnextsend);
2920     }
2921 }
2922 
2923 static
sendcmd(argc,argv)2924 sendcmd(argc, argv)
2925 int	argc;
2926 char	**argv;
2927 {
2928     int what;		/* what we are sending this time */
2929     int count;		/* how many bytes we are going to need to send */
2930     int i;
2931     int question = 0;	/* was at least one argument a question */
2932     struct sendlist *s;	/* pointer to current command */
2933 
2934     if (argc < 2) {
2935 	printf("need at least one argument for 'send' command\n");
2936 	printf("'send ?' for help\n");
2937 	return 0;
2938     }
2939     /*
2940      * First, validate all the send arguments.
2941      * In addition, we see how much space we are going to need, and
2942      * whether or not we will be doing a "SYNCH" operation (which
2943      * flushes the network queue).
2944      */
2945     count = 0;
2946     for (i = 1; i < argc; i++) {
2947 	s = getsend(argv[i]);
2948 	if (s == 0) {
2949 	    printf("Unknown send argument '%s'\n'send ?' for help.\n",
2950 			argv[i]);
2951 	    return 0;
2952 	} else if (s == Ambiguous(struct sendlist *)) {
2953 	    printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
2954 			argv[i]);
2955 	    return 0;
2956 	}
2957 	switch (s->what) {
2958 	case SENDQUESTION:
2959 	    break;
2960 	case SENDESCAPE:
2961 	    count += 1;
2962 	    break;
2963 	case SYNCH:
2964 	    count += 2;
2965 	    break;
2966 	default:
2967 	    count += 2;
2968 	    break;
2969 	}
2970     }
2971     /* Now, do we have enough room? */
2972     if (NETROOM() < count) {
2973 	printf("There is not enough room in the buffer TO the network\n");
2974 	printf("to process your request.  Nothing will be done.\n");
2975 	printf("('send synch' will throw away most data in the network\n");
2976 	printf("buffer, if this might help.)\n");
2977 	return 0;
2978     }
2979     /* OK, they are all OK, now go through again and actually send */
2980     for (i = 1; i < argc; i++) {
2981 	if ((s = getsend(argv[i])) == 0) {
2982 	    fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
2983 	    quit();
2984 	    /*NOTREACHED*/
2985 	}
2986 	if (s->routine) {
2987 	    (*s->routine)(s);
2988 	} else {
2989 	    switch (what = s->what) {
2990 	    case SYNCH:
2991 		dosynch();
2992 		break;
2993 	    case SENDQUESTION:
2994 		for (s = Sendlist; s->name; s++) {
2995 		    if (s->help) {
2996 			printf(s->name);
2997 			if (s->help) {
2998 			    printf("\t%s", s->help);
2999 			}
3000 			printf("\n");
3001 		    }
3002 		}
3003 		question = 1;
3004 		break;
3005 	    case SENDESCAPE:
3006 		NETADD(escape);
3007 		break;
3008 	    default:
3009 		NET2ADD(IAC, what);
3010 		break;
3011 	    }
3012 	}
3013     }
3014     return !question;
3015 }
3016 
3017 /*
3018  * The following are the routines and data structures referred
3019  * to by the arguments to the "toggle" command.
3020  */
3021 
3022 static
lclchars()3023 lclchars()
3024 {
3025     donelclchars = 1;
3026     return 1;
3027 }
3028 
3029 static
togdebug()3030 togdebug()
3031 {
3032 #ifndef	NOT43
3033     if (net > 0 &&
3034 	(SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) {
3035 	    perror("setsockopt (SO_DEBUG)");
3036     }
3037 #else	/* NOT43 */
3038     if (debug) {
3039 	if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
3040 	    perror("setsockopt (SO_DEBUG)");
3041     } else
3042 	printf("Cannot turn off socket debugging\n");
3043 #endif	/* NOT43 */
3044     return 1;
3045 }
3046 
3047 
3048 static int
togbinary()3049 togbinary()
3050 {
3051     donebinarytoggle = 1;
3052 
3053     if (myopts[TELOPT_BINARY] == 0) {	/* Go into binary mode */
3054 	NET2ADD(IAC, DO);
3055 	NETADD(TELOPT_BINARY);
3056 	printoption("<SENT", doopt, TELOPT_BINARY, 0);
3057 	NET2ADD(IAC, WILL);
3058 	NETADD(TELOPT_BINARY);
3059 	printoption("<SENT", doopt, TELOPT_BINARY, 0);
3060 	hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 1;
3061 	printf("Negotiating binary mode with remote host.\n");
3062     } else {				/* Turn off binary mode */
3063 	NET2ADD(IAC, DONT);
3064 	NETADD(TELOPT_BINARY);
3065 	printoption("<SENT", dont, TELOPT_BINARY, 0);
3066 	NET2ADD(IAC, DONT);
3067 	NETADD(TELOPT_BINARY);
3068 	printoption("<SENT", dont, TELOPT_BINARY, 0);
3069 	hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 0;
3070 	printf("Negotiating network ascii mode with remote host.\n");
3071     }
3072     return 1;
3073 }
3074 
3075 
3076 
3077 extern int togglehelp();
3078 
3079 struct togglelist {
3080     char	*name;		/* name of toggle */
3081     char	*help;		/* help message */
3082     int		(*handler)();	/* routine to do actual setting */
3083     int		dohelp;		/* should we display help information */
3084     int		*variable;
3085     char	*actionexplanation;
3086 };
3087 
3088 static struct togglelist Togglelist[] = {
3089     { "autoflush",
3090 	"toggle flushing of output when sending interrupt characters",
3091 	    0,
3092 		1,
3093 		    &autoflush,
3094 			"flush output when sending interrupt characters" },
3095     { "autosynch",
3096 	"toggle automatic sending of interrupt characters in urgent mode",
3097 	    0,
3098 		1,
3099 		    &autosynch,
3100 			"send interrupt characters in urgent mode" },
3101     { "binary",
3102 	"toggle sending and receiving of binary data",
3103 	    togbinary,
3104 		1,
3105 		    0,
3106 			"send and receive network data in binary mode" },
3107     { "crmod",
3108 	"toggle mapping of received carriage returns",
3109 	    0,
3110 		1,
3111 		    &crmod,
3112 			"map carriage return on output" },
3113     { "localchars",
3114 	"toggle local recognition of certain control characters",
3115 	    lclchars,
3116 		1,
3117 		    &localchars,
3118 			"recognize certain control characters" },
3119     { " ", "", 0, 1 },		/* empty line */
3120     { "debug",
3121 	"(debugging) toggle debugging",
3122 	    togdebug,
3123 		1,
3124 		    &debug,
3125 			"turn on socket level debugging" },
3126     { "netdata",
3127 	"(debugging) toggle printing of hexadecimal network data",
3128 	    0,
3129 		1,
3130 		    &netdata,
3131 			"print hexadecimal representation of network traffic" },
3132     { "options",
3133 	"(debugging) toggle viewing of options processing",
3134 	    0,
3135 		1,
3136 		    &showoptions,
3137 			"show option processing" },
3138     { " ", "", 0, 1 },		/* empty line */
3139     { "?",
3140 	"display help information",
3141 	    togglehelp,
3142 		1 },
3143     { "help",
3144 	"display help information",
3145 	    togglehelp,
3146 		0 },
3147     { 0 }
3148 };
3149 
3150 static
togglehelp()3151 togglehelp()
3152 {
3153     struct togglelist *c;
3154 
3155     for (c = Togglelist; c->name; c++) {
3156 	if (c->dohelp) {
3157 	    printf("%s\t%s\n", c->name, c->help);
3158 	}
3159     }
3160     return 0;
3161 }
3162 
3163 static char **
getnexttoggle(name)3164 getnexttoggle(name)
3165 char *name;
3166 {
3167     struct togglelist *c = (struct togglelist *) name;
3168 
3169     return (char **) (c+1);
3170 }
3171 
3172 static struct togglelist *
gettoggle(name)3173 gettoggle(name)
3174 char *name;
3175 {
3176     return (struct togglelist *)
3177 			genget(name, (char **) Togglelist, getnexttoggle);
3178 }
3179 
3180 static
toggle(argc,argv)3181 toggle(argc, argv)
3182 int	argc;
3183 char	*argv[];
3184 {
3185     int retval = 1;
3186     char *name;
3187     struct togglelist *c;
3188 
3189     if (argc < 2) {
3190 	fprintf(stderr,
3191 	    "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
3192 	return 0;
3193     }
3194     argc--;
3195     argv++;
3196     while (argc--) {
3197 	name = *argv++;
3198 	c = gettoggle(name);
3199 	if (c == Ambiguous(struct togglelist *)) {
3200 	    fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
3201 					name);
3202 	    return 0;
3203 	} else if (c == 0) {
3204 	    fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
3205 					name);
3206 	    return 0;
3207 	} else {
3208 	    if (c->variable) {
3209 		*c->variable = !*c->variable;		/* invert it */
3210 		printf("%s %s.\n", *c->variable? "Will" : "Won't",
3211 							c->actionexplanation);
3212 	    }
3213 	    if (c->handler) {
3214 		retval &= (*c->handler)(c);
3215 	    }
3216 	}
3217     }
3218     return retval;
3219 }
3220 
3221 /*
3222  * The following perform the "set" command.
3223  */
3224 
3225 struct setlist {
3226     char *name;				/* name */
3227     char *help;				/* help information */
3228     char *charp;			/* where it is located at */
3229 };
3230 
3231 static struct setlist Setlist[] = {
3232     { "echo", 	"character to toggle local echoing on/off", &echoc },
3233     { "escape",	"character to escape back to telnet command mode", &escape },
3234     { " ", "" },
3235     { " ", "The following need 'localchars' to be toggled true", 0 },
3236 #if	defined(unix)
3237     { "erase",	"character to cause an Erase Character", &nttyb.sg_erase },
3238     { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
3239     { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
3240     { "kill",	"character to cause an Erase Line", &nttyb.sg_kill },
3241     { "quit",	"character to cause a Break", &ntc.t_quitc },
3242     { "eof",	"character to cause an EOF ", &ntc.t_eofc },
3243 #endif	/* defined(unix) */
3244 #if	defined(MSDOS)
3245     { "erase",	"character to cause an Erase Character", &termEraseChar },
3246     { "flushoutput", "character to cause an Abort Oubput", &termFlushChar },
3247     { "interrupt", "character to cause an Interrupt Process", &termIntChar },
3248     { "kill",	"character to cause an Erase Line", &termKillChar },
3249     { "quit",	"character to cause a Break", &termQuitChar },
3250     { "eof",	"character to cause an EOF ", &termEofChar },
3251 #endif	/* defined(MSDOS) */
3252     { 0 }
3253 };
3254 
3255 static char **
getnextset(name)3256 getnextset(name)
3257 char *name;
3258 {
3259     struct setlist *c = (struct setlist *)name;
3260 
3261     return (char **) (c+1);
3262 }
3263 
3264 static struct setlist *
getset(name)3265 getset(name)
3266 char *name;
3267 {
3268     return (struct setlist *) genget(name, (char **) Setlist, getnextset);
3269 }
3270 
3271 static
setcmd(argc,argv)3272 setcmd(argc, argv)
3273 int	argc;
3274 char	*argv[];
3275 {
3276     int value;
3277     struct setlist *ct;
3278 
3279     /* XXX back we go... sigh */
3280     if (argc != 3) {
3281 	if ((argc == 2) &&
3282 		    ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
3283 	    for (ct = Setlist; ct->name; ct++) {
3284 		printf("%s\t%s\n", ct->name, ct->help);
3285 	    }
3286 	    printf("?\tdisplay help information\n");
3287 	} else {
3288 	    printf("Format is 'set Name Value'\n'set ?' for help.\n");
3289 	}
3290 	return 0;
3291     }
3292 
3293     ct = getset(argv[1]);
3294     if (ct == 0) {
3295 	fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
3296 			argv[1]);
3297 	return 0;
3298     } else if (ct == Ambiguous(struct setlist *)) {
3299 	fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
3300 			argv[1]);
3301 	return 0;
3302     } else {
3303 	if (strcmp("off", argv[2])) {
3304 	    value = special(argv[2]);
3305 	} else {
3306 	    value = -1;
3307 	}
3308 	*(ct->charp) = value;
3309 	printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
3310     }
3311     return 1;
3312 }
3313 
3314 /*
3315  * The following are the data structures and routines for the
3316  * 'mode' command.
3317  */
3318 
3319 static
dolinemode()3320 dolinemode()
3321 {
3322     if (hisopts[TELOPT_SGA]) {
3323 	wontoption(TELOPT_SGA, 0);
3324     }
3325     if (hisopts[TELOPT_ECHO]) {
3326 	wontoption(TELOPT_ECHO, 0);
3327     }
3328     return 1;
3329 }
3330 
3331 static
docharmode()3332 docharmode()
3333 {
3334     if (!hisopts[TELOPT_SGA]) {
3335 	willoption(TELOPT_SGA, 0);
3336     }
3337     if (!hisopts[TELOPT_ECHO]) {
3338 	willoption(TELOPT_ECHO, 0);
3339     }
3340     return 1;
3341 }
3342 
3343 static struct cmd Modelist[] = {
3344     { "character",	"character-at-a-time mode",	docharmode, 1, 1 },
3345     { "line",		"line-by-line mode",		dolinemode, 1, 1 },
3346     { 0 },
3347 };
3348 
3349 static char **
getnextmode(name)3350 getnextmode(name)
3351 char *name;
3352 {
3353     struct cmd *c = (struct cmd *) name;
3354 
3355     return (char **) (c+1);
3356 }
3357 
3358 static struct cmd *
getmodecmd(name)3359 getmodecmd(name)
3360 char *name;
3361 {
3362     return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
3363 }
3364 
3365 static
modecmd(argc,argv)3366 modecmd(argc, argv)
3367 int	argc;
3368 char	*argv[];
3369 {
3370     struct cmd *mt;
3371 
3372     if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
3373 	printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
3374 	for (mt = Modelist; mt->name; mt++) {
3375 	    printf("%s\t%s\n", mt->name, mt->help);
3376 	}
3377 	return 0;
3378     }
3379     mt = getmodecmd(argv[1]);
3380     if (mt == 0) {
3381 	fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
3382 	return 0;
3383     } else if (mt == Ambiguous(struct cmd *)) {
3384 	fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
3385 	return 0;
3386     } else {
3387 	(*mt->handler)();
3388     }
3389     return 1;
3390 }
3391 
3392 /*
3393  * The following data structures and routines implement the
3394  * "display" command.
3395  */
3396 
3397 static
display(argc,argv)3398 display(argc, argv)
3399 int	argc;
3400 char	*argv[];
3401 {
3402 #define	dotog(tl)	if (tl->variable && tl->actionexplanation) { \
3403 			    if (*tl->variable) { \
3404 				printf("will"); \
3405 			    } else { \
3406 				printf("won't"); \
3407 			    } \
3408 			    printf(" %s.\n", tl->actionexplanation); \
3409 			}
3410 
3411 #define	doset(sl)   if (sl->name && *sl->name != ' ') { \
3412 			printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
3413 		    }
3414 
3415     struct togglelist *tl;
3416     struct setlist *sl;
3417 
3418     if (argc == 1) {
3419 	for (tl = Togglelist; tl->name; tl++) {
3420 	    dotog(tl);
3421 	}
3422 	printf("\n");
3423 	for (sl = Setlist; sl->name; sl++) {
3424 	    doset(sl);
3425 	}
3426     } else {
3427 	int i;
3428 
3429 	for (i = 1; i < argc; i++) {
3430 	    sl = getset(argv[i]);
3431 	    tl = gettoggle(argv[i]);
3432 	    if ((sl == Ambiguous(struct setlist *)) ||
3433 				(tl == Ambiguous(struct togglelist *))) {
3434 		printf("?Ambiguous argument '%s'.\n", argv[i]);
3435 		return 0;
3436 	    } else if (!sl && !tl) {
3437 		printf("?Unknown argument '%s'.\n", argv[i]);
3438 		return 0;
3439 	    } else {
3440 		if (tl) {
3441 		    dotog(tl);
3442 		}
3443 		if (sl) {
3444 		    doset(sl);
3445 		}
3446 	    }
3447 	}
3448     }
3449     return 1;
3450 #undef	doset
3451 #undef	dotog
3452 }
3453 
3454 /*
3455  * The following are the data structures, and many of the routines,
3456  * relating to command processing.
3457  */
3458 
3459 /*
3460  * Set the escape character.
3461  */
3462 static
setescape(argc,argv)3463 setescape(argc, argv)
3464 	int argc;
3465 	char *argv[];
3466 {
3467 	register char *arg;
3468 	char buf[50];
3469 
3470 	printf(
3471 	    "Deprecated usage - please use 'set escape%s%s' in the future.\n",
3472 				(argc > 2)? " ":"", (argc > 2)? argv[1]: "");
3473 	if (argc > 2)
3474 		arg = argv[1];
3475 	else {
3476 		printf("new escape character: ");
3477 		gets(buf);
3478 		arg = buf;
3479 	}
3480 	if (arg[0] != '\0')
3481 		escape = arg[0];
3482 	if (!In3270) {
3483 		printf("Escape character is '%s'.\n", control(escape));
3484 	}
3485 	fflush(stdout);
3486 	return 1;
3487 }
3488 
3489 /*VARARGS*/
3490 static
togcrmod()3491 togcrmod()
3492 {
3493     crmod = !crmod;
3494     printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
3495     printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
3496     fflush(stdout);
3497     return 1;
3498 }
3499 
3500 /*VARARGS*/
suspend()3501 suspend()
3502 {
3503 	setcommandmode();
3504 #if	defined(unix)
3505 	kill(0, SIGTSTP);
3506 #endif	/* defined(unix) */
3507 	/* reget parameters in case they were changed */
3508 	TerminalSaveState();
3509 	setconnmode();
3510 	return 1;
3511 }
3512 
3513 /*VARARGS*/
3514 static
bye(argc,argv)3515 bye(argc, argv)
3516 int	argc;		/* Number of arguments */
3517 char	*argv[];	/* arguments */
3518 {
3519     if (connected) {
3520 	shutdown(net, 2);
3521 	printf("Connection closed.\n");
3522 	NetClose(net);
3523 	connected = 0;
3524 	/* reset options */
3525 	tninit();
3526 #if	defined(TN3270)
3527 	SetIn3270();		/* Get out of 3270 mode */
3528 #endif	/* defined(TN3270) */
3529     }
3530     if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) {
3531 	longjmp(toplevel, 1);
3532 	/* NOTREACHED */
3533     }
3534     return 1;			/* Keep lint, etc., happy */
3535 }
3536 
3537 /*VARARGS*/
quit()3538 quit()
3539 {
3540 	(void) call(bye, "bye", "fromquit", 0);
3541 	Exit(0);
3542 	/*NOTREACHED*/
3543 	return 1;			/* just to keep lint happy */
3544 }
3545 
3546 /*
3547  * Print status about the connection.
3548  */
3549 static
status(argc,argv)3550 status(argc, argv)
3551 int	argc;
3552 char	*argv[];
3553 {
3554     if (connected) {
3555 	printf("Connected to %s.\n", hostname);
3556 	if (argc < 2) {
3557 	    printf("Operating in %s.\n",
3558 				modelist[getconnmode()].modedescriptions);
3559 	    if (localchars) {
3560 		printf("Catching signals locally.\n");
3561 	    }
3562 	}
3563     } else {
3564 	printf("No connection.\n");
3565     }
3566 #   if !defined(TN3270)
3567     printf("Escape character is '%s'.\n", control(escape));
3568     fflush(stdout);
3569 #   else /* !defined(TN3270) */
3570     if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) {
3571 	printf("Escape character is '%s'.\n", control(escape));
3572     }
3573 #   if defined(unix)
3574     if (In3270 && transcom) {
3575        printf("Transparent mode command is '%s'.\n", transcom);
3576     }
3577 #   endif /* defined(unix) */
3578     fflush(stdout);
3579     if (In3270) {
3580 	return 0;
3581     }
3582 #   endif /* defined(TN3270) */
3583     return 1;
3584 }
3585 
3586 #if	defined(TN3270) && defined(unix)
3587 static
settranscom(argc,argv)3588 settranscom(argc, argv)
3589 	int argc;
3590 	char *argv[];
3591 {
3592 	int i, len = 0;
3593 	char *strcpy(), *strcat();
3594 
3595 	if (argc == 1 && transcom) {
3596 	   transcom = 0;
3597 	}
3598 	if (argc == 1) {
3599 	   return;
3600 	}
3601 	for (i = 1; i < argc; ++i) {
3602 	    len += 1 + strlen(argv[1]);
3603 	}
3604 	transcom = tline;
3605 	(void) strcpy(transcom, argv[1]);
3606 	for (i = 2; i < argc; ++i) {
3607 	    (void) strcat(transcom, " ");
3608 	    (void) strcat(transcom, argv[i]);
3609 	}
3610 }
3611 #endif	/* defined(TN3270) && defined(unix) */
3612 
3613 
3614 
3615 static
tn(argc,argv)3616 tn(argc, argv)
3617 	int argc;
3618 	char *argv[];
3619 {
3620     register struct hostent *host = 0;
3621 #if defined(MSDOS)
3622     char *cp;
3623 #endif	/* defined(MSDOS) */
3624 
3625     if (connected) {
3626 	printf("?Already connected to %s\n", hostname);
3627 	return 0;
3628     }
3629     if (argc < 2) {
3630 	(void) strcpy(line, "Connect ");
3631 	printf("(to) ");
3632 	gets(&line[strlen(line)]);
3633 	makeargv();
3634 	argc = margc;
3635 	argv = margv;
3636     }
3637     if ((argc < 2) || (argc > 3)) {
3638 	printf("usage: %s host-name [port]\n", argv[0]);
3639 	return 0;
3640     }
3641 #if	defined(MSDOS)
3642     for (cp = argv[1]; *cp; cp++) {
3643 	if (isupper(*cp)) {
3644 	    *cp = tolower(*cp);
3645 	}
3646     }
3647 #endif	/* defined(MSDOS) */
3648     sin.sin_addr.s_addr = inet_addr(argv[1]);
3649     if (sin.sin_addr.s_addr != -1) {
3650 	sin.sin_family = AF_INET;
3651 	(void) strcpy(hnamebuf, argv[1]);
3652 	hostname = hnamebuf;
3653     } else {
3654 	host = gethostbyname(argv[1]);
3655 	if (host) {
3656 	    sin.sin_family = host->h_addrtype;
3657 #if	defined(h_addr)		/* In 4.3, this is a #define */
3658 	    memcpy((caddr_t)&sin.sin_addr,
3659 				host->h_addr_list[0], host->h_length);
3660 #else	/* defined(h_addr) */
3661 	    memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length);
3662 #endif	/* defined(h_addr) */
3663 	    hostname = host->h_name;
3664 	} else {
3665 	    printf("%s: unknown host\n", argv[1]);
3666 	    return 0;
3667 	}
3668     }
3669     sin.sin_port = sp->s_port;
3670     if (argc == 3) {
3671 	sin.sin_port = atoi(argv[2]);
3672 	if (sin.sin_port == 0) {
3673 	    sp = getservbyname(argv[2], "tcp");
3674 	    if (sp)
3675 		sin.sin_port = sp->s_port;
3676 	    else {
3677 		printf("%s: bad port number\n", argv[2]);
3678 		return 0;
3679 	    }
3680 	} else {
3681 	    sin.sin_port = atoi(argv[2]);
3682 	    sin.sin_port = htons(sin.sin_port);
3683 	}
3684 	telnetport = 0;
3685     } else {
3686 	telnetport = 1;
3687     }
3688 #if	defined(unix)
3689     signal(SIGINT, intr);
3690     signal(SIGQUIT, intr2);
3691     signal(SIGPIPE, deadpeer);
3692 #endif	/* defined(unix) */
3693     printf("Trying...\n");
3694     do {
3695 	net = socket(AF_INET, SOCK_STREAM, 0);
3696 	if (net < 0) {
3697 	    perror("telnet: socket");
3698 	    return 0;
3699 	}
3700 	if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) {
3701 		perror("setsockopt (SO_DEBUG)");
3702 	}
3703 
3704 	if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
3705 #if	defined(h_addr)		/* In 4.3, this is a #define */
3706 	    if (host && host->h_addr_list[1]) {
3707 		int oerrno = errno;
3708 
3709 		fprintf(stderr, "telnet: connect to address %s: ",
3710 						inet_ntoa(sin.sin_addr));
3711 		errno = oerrno;
3712 		perror((char *)0);
3713 		host->h_addr_list++;
3714 		memcpy((caddr_t)&sin.sin_addr,
3715 			host->h_addr_list[0], host->h_length);
3716 		fprintf(stderr, "Trying %s...\n",
3717 			inet_ntoa(sin.sin_addr));
3718 		(void) NetClose(net);
3719 		continue;
3720 	    }
3721 #endif	/* defined(h_addr) */
3722 	    perror("telnet: Unable to connect to remote host");
3723 #if defined(unix)
3724 	    signal(SIGINT, SIG_DFL);
3725 	    signal(SIGQUIT, SIG_DFL);
3726 #endif	/* defined(unix) */
3727 	    return 0;
3728 	    }
3729 	connected++;
3730     } while (connected == 0);
3731     call(status, "status", "notmuch", 0);
3732     if (setjmp(peerdied) == 0)
3733 	telnet();
3734     NetClose(net);
3735     ExitString(stderr, "Connection closed by foreign host.\n",1);
3736     /*NOTREACHED*/
3737 }
3738 
3739 
3740 #define HELPINDENT (sizeof ("connect"))
3741 
3742 static char
3743 	openhelp[] =	"connect to a site",
3744 	closehelp[] =	"close current connection",
3745 	quithelp[] =	"exit telnet",
3746 	statushelp[] =	"print status information",
3747 	helphelp[] =	"print help information",
3748 	sendhelp[] =	"transmit special characters ('send ?' for more)",
3749 	sethelp[] = 	"set operating parameters ('set ?' for more)",
3750 	togglestring[] ="toggle operating parameters ('toggle ?' for more)",
3751 	displayhelp[] =	"display operating parameters",
3752 #if	defined(TN3270) && defined(unix)
3753 	transcomhelp[] = "specify Unix command for transparent mode pipe",
3754 #endif	/* defined(TN3270) && defined(unix) */
3755 #if	defined(unix)
3756 	zhelp[] =	"suspend telnet",
3757 #endif	/* defined(unix */
3758 #if	defined(TN3270)
3759 	shellhelp[] =	"invoke a subshell",
3760 #endif	/* defined(TN3270) */
3761 	modehelp[] = "try to enter line-by-line or character-at-a-time mode";
3762 
3763 extern int	help(), shell();
3764 
3765 static struct cmd cmdtab[] = {
3766 	{ "close",	closehelp,	bye,		1, 1 },
3767 	{ "display",	displayhelp,	display,	1, 0 },
3768 	{ "mode",	modehelp,	modecmd,	1, 1 },
3769 	{ "open",	openhelp,	tn,		1, 0 },
3770 	{ "quit",	quithelp,	quit,		1, 0 },
3771 	{ "send",	sendhelp,	sendcmd,	1, 1 },
3772 	{ "set",	sethelp,	setcmd,		1, 0 },
3773 	{ "status",	statushelp,	status,		1, 0 },
3774 	{ "toggle",	togglestring,	toggle,		1, 0 },
3775 #if	defined(TN3270) && defined(unix)
3776 	{ "transcom",	transcomhelp,	settranscom,	1, 0 },
3777 #endif	/* defined(TN3270) && defined(unix) */
3778 #if	defined(unix)
3779 	{ "z",		zhelp,		suspend,	1, 0 },
3780 #endif	/* defined(unix) */
3781 #if	defined(TN3270)
3782 	{ "!",		shellhelp,	shell,		1, 1 },
3783 #endif	/* defined(TN3270) */
3784 	{ "?",		helphelp,	help,		1, 0 },
3785 	0
3786 };
3787 
3788 static char	crmodhelp[] =	"deprecated command -- use 'toggle crmod' instead";
3789 static char	escapehelp[] =	"deprecated command -- use 'set escape' instead";
3790 
3791 static struct cmd cmdtab2[] = {
3792 	{ "help",	helphelp,	help,		0, 0 },
3793 	{ "escape",	escapehelp,	setescape,	1, 0 },
3794 	{ "crmod",	crmodhelp,	togcrmod,	1, 0 },
3795 	0
3796 };
3797 
3798 /*
3799  * Call routine with argc, argv set from args (terminated by 0).
3800  * VARARGS2
3801  */
3802 static
3803 call(routine, args)
3804 	int (*routine)();
3805 	char *args;
3806 {
3807 	register char **argp;
3808 	register int argc;
3809 
3810 	for (argc = 0, argp = &args; *argp++ != 0; argc++)
3811 		;
3812 	return (*routine)(argc, &args);
3813 }
3814 
3815 static char **
getnextcmd(name)3816 getnextcmd(name)
3817 char *name;
3818 {
3819     struct cmd *c = (struct cmd *) name;
3820 
3821     return (char **) (c+1);
3822 }
3823 
3824 static struct cmd *
getcmd(name)3825 getcmd(name)
3826 char *name;
3827 {
3828     struct cmd *cm;
3829 
3830     if ((cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) != 0) {
3831 	return cm;
3832     } else {
3833 	return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
3834     }
3835 }
3836 
3837 void
command(top)3838 command(top)
3839 	int top;
3840 {
3841     register struct cmd *c;
3842 
3843     setcommandmode();
3844     if (!top) {
3845 	putchar('\n');
3846     } else {
3847 #if	defined(unix)
3848 	signal(SIGINT, SIG_DFL);
3849 	signal(SIGQUIT, SIG_DFL);
3850 #endif	/* defined(unix) */
3851     }
3852     for (;;) {
3853 	printf("%s> ", prompt);
3854 	if (gets(line) == NULL) {
3855 	    if (feof(stdin) || ferror(stdin))
3856 		quit();
3857 	    break;
3858 	}
3859 	if (line[0] == 0)
3860 	    break;
3861 	makeargv();
3862 	c = getcmd(margv[0]);
3863 	if (c == Ambiguous(struct cmd *)) {
3864 	    printf("?Ambiguous command\n");
3865 	    continue;
3866 	}
3867 	if (c == 0) {
3868 	    printf("?Invalid command\n");
3869 	    continue;
3870 	}
3871 	if (c->needconnect && !connected) {
3872 	    printf("?Need to be connected first.\n");
3873 	    continue;
3874 	}
3875 	if ((*c->handler)(margc, margv)) {
3876 	    break;
3877 	}
3878     }
3879     if (!top) {
3880 	if (!connected) {
3881 	    longjmp(toplevel, 1);
3882 	    /*NOTREACHED*/
3883 	}
3884 	if (shell_active == 0) {
3885 	    setconnmode();
3886 	}
3887     }
3888 }
3889 
3890 /*
3891  * Help command.
3892  */
3893 static
help(argc,argv)3894 help(argc, argv)
3895 	int argc;
3896 	char *argv[];
3897 {
3898 	register struct cmd *c;
3899 
3900 	if (argc == 1) {
3901 		printf("Commands may be abbreviated.  Commands are:\n\n");
3902 		for (c = cmdtab; c->name; c++)
3903 			if (c->dohelp) {
3904 				printf("%-*s\t%s\n", HELPINDENT, c->name,
3905 								    c->help);
3906 			}
3907 		return 0;
3908 	}
3909 	while (--argc > 0) {
3910 		register char *arg;
3911 		arg = *++argv;
3912 		c = getcmd(arg);
3913 		if (c == Ambiguous(struct cmd *))
3914 			printf("?Ambiguous help command %s\n", arg);
3915 		else if (c == (struct cmd *)0)
3916 			printf("?Invalid help command %s\n", arg);
3917 		else
3918 			printf("%s\n", c->help);
3919 	}
3920 	return 0;
3921 }
3922 
3923 /*
3924  * main.  Parse arguments, invoke the protocol or command parser.
3925  */
3926 
3927 
3928 void
main(argc,argv)3929 main(argc, argv)
3930 	int argc;
3931 	char *argv[];
3932 {
3933     tninit();		/* Clear out things */
3934 
3935     NetTrace = stdout;
3936     TerminalSaveState();
3937     autoflush = TerminalAutoFlush();
3938 
3939     prompt = argv[0];
3940     while ((argc > 1) && (argv[1][0] == '-')) {
3941 	if (!strcmp(argv[1], "-d")) {
3942 	    debug = 1;
3943 	} else if (!strcmp(argv[1], "-n")) {
3944 	    if ((argc > 1) && (argv[2][0] != '-')) {	/* get file name */
3945 		NetTrace = fopen(argv[2], "w");
3946 		argv++;
3947 		argc--;
3948 		if (NetTrace == NULL) {
3949 		    NetTrace = stdout;
3950 		}
3951 	    }
3952 	} else {
3953 #if	defined(TN3270) && defined(unix)
3954 	    if (!strcmp(argv[1], "-t")) {
3955 		if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */
3956 		    transcom = tline;
3957 		    (void) strcpy(transcom, argv[1]);
3958 		    argv++;
3959 		    argc--;
3960 		}
3961 	    } else if (!strcmp(argv[1], "-noasynch")) {
3962 		noasynch = 1;
3963 	    } else
3964 #endif	/* defined(TN3270) && defined(unix) */
3965 	    if (argv[1][1] != '\0') {
3966 		fprintf(stderr, "Unknown option *%s*.\n", argv[1]);
3967 	    }
3968 	}
3969 	argc--;
3970 	argv++;
3971     }
3972     if (argc != 1) {
3973 	if (setjmp(toplevel) != 0)
3974 	    Exit(0);
3975 	tn(argc, argv);
3976     }
3977     setjmp(toplevel);
3978     for (;;) {
3979 #if	!defined(TN3270)
3980 	command(1);
3981 #else	/* !defined(TN3270) */
3982 	if (!shell_active) {
3983 	    command(1);
3984 	} else {
3985 #if	defined(TN3270)
3986 	    shell_continue();
3987 #endif	/* defined(TN3270) */
3988 	}
3989 #endif	/* !defined(TN3270) */
3990     }
3991 }
3992