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