xref: /original-bsd/usr.bin/telnet/telnet.c (revision 4f5736a5)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)telnet.c	5.33 (Berkeley) 6/29/88";
20 #endif /* not lint */
21 
22 #include <sys/types.h>
23 
24 #if	defined(unix)
25 #include <signal.h>
26 /* By the way, we need to include curses.h before telnet.h since,
27  * among other things, telnet.h #defines 'DO', which is a variable
28  * declared in curses.h.
29  */
30 #include <curses.h>
31 #endif	/* defined(unix) */
32 
33 #include <arpa/telnet.h>
34 
35 #if	defined(unix)
36 #include <strings.h>
37 #else	/* defined(unix) */
38 #include <string.h>
39 #endif	/* defined(unix) */
40 
41 #include "ring.h"
42 
43 #include "defines.h"
44 #include "externs.h"
45 #include "types.h"
46 #include "general.h"
47 
48 
49 #define	strip(x)	((x)&0x7f)
50 
51 
52 static char	subbuffer[SUBBUFSIZE],
53 		*subpointer, *subend;	 /* buffer for sub-options */
54 #define	SB_CLEAR()	subpointer = subbuffer;
55 #define	SB_TERM()	subend = subpointer;
56 #define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \
57 				*subpointer++ = (c); \
58 			}
59 
60 char	hisopts[256];
61 char	myopts[256];
62 
63 char	doopt[] = { IAC, DO, '%', 'c', 0 };
64 char	dont[] = { IAC, DONT, '%', 'c', 0 };
65 char	will[] = { IAC, WILL, '%', 'c', 0 };
66 char	wont[] = { IAC, WONT, '%', 'c', 0 };
67 
68 int
69 	connected,
70 	showoptions,
71 	In3270,		/* Are we in 3270 mode? */
72 	ISend,		/* trying to send network data in */
73 	debug = 0,
74 	crmod,
75 	netdata,	/* Print out network data flow */
76 	crlf,		/* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
77 #if	defined(TN3270)
78 	noasynchtty = 0,/* User specified "-noasynch" on command line */
79 	noasynchnet = 0,/* User specified "-noasynch" on command line */
80 	askedSGA = 0,	/* We have talked about suppress go ahead */
81 #endif	/* defined(TN3270) */
82 	telnetport,
83 	SYNCHing,	/* we are in TELNET SYNCH mode */
84 	flushout,	/* flush output */
85 	autoflush = 0,	/* flush output when interrupting? */
86 	autosynch,	/* send interrupt characters with SYNCH? */
87 	localchars,	/* we recognize interrupt/quit */
88 	donelclchars,	/* the user has set "localchars" */
89 	donebinarytoggle,	/* the user has put us in binary */
90 	dontlecho,	/* do we suppress local echoing right now? */
91 	globalmode;
92 
93 #define	CONTROL(x)	((x)&0x1f)		/* CTRL(x) is not portable */
94 
95 char
96 	*prompt = 0,
97 	escape,
98 	echoc;
99 
100 /*
101  * Telnet receiver states for fsm
102  */
103 #define	TS_DATA		0
104 #define	TS_IAC		1
105 #define	TS_WILL		2
106 #define	TS_WONT		3
107 #define	TS_DO		4
108 #define	TS_DONT		5
109 #define	TS_CR		6
110 #define	TS_SB		7		/* sub-option collection */
111 #define	TS_SE		8		/* looking for sub-option end */
112 
113 static int	telrcv_state;
114 
115 jmp_buf	toplevel = { 0 };
116 jmp_buf	peerdied;
117 
118 int	flushline;
119 
120 /*
121  * The following are some clocks used to decide how to interpret
122  * the relationship between various variables.
123  */
124 
125 Clocks clocks;
126 
127 Modelist modelist[] = {
128 	{ "telnet command mode", COMMAND_LINE },
129 	{ "character-at-a-time mode", 0 },
130 	{ "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
131 	{ "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
132 	{ "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
133 	{ "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
134 	{ "3270 mode", 0 },
135 };
136 
137 
138 /*
139  * Initialize telnet environment.
140  */
141 
142 init_telnet()
143 {
144     SB_CLEAR();
145     ClearArray(hisopts);
146     ClearArray(myopts);
147 
148     connected = In3270 = ISend = donebinarytoggle = 0;
149 
150     SYNCHing = 0;
151 
152     /* Don't change NetTrace */
153 
154     escape = CONTROL(']');
155     echoc = CONTROL('E');
156 
157     flushline = 1;
158     telrcv_state = TS_DATA;
159 }
160 
161 
162 #include <varargs.h>
163 
164 /*VARARGS*/
165 static void
166 printring(va_alist)
167 va_dcl
168 {
169     va_list ap;
170     char buffer[100];		/* where things go */
171     char *ptr;
172     char *format;
173     char *string;
174     Ring *ring;
175     int i;
176 
177     va_start(ap);
178 
179     ring = va_arg(ap, Ring *);
180     format = va_arg(ap, char *);
181     ptr = buffer;
182 
183     while ((i = *format++) != 0) {
184 	if (i == '%') {
185 	    i = *format++;
186 	    switch (i) {
187 	    case 'c':
188 		*ptr++ = va_arg(ap, int);
189 		break;
190 	    case 's':
191 		string = va_arg(ap, char *);
192 		ring_supply_data(ring, buffer, ptr-buffer);
193 		ring_supply_data(ring, string, strlen(string));
194 		ptr = buffer;
195 		break;
196 	    case 0:
197 		ExitString("printring: trailing %%.\n", 1);
198 		/*NOTREACHED*/
199 	    default:
200 		ExitString("printring: unknown format character.\n", 1);
201 		/*NOTREACHED*/
202 	    }
203 	} else {
204 	    *ptr++ = i;
205 	}
206     }
207     ring_supply_data(ring, buffer, ptr-buffer);
208 }
209 
210 
211 void
212 willoption(option, reply)
213 	int option, reply;
214 {
215 	char *fmt;
216 
217 	switch (option) {
218 
219 	case TELOPT_ECHO:
220 #	if defined(TN3270)
221 	    /*
222 	     * The following is a pain in the rear-end.
223 	     * Various IBM servers (some versions of Wiscnet,
224 	     * possibly Fibronics/Spartacus, and who knows who
225 	     * else) will NOT allow us to send "DO SGA" too early
226 	     * in the setup proceedings.  On the other hand,
227 	     * 4.2 servers (telnetd) won't set SGA correctly.
228 	     * So, we are stuck.  Empirically (but, based on
229 	     * a VERY small sample), the IBM servers don't send
230 	     * out anything about ECHO, so we postpone our sending
231 	     * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
232 	     * DO send).
233 	     */
234 	    {
235 		if (askedSGA == 0) {
236 		    askedSGA = 1;
237 		    if (!hisopts[TELOPT_SGA]) {
238 			willoption(TELOPT_SGA, 0);
239 		    }
240 		}
241 	    }
242 		/* Fall through */
243 	case TELOPT_EOR:
244 	case TELOPT_BINARY:
245 #endif	/* defined(TN3270) */
246 	case TELOPT_SGA:
247 		settimer(modenegotiated);
248 		hisopts[option] = 1;
249 		fmt = doopt;
250 		setconnmode();		/* possibly set new tty mode */
251 		break;
252 
253 	case TELOPT_TM:
254 		return;			/* Never reply to TM will's/wont's */
255 
256 	default:
257 		fmt = dont;
258 		break;
259 	}
260 	printring(&netoring, fmt, option);
261 	if (reply)
262 		printoption(">SENT", fmt, option, reply);
263 	else
264 		printoption("<SENT", fmt, option, reply);
265 }
266 
267 void
268 wontoption(option, reply)
269 	int option, reply;
270 {
271 	char *fmt;
272 
273 	switch (option) {
274 
275 	case TELOPT_ECHO:
276 	case TELOPT_SGA:
277 		settimer(modenegotiated);
278 		hisopts[option] = 0;
279 		fmt = dont;
280 		setconnmode();			/* Set new tty mode */
281 		break;
282 
283 	case TELOPT_TM:
284 		return;		/* Never reply to TM will's/wont's */
285 
286 	default:
287 		fmt = dont;
288 	}
289 	printring(&netoring, fmt, option);
290 	if (reply)
291 		printoption(">SENT", fmt, option, reply);
292 	else
293 		printoption("<SENT", fmt, option, reply);
294 }
295 
296 static void
297 dooption(option)
298 	int option;
299 {
300 	char *fmt;
301 
302 	switch (option) {
303 
304 	case TELOPT_TM:
305 		fmt = will;
306 		break;
307 
308 #	if defined(TN3270)
309 	case TELOPT_EOR:
310 	case TELOPT_BINARY:
311 #	endif	/* defined(TN3270) */
312 	case TELOPT_TTYPE:		/* terminal type option */
313 	case TELOPT_SGA:		/* no big deal */
314 		fmt = will;
315 		myopts[option] = 1;
316 		break;
317 
318 	case TELOPT_ECHO:		/* We're never going to echo... */
319 	default:
320 		fmt = wont;
321 		break;
322 	}
323 	printring(&netoring, fmt, option);
324 	printoption(">SENT", fmt, option, 0);
325 }
326 
327 /*
328  * suboption()
329  *
330  *	Look at the sub-option buffer, and try to be helpful to the other
331  * side.
332  *
333  *	Currently we recognize:
334  *
335  *		Terminal type, send request.
336  */
337 
338 static void
339 suboption()
340 {
341     printsub("<", subbuffer, subend-subbuffer+1);
342     switch (subbuffer[0]&0xff) {
343     case TELOPT_TTYPE:
344 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
345 	    ;
346 	} else {
347 	    char *name;
348 	    char namebuf[41];
349 	    extern char *getenv();
350 	    int len;
351 
352 #if	defined(TN3270)
353 	    if (tn3270_ttype()) {
354 		return;
355 	    }
356 #endif	/* defined(TN3270) */
357 	    name = getenv("TERM");
358 	    if ((name == 0) || ((len = strlen(name)) > 40)) {
359 		name = "UNKNOWN";
360 		len = strlen(name);
361 	    }
362 	    if ((len + 4+2) < NETROOM()) {
363 		strcpy(namebuf, name);
364 		upcase(namebuf);
365 		printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
366 				    TELQUAL_IS, namebuf, IAC, SE);
367 		/* XXX */
368 		/* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
369 	    } else {
370 		ExitString("No room in buffer for terminal type.\n",
371 							1);
372 		/*NOTREACHED*/
373 	    }
374 	}
375 
376     default:
377 	break;
378     }
379 }
380 
381 
382 int
383 telrcv()
384 {
385     register int c;
386     register int scc;
387     register char *sbp;
388     int count;
389     int returnValue = 0;
390 
391     scc = 0;
392     count = 0;
393     while (TTYROOM() > 2) {
394 	if (scc == 0) {
395 	    if (count) {
396 		ring_consumed(&netiring, count);
397 		returnValue = 1;
398 		count = 0;
399 	    }
400 	    sbp = netiring.consume;
401 	    scc = ring_full_consecutive(&netiring);
402 	    if (scc == 0) {
403 		/* No more data coming in */
404 		break;
405 	    }
406 	}
407 
408 	c = *sbp++ & 0xff, scc--; count++;
409 
410 	switch (telrcv_state) {
411 
412 	case TS_CR:
413 	    telrcv_state = TS_DATA;
414 	    if (c == '\0') {
415 		break;	/* Ignore \0 after CR */
416 	    } else if ((c == '\n') && (!hisopts[TELOPT_ECHO]) && !crmod) {
417 		TTYADD(c);
418 		break;
419 	    }
420 	    /* Else, fall through */
421 
422 	case TS_DATA:
423 	    if (c == IAC) {
424 		telrcv_state = TS_IAC;
425 		break;
426 	    }
427 #	    if defined(TN3270)
428 	    if (In3270) {
429 		*Ifrontp++ = c;
430 		while (scc > 0) {
431 		    c = *sbp++ & 0377, scc--; count++;
432 		    if (c == IAC) {
433 			telrcv_state = TS_IAC;
434 			break;
435 		    }
436 		    *Ifrontp++ = c;
437 		}
438 	    } else
439 #	    endif /* defined(TN3270) */
440 		    /*
441 		     * The 'crmod' hack (see following) is needed
442 		     * since we can't * set CRMOD on output only.
443 		     * Machines like MULTICS like to send \r without
444 		     * \n; since we must turn off CRMOD to get proper
445 		     * input, the mapping is done here (sigh).
446 		     */
447 	    if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
448 		if (scc > 0) {
449 		    c = *sbp&0xff;
450 		    if (c == 0) {
451 			sbp++, scc--; count++;
452 			/* a "true" CR */
453 			TTYADD('\r');
454 		    } else if (!hisopts[TELOPT_ECHO] &&
455 					(c == '\n')) {
456 			sbp++, scc--; count++;
457 			TTYADD('\n');
458 		    } else {
459 			TTYADD('\r');
460 			if (crmod) {
461 				TTYADD('\n');
462 			}
463 		    }
464 		} else {
465 		    telrcv_state = TS_CR;
466 		    TTYADD('\r');
467 		    if (crmod) {
468 			    TTYADD('\n');
469 		    }
470 		}
471 	    } else {
472 		TTYADD(c);
473 	    }
474 	    continue;
475 
476 	case TS_IAC:
477 	    switch (c) {
478 
479 	    case WILL:
480 		telrcv_state = TS_WILL;
481 		continue;
482 
483 	    case WONT:
484 		telrcv_state = TS_WONT;
485 		continue;
486 
487 	    case DO:
488 		telrcv_state = TS_DO;
489 		continue;
490 
491 	    case DONT:
492 		telrcv_state = TS_DONT;
493 		continue;
494 
495 	    case DM:
496 		    /*
497 		     * We may have missed an urgent notification,
498 		     * so make sure we flush whatever is in the
499 		     * buffer currently.
500 		     */
501 		SYNCHing = 1;
502 		ttyflush(1);
503 		SYNCHing = stilloob();
504 		settimer(gotDM);
505 		break;
506 
507 	    case NOP:
508 	    case GA:
509 		break;
510 
511 	    case SB:
512 		SB_CLEAR();
513 		telrcv_state = TS_SB;
514 		continue;
515 
516 #	    if defined(TN3270)
517 	    case EOR:
518 		if (In3270) {
519 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
520 		    if (Ibackp == Ifrontp) {
521 			Ibackp = Ifrontp = Ibuf;
522 			ISend = 0;	/* should have been! */
523 		    } else {
524 			ISend = 1;
525 		    }
526 		}
527 		break;
528 #	    endif /* defined(TN3270) */
529 
530 	    case IAC:
531 #	    if !defined(TN3270)
532 		TTYADD(IAC);
533 #	    else /* !defined(TN3270) */
534 		if (In3270) {
535 		    *Ifrontp++ = IAC;
536 		} else {
537 		    TTYADD(IAC);
538 		}
539 #	    endif /* !defined(TN3270) */
540 		break;
541 
542 	    default:
543 		break;
544 	    }
545 	    telrcv_state = TS_DATA;
546 	    continue;
547 
548 	case TS_WILL:
549 	    printoption(">RCVD", will, c, !hisopts[c]);
550 	    if (c == TELOPT_TM) {
551 		if (flushout) {
552 		    flushout = 0;
553 		}
554 	    } else if (!hisopts[c]) {
555 		willoption(c, 1);
556 	    }
557 	    SetIn3270();
558 	    telrcv_state = TS_DATA;
559 	    continue;
560 
561 	case TS_WONT:
562 	    printoption(">RCVD", wont, c, hisopts[c]);
563 	    if (c == TELOPT_TM) {
564 		if (flushout) {
565 		    flushout = 0;
566 		}
567 	    } else if (hisopts[c]) {
568 		wontoption(c, 1);
569 	    }
570 	    SetIn3270();
571 	    telrcv_state = TS_DATA;
572 	    continue;
573 
574 	case TS_DO:
575 	    printoption(">RCVD", doopt, c, !myopts[c]);
576 	    if (!myopts[c])
577 		dooption(c);
578 	    SetIn3270();
579 	    telrcv_state = TS_DATA;
580 	    continue;
581 
582 	case TS_DONT:
583 	    printoption(">RCVD", dont, c, myopts[c]);
584 	    if (myopts[c]) {
585 		myopts[c] = 0;
586 		printring(&netoring, wont, c);
587 		flushline = 1;
588 		setconnmode();	/* set new tty mode (maybe) */
589 		printoption(">SENT", wont, c, 0);
590 	    }
591 	    SetIn3270();
592 	    telrcv_state = TS_DATA;
593 	    continue;
594 
595 	case TS_SB:
596 	    if (c == IAC) {
597 		telrcv_state = TS_SE;
598 	    } else {
599 		SB_ACCUM(c);
600 	    }
601 	    continue;
602 
603 	case TS_SE:
604 	    if (c != SE) {
605 		if (c != IAC) {
606 		    SB_ACCUM(IAC);
607 		}
608 		SB_ACCUM(c);
609 		telrcv_state = TS_SB;
610 	    } else {
611 		SB_TERM();
612 		suboption();	/* handle sub-option */
613 		SetIn3270();
614 		telrcv_state = TS_DATA;
615 	    }
616 	}
617     }
618     if (count)
619 	ring_consumed(&netiring, count);
620     return returnValue||count;
621 }
622 
623 static int
624 telsnd()
625 {
626     int tcc;
627     int count;
628     int returnValue = 0;
629     char *tbp;
630 
631     tcc = 0;
632     count = 0;
633     while (NETROOM() > 2) {
634 	register int sc;
635 	register int c;
636 
637 	if (tcc == 0) {
638 	    if (count) {
639 		ring_consumed(&ttyiring, count);
640 		returnValue = 1;
641 		count = 0;
642 	    }
643 	    tbp = ttyiring.consume;
644 	    tcc = ring_full_consecutive(&ttyiring);
645 	    if (tcc == 0) {
646 		break;
647 	    }
648 	}
649 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
650 	if (sc == escape) {
651 	    command(0);
652 	    tcc = 0;
653 	    flushline = 1;
654 	    break;
655 	} else if (MODE_LINE(globalmode) && (sc == echoc)) {
656 	    if (tcc > 0 && strip(*tbp) == echoc) {
657 		tcc--; tbp++; count++;
658 	    } else {
659 		dontlecho = !dontlecho;
660 		settimer(echotoggle);
661 		setconnmode();
662 		flushline = 1;
663 		break;
664 	    }
665 	}
666 	if (localchars) {
667 	    if (TerminalSpecialChars(sc) == 0) {
668 		break;
669 	    }
670 	}
671 	if (!myopts[TELOPT_BINARY]) {
672 	    switch (c) {
673 	    case '\n':
674 		    /*
675 		     * If we are in CRMOD mode (\r ==> \n)
676 		     * on our local machine, then probably
677 		     * a newline (unix) is CRLF (TELNET).
678 		     */
679 		if (MODE_LOCAL_CHARS(globalmode)) {
680 		    NETADD('\r');
681 		}
682 		NETADD('\n');
683 		flushline = 1;
684 		break;
685 	    case '\r':
686 		if (!crlf) {
687 		    NET2ADD('\r', '\0');
688 		} else {
689 		    NET2ADD('\r', '\n');
690 		}
691 		flushline = 1;
692 		break;
693 	    case IAC:
694 		NET2ADD(IAC, IAC);
695 		break;
696 	    default:
697 		NETADD(c);
698 		break;
699 	    }
700 	} else if (c == IAC) {
701 	    NET2ADD(IAC, IAC);
702 	} else {
703 	    NETADD(c);
704 	}
705     }
706     if (count)
707 	ring_consumed(&ttyiring, count);
708     return returnValue||count;		/* Non-zero if we did anything */
709 }
710 
711 /*
712  * Scheduler()
713  *
714  * Try to do something.
715  *
716  * If we do something useful, return 1; else return 0.
717  *
718  */
719 
720 
721 int
722 Scheduler(block)
723 int	block;			/* should we block in the select ? */
724 {
725 		/* One wants to be a bit careful about setting returnValue
726 		 * to one, since a one implies we did some useful work,
727 		 * and therefore probably won't be called to block next
728 		 * time (TN3270 mode only).
729 		 */
730     int returnValue;
731     int netin, netout, netex, ttyin, ttyout;
732 
733     /* Decide which rings should be processed */
734 
735     netout = ring_full_count(&netoring) &&
736 	    (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]);
737     ttyout = ring_full_count(&ttyoring);
738 
739 #if	defined(TN3270)
740     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
741 #else	/* defined(TN3270) */
742     ttyin = ring_empty_count(&ttyiring);
743 #endif	/* defined(TN3270) */
744 
745 #if	defined(TN3270)
746     netin = ring_empty_count(&netiring);
747 #   else /* !defined(TN3270) */
748     netin = !ISend && ring_empty_count(&netiring);
749 #   endif /* !defined(TN3270) */
750 
751     netex = !SYNCHing;
752 
753     /* If we have seen a signal recently, reset things */
754 #   if defined(TN3270) && defined(unix)
755     if (HaveInput) {
756 	HaveInput = 0;
757 	signal(SIGIO, inputAvailable);
758     }
759 #endif	/* defined(TN3270) && defined(unix) */
760 
761     /* Call to system code to process rings */
762 
763     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
764 
765     /* Now, look at the input rings, looking for work to do. */
766 
767     if (ring_full_count(&ttyiring)) {
768 #   if defined(TN3270)
769 	if (In3270) {
770 	    int c;
771 
772 	    c = DataFromTerminal(ttyiring.consume,
773 					ring_full_consecutive(&ttyiring));
774 	    if (c) {
775 		returnValue = 1;
776 	        ring_consumed(&ttyiring, c);
777 	    }
778 	} else {
779 #   endif /* defined(TN3270) */
780 	    returnValue |= telsnd();
781 #   if defined(TN3270)
782 	}
783 #   endif /* defined(TN3270) */
784     }
785 
786     if (ring_full_count(&netiring)) {
787 #	if !defined(TN3270)
788 	returnValue |= telrcv();
789 #	else /* !defined(TN3270) */
790 	returnValue = Push3270();
791 #	endif /* !defined(TN3270) */
792     }
793     return returnValue;
794 }
795 
796 /*
797  * Select from tty and network...
798  */
799 void
800 telnet()
801 {
802     sys_telnet_init();
803 
804 #   if !defined(TN3270)
805     if (telnetport) {
806 	if (!hisopts[TELOPT_SGA]) {
807 	    willoption(TELOPT_SGA, 0);
808 	}
809 	if (!myopts[TELOPT_TTYPE]) {
810 	    dooption(TELOPT_TTYPE);
811 	}
812     }
813 #   endif /* !defined(TN3270) */
814 
815 #   if !defined(TN3270)
816     for (;;) {
817 	int schedValue;
818 
819 	while ((schedValue = Scheduler(0)) != 0) {
820 	    if (schedValue == -1) {
821 		setcommandmode();
822 		return;
823 	    }
824 	}
825 
826 	if (Scheduler(1) == -1) {
827 	    setcommandmode();
828 	    return;
829 	}
830     }
831 #   else /* !defined(TN3270) */
832     for (;;) {
833 	int schedValue;
834 
835 	while (!In3270 && !shell_active) {
836 	    if (Scheduler(1) == -1) {
837 		setcommandmode();
838 		return;
839 	    }
840 	}
841 
842 	while ((schedValue = Scheduler(0)) != 0) {
843 	    if (schedValue == -1) {
844 		setcommandmode();
845 		return;
846 	    }
847 	}
848 		/* If there is data waiting to go out to terminal, don't
849 		 * schedule any more data for the terminal.
850 		 */
851 	if (ring_full_count(&ttyoring)) {
852 	    schedValue = 1;
853 	} else {
854 	    if (shell_active) {
855 		if (shell_continue() == 0) {
856 		    ConnectScreen();
857 		}
858 	    } else if (In3270) {
859 		schedValue = DoTerminalOutput();
860 	    }
861 	}
862 	if (schedValue && (shell_active == 0)) {
863 	    if (Scheduler(1) == -1) {
864 		setcommandmode();
865 		return;
866 	    }
867 	}
868     }
869 #   endif /* !defined(TN3270) */
870 }
871 
872 #if	0	/* XXX - this not being in is a bug */
873 /*
874  * nextitem()
875  *
876  *	Return the address of the next "item" in the TELNET data
877  * stream.  This will be the address of the next character if
878  * the current address is a user data character, or it will
879  * be the address of the character following the TELNET command
880  * if the current address is a TELNET IAC ("I Am a Command")
881  * character.
882  */
883 
884 static char *
885 nextitem(current)
886 char	*current;
887 {
888     if ((*current&0xff) != IAC) {
889 	return current+1;
890     }
891     switch (*(current+1)&0xff) {
892     case DO:
893     case DONT:
894     case WILL:
895     case WONT:
896 	return current+3;
897     case SB:		/* loop forever looking for the SE */
898 	{
899 	    register char *look = current+2;
900 
901 	    for (;;) {
902 		if ((*look++&0xff) == IAC) {
903 		    if ((*look++&0xff) == SE) {
904 			return look;
905 		    }
906 		}
907 	    }
908 	}
909     default:
910 	return current+2;
911     }
912 }
913 #endif	/* 0 */
914 
915 /*
916  * netclear()
917  *
918  *	We are about to do a TELNET SYNCH operation.  Clear
919  * the path to the network.
920  *
921  *	Things are a bit tricky since we may have sent the first
922  * byte or so of a previous TELNET command into the network.
923  * So, we have to scan the network buffer from the beginning
924  * until we are up to where we want to be.
925  *
926  *	A side effect of what we do, just to keep things
927  * simple, is to clear the urgent data pointer.  The principal
928  * caller should be setting the urgent data pointer AFTER calling
929  * us in any case.
930  */
931 
932 static void
933 netclear()
934 {
935 #if	0	/* XXX */
936     register char *thisitem, *next;
937     char *good;
938 #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
939 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
940 
941     thisitem = netobuf;
942 
943     while ((next = nextitem(thisitem)) <= netobuf.send) {
944 	thisitem = next;
945     }
946 
947     /* Now, thisitem is first before/at boundary. */
948 
949     good = netobuf;	/* where the good bytes go */
950 
951     while (netoring.add > thisitem) {
952 	if (wewant(thisitem)) {
953 	    int length;
954 
955 	    next = thisitem;
956 	    do {
957 		next = nextitem(next);
958 	    } while (wewant(next) && (nfrontp > next));
959 	    length = next-thisitem;
960 	    memcpy(good, thisitem, length);
961 	    good += length;
962 	    thisitem = next;
963 	} else {
964 	    thisitem = nextitem(thisitem);
965 	}
966     }
967 
968 #endif	/* 0 */
969 }
970 
971 /*
972  * These routines add various telnet commands to the data stream.
973  */
974 
975 static void
976 doflush()
977 {
978     NET2ADD(IAC, DO);
979     NETADD(TELOPT_TM);
980     flushline = 1;
981     flushout = 1;
982     ttyflush(1);			/* Flush/drop output */
983     /* do printoption AFTER flush, otherwise the output gets tossed... */
984     printoption("<SENT", doopt, TELOPT_TM, 0);
985 }
986 
987 void
988 xmitAO()
989 {
990     NET2ADD(IAC, AO);
991     if (autoflush) {
992 	doflush();
993     }
994 }
995 
996 
997 void
998 xmitEL()
999 {
1000     NET2ADD(IAC, EL);
1001 }
1002 
1003 void
1004 xmitEC()
1005 {
1006     NET2ADD(IAC, EC);
1007 }
1008 
1009 
1010 #if	defined(NOT43)
1011 int
1012 #else	/* defined(NOT43) */
1013 void
1014 #endif	/* defined(NOT43) */
1015 dosynch()
1016 {
1017     netclear();			/* clear the path to the network */
1018     NETADD(IAC);
1019     setneturg();
1020     NETADD(DM);
1021 
1022 #if	defined(NOT43)
1023     return 0;
1024 #endif	/* defined(NOT43) */
1025 }
1026 
1027 void
1028 intp()
1029 {
1030     NET2ADD(IAC, IP);
1031     flushline = 1;
1032     if (autoflush) {
1033 	doflush();
1034     }
1035     if (autosynch) {
1036 	dosynch();
1037     }
1038 }
1039 
1040 void
1041 sendbrk()
1042 {
1043     NET2ADD(IAC, BREAK);
1044     flushline = 1;
1045     if (autoflush) {
1046 	doflush();
1047     }
1048     if (autosynch) {
1049 	dosynch();
1050     }
1051 }
1052