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