xref: /original-bsd/usr.bin/telnet/telnet.c (revision 444dc9d9)
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.38 (Berkeley) 02/06/89";
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 		hisopts[option] = 0;
288 		fmt = dont;
289 	}
290 	printring(&netoring, fmt, option);
291 	if (reply)
292 		printoption(">SENT", fmt, option, reply);
293 	else
294 		printoption("<SENT", fmt, option, reply);
295 }
296 
297 static void
298 dooption(option)
299 	int option;
300 {
301 	char *fmt;
302 
303 	switch (option) {
304 
305 	case TELOPT_TM:
306 		fmt = will;
307 		break;
308 
309 #	if defined(TN3270)
310 	case TELOPT_EOR:
311 	case TELOPT_BINARY:
312 #	endif	/* defined(TN3270) */
313 	case TELOPT_TTYPE:		/* terminal type option */
314 	case TELOPT_SGA:		/* no big deal */
315 		fmt = will;
316 		myopts[option] = 1;
317 		break;
318 
319 	case TELOPT_ECHO:		/* We're never going to echo... */
320 	default:
321 		fmt = wont;
322 		break;
323 	}
324 	printring(&netoring, fmt, option);
325 	printoption(">SENT", fmt, option, 0);
326 }
327 
328 /*
329  * suboption()
330  *
331  *	Look at the sub-option buffer, and try to be helpful to the other
332  * side.
333  *
334  *	Currently we recognize:
335  *
336  *		Terminal type, send request.
337  */
338 
339 static void
340 suboption()
341 {
342     printsub("<", subbuffer, subend-subbuffer+1);
343     switch (subbuffer[0]&0xff) {
344     case TELOPT_TTYPE:
345 	if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
346 	    ;
347 	} else {
348 	    char *name;
349 	    char namebuf[41];
350 	    extern char *getenv();
351 	    int len;
352 
353 #if	defined(TN3270)
354 	    if (tn3270_ttype()) {
355 		return;
356 	    }
357 #endif	/* defined(TN3270) */
358 	    name = getenv("TERM");
359 	    if ((name == 0) || ((len = strlen(name)) > 40)) {
360 		name = "UNKNOWN";
361 		len = strlen(name);
362 	    }
363 	    if ((len + 4+2) < NETROOM()) {
364 		strcpy(namebuf, name);
365 		upcase(namebuf);
366 		printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
367 				    TELQUAL_IS, namebuf, IAC, SE);
368 		/* XXX */
369 		/* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
370 	    } else {
371 		ExitString("No room in buffer for terminal type.\n",
372 							1);
373 		/*NOTREACHED*/
374 	    }
375 	}
376 
377     default:
378 	break;
379     }
380 }
381 
382 
383 int
384 telrcv()
385 {
386     register int c;
387     register int scc;
388     register char *sbp;
389     int count;
390     int returnValue = 0;
391 
392     scc = 0;
393     count = 0;
394     while (TTYROOM() > 2) {
395 	if (scc == 0) {
396 	    if (count) {
397 		ring_consumed(&netiring, count);
398 		returnValue = 1;
399 		count = 0;
400 	    }
401 	    sbp = netiring.consume;
402 	    scc = ring_full_consecutive(&netiring);
403 	    if (scc == 0) {
404 		/* No more data coming in */
405 		break;
406 	    }
407 	}
408 
409 	c = *sbp++ & 0xff, scc--; count++;
410 
411 	switch (telrcv_state) {
412 
413 	case TS_CR:
414 	    telrcv_state = TS_DATA;
415 	    if (c == '\0') {
416 		break;	/* Ignore \0 after CR */
417 	    } else if ((c == '\n') && (!hisopts[TELOPT_ECHO]) && !crmod) {
418 		TTYADD(c);
419 		break;
420 	    }
421 	    /* Else, fall through */
422 
423 	case TS_DATA:
424 	    if (c == IAC) {
425 		telrcv_state = TS_IAC;
426 		break;
427 	    }
428 #	    if defined(TN3270)
429 	    if (In3270) {
430 		*Ifrontp++ = c;
431 		while (scc > 0) {
432 		    c = *sbp++ & 0377, scc--; count++;
433 		    if (c == IAC) {
434 			telrcv_state = TS_IAC;
435 			break;
436 		    }
437 		    *Ifrontp++ = c;
438 		}
439 	    } else
440 #	    endif /* defined(TN3270) */
441 		    /*
442 		     * The 'crmod' hack (see following) is needed
443 		     * since we can't * set CRMOD on output only.
444 		     * Machines like MULTICS like to send \r without
445 		     * \n; since we must turn off CRMOD to get proper
446 		     * input, the mapping is done here (sigh).
447 		     */
448 	    if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
449 		if (scc > 0) {
450 		    c = *sbp&0xff;
451 		    if (c == 0) {
452 			sbp++, scc--; count++;
453 			/* a "true" CR */
454 			TTYADD('\r');
455 		    } else if (!hisopts[TELOPT_ECHO] &&
456 					(c == '\n')) {
457 			sbp++, scc--; count++;
458 			TTYADD('\n');
459 		    } else {
460 			TTYADD('\r');
461 			if (crmod) {
462 				TTYADD('\n');
463 			}
464 		    }
465 		} else {
466 		    telrcv_state = TS_CR;
467 		    TTYADD('\r');
468 		    if (crmod) {
469 			    TTYADD('\n');
470 		    }
471 		}
472 	    } else {
473 		TTYADD(c);
474 	    }
475 	    continue;
476 
477 	case TS_IAC:
478 	    switch (c) {
479 
480 	    case WILL:
481 		telrcv_state = TS_WILL;
482 		continue;
483 
484 	    case WONT:
485 		telrcv_state = TS_WONT;
486 		continue;
487 
488 	    case DO:
489 		telrcv_state = TS_DO;
490 		continue;
491 
492 	    case DONT:
493 		telrcv_state = TS_DONT;
494 		continue;
495 
496 	    case DM:
497 		    /*
498 		     * We may have missed an urgent notification,
499 		     * so make sure we flush whatever is in the
500 		     * buffer currently.
501 		     */
502 		SYNCHing = 1;
503 		ttyflush(1);
504 		SYNCHing = stilloob();
505 		settimer(gotDM);
506 		break;
507 
508 	    case NOP:
509 	    case GA:
510 		break;
511 
512 	    case SB:
513 		SB_CLEAR();
514 		telrcv_state = TS_SB;
515 		continue;
516 
517 #	    if defined(TN3270)
518 	    case EOR:
519 		if (In3270) {
520 		    Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1);
521 		    if (Ibackp == Ifrontp) {
522 			Ibackp = Ifrontp = Ibuf;
523 			ISend = 0;	/* should have been! */
524 		    } else {
525 			ISend = 1;
526 		    }
527 		}
528 		break;
529 #	    endif /* defined(TN3270) */
530 
531 	    case IAC:
532 #	    if !defined(TN3270)
533 		TTYADD(IAC);
534 #	    else /* !defined(TN3270) */
535 		if (In3270) {
536 		    *Ifrontp++ = IAC;
537 		} else {
538 		    TTYADD(IAC);
539 		}
540 #	    endif /* !defined(TN3270) */
541 		break;
542 
543 	    default:
544 		break;
545 	    }
546 	    telrcv_state = TS_DATA;
547 	    continue;
548 
549 	case TS_WILL:
550 	    printoption(">RCVD", will, c, !hisopts[c]);
551 	    if (c == TELOPT_TM) {
552 		if (flushout) {
553 		    flushout = 0;
554 		}
555 	    } else if (!hisopts[c]) {
556 		willoption(c, 1);
557 	    }
558 	    SetIn3270();
559 	    telrcv_state = TS_DATA;
560 	    continue;
561 
562 	case TS_WONT:
563 	    printoption(">RCVD", wont, c, hisopts[c]);
564 	    if (c == TELOPT_TM) {
565 		if (flushout) {
566 		    flushout = 0;
567 		}
568 	    } else if (hisopts[c]) {
569 		wontoption(c, 1);
570 	    }
571 	    SetIn3270();
572 	    telrcv_state = TS_DATA;
573 	    continue;
574 
575 	case TS_DO:
576 	    printoption(">RCVD", doopt, c, !myopts[c]);
577 	    if (!myopts[c])
578 		dooption(c);
579 	    SetIn3270();
580 	    telrcv_state = TS_DATA;
581 	    continue;
582 
583 	case TS_DONT:
584 	    printoption(">RCVD", dont, c, myopts[c]);
585 	    if (myopts[c]) {
586 		myopts[c] = 0;
587 		printring(&netoring, wont, c);
588 		flushline = 1;
589 		setconnmode();	/* set new tty mode (maybe) */
590 		printoption(">SENT", wont, c, 0);
591 	    }
592 	    SetIn3270();
593 	    telrcv_state = TS_DATA;
594 	    continue;
595 
596 	case TS_SB:
597 	    if (c == IAC) {
598 		telrcv_state = TS_SE;
599 	    } else {
600 		SB_ACCUM(c);
601 	    }
602 	    continue;
603 
604 	case TS_SE:
605 	    if (c != SE) {
606 		if (c != IAC) {
607 		    SB_ACCUM(IAC);
608 		}
609 		SB_ACCUM(c);
610 		telrcv_state = TS_SB;
611 	    } else {
612 		SB_TERM();
613 		suboption();	/* handle sub-option */
614 		SetIn3270();
615 		telrcv_state = TS_DATA;
616 	    }
617 	}
618     }
619     if (count)
620 	ring_consumed(&netiring, count);
621     return returnValue||count;
622 }
623 
624 static int
625 telsnd()
626 {
627     int tcc;
628     int count;
629     int returnValue = 0;
630     char *tbp;
631 
632     tcc = 0;
633     count = 0;
634     while (NETROOM() > 2) {
635 	register int sc;
636 	register int c;
637 
638 	if (tcc == 0) {
639 	    if (count) {
640 		ring_consumed(&ttyiring, count);
641 		returnValue = 1;
642 		count = 0;
643 	    }
644 	    tbp = ttyiring.consume;
645 	    tcc = ring_full_consecutive(&ttyiring);
646 	    if (tcc == 0) {
647 		break;
648 	    }
649 	}
650 	c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
651 	if (sc == escape) {
652 	    command(0);
653 	    tcc = 0;
654 	    flushline = 1;
655 	    break;
656 	} else if (MODE_LINE(globalmode) && (sc == echoc)) {
657 	    if (tcc > 0 && strip(*tbp) == echoc) {
658 		tcc--; tbp++; count++;
659 	    } else {
660 		dontlecho = !dontlecho;
661 		settimer(echotoggle);
662 		setconnmode();
663 		flushline = 1;
664 		break;
665 	    }
666 	}
667 	if (localchars) {
668 	    if (TerminalSpecialChars(sc) == 0) {
669 		break;
670 	    }
671 	}
672 	if (!myopts[TELOPT_BINARY]) {
673 	    switch (c) {
674 	    case '\n':
675 		    /*
676 		     * If we are in CRMOD mode (\r ==> \n)
677 		     * on our local machine, then probably
678 		     * a newline (unix) is CRLF (TELNET).
679 		     */
680 		if (MODE_LOCAL_CHARS(globalmode)) {
681 		    NETADD('\r');
682 		}
683 		NETADD('\n');
684 		flushline = 1;
685 		break;
686 	    case '\r':
687 		if (!crlf) {
688 		    NET2ADD('\r', '\0');
689 		} else {
690 		    NET2ADD('\r', '\n');
691 		}
692 		flushline = 1;
693 		break;
694 	    case IAC:
695 		NET2ADD(IAC, IAC);
696 		break;
697 	    default:
698 		NETADD(c);
699 		break;
700 	    }
701 	} else if (c == IAC) {
702 	    NET2ADD(IAC, IAC);
703 	} else {
704 	    NETADD(c);
705 	}
706     }
707     if (count)
708 	ring_consumed(&ttyiring, count);
709     return returnValue||count;		/* Non-zero if we did anything */
710 }
711 
712 /*
713  * Scheduler()
714  *
715  * Try to do something.
716  *
717  * If we do something useful, return 1; else return 0.
718  *
719  */
720 
721 
722 int
723 Scheduler(block)
724 int	block;			/* should we block in the select ? */
725 {
726 		/* One wants to be a bit careful about setting returnValue
727 		 * to one, since a one implies we did some useful work,
728 		 * and therefore probably won't be called to block next
729 		 * time (TN3270 mode only).
730 		 */
731     int returnValue;
732     int netin, netout, netex, ttyin, ttyout;
733 
734     /* Decide which rings should be processed */
735 
736     netout = ring_full_count(&netoring) &&
737 	    (!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]);
738     ttyout = ring_full_count(&ttyoring);
739 
740 #if	defined(TN3270)
741     ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
742 #else	/* defined(TN3270) */
743     ttyin = ring_empty_count(&ttyiring);
744 #endif	/* defined(TN3270) */
745 
746 #if	defined(TN3270)
747     netin = ring_empty_count(&netiring);
748 #   else /* !defined(TN3270) */
749     netin = !ISend && ring_empty_count(&netiring);
750 #   endif /* !defined(TN3270) */
751 
752     netex = !SYNCHing;
753 
754     /* If we have seen a signal recently, reset things */
755 #   if defined(TN3270) && defined(unix)
756     if (HaveInput) {
757 	HaveInput = 0;
758 	signal(SIGIO, inputAvailable);
759     }
760 #endif	/* defined(TN3270) && defined(unix) */
761 
762     /* Call to system code to process rings */
763 
764     returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
765 
766     /* Now, look at the input rings, looking for work to do. */
767 
768     if (ring_full_count(&ttyiring)) {
769 #   if defined(TN3270)
770 	if (In3270) {
771 	    int c;
772 
773 	    c = DataFromTerminal(ttyiring.consume,
774 					ring_full_consecutive(&ttyiring));
775 	    if (c) {
776 		returnValue = 1;
777 	        ring_consumed(&ttyiring, c);
778 	    }
779 	} else {
780 #   endif /* defined(TN3270) */
781 	    returnValue |= telsnd();
782 #   if defined(TN3270)
783 	}
784 #   endif /* defined(TN3270) */
785     }
786 
787     if (ring_full_count(&netiring)) {
788 #	if !defined(TN3270)
789 	returnValue |= telrcv();
790 #	else /* !defined(TN3270) */
791 	returnValue = Push3270();
792 #	endif /* !defined(TN3270) */
793     }
794     return returnValue;
795 }
796 
797 /*
798  * Select from tty and network...
799  */
800 void
801 telnet()
802 {
803     sys_telnet_init();
804 
805 #   if !defined(TN3270)
806     if (telnetport) {
807 	if (!hisopts[TELOPT_SGA]) {
808 	    willoption(TELOPT_SGA, 0);
809 	}
810 	if (!myopts[TELOPT_TTYPE]) {
811 	    dooption(TELOPT_TTYPE);
812 	}
813     }
814 #   endif /* !defined(TN3270) */
815 
816 #   if !defined(TN3270)
817     for (;;) {
818 	int schedValue;
819 
820 	while ((schedValue = Scheduler(0)) != 0) {
821 	    if (schedValue == -1) {
822 		setcommandmode();
823 		return;
824 	    }
825 	}
826 
827 	if (Scheduler(1) == -1) {
828 	    setcommandmode();
829 	    return;
830 	}
831     }
832 #   else /* !defined(TN3270) */
833     for (;;) {
834 	int schedValue;
835 
836 	while (!In3270 && !shell_active) {
837 	    if (Scheduler(1) == -1) {
838 		setcommandmode();
839 		return;
840 	    }
841 	}
842 
843 	while ((schedValue = Scheduler(0)) != 0) {
844 	    if (schedValue == -1) {
845 		setcommandmode();
846 		return;
847 	    }
848 	}
849 		/* If there is data waiting to go out to terminal, don't
850 		 * schedule any more data for the terminal.
851 		 */
852 	if (ring_full_count(&ttyoring)) {
853 	    schedValue = 1;
854 	} else {
855 	    if (shell_active) {
856 		if (shell_continue() == 0) {
857 		    ConnectScreen();
858 		}
859 	    } else if (In3270) {
860 		schedValue = DoTerminalOutput();
861 	    }
862 	}
863 	if (schedValue && (shell_active == 0)) {
864 	    if (Scheduler(1) == -1) {
865 		setcommandmode();
866 		return;
867 	    }
868 	}
869     }
870 #   endif /* !defined(TN3270) */
871 }
872 
873 #if	0	/* XXX - this not being in is a bug */
874 /*
875  * nextitem()
876  *
877  *	Return the address of the next "item" in the TELNET data
878  * stream.  This will be the address of the next character if
879  * the current address is a user data character, or it will
880  * be the address of the character following the TELNET command
881  * if the current address is a TELNET IAC ("I Am a Command")
882  * character.
883  */
884 
885 static char *
886 nextitem(current)
887 char	*current;
888 {
889     if ((*current&0xff) != IAC) {
890 	return current+1;
891     }
892     switch (*(current+1)&0xff) {
893     case DO:
894     case DONT:
895     case WILL:
896     case WONT:
897 	return current+3;
898     case SB:		/* loop forever looking for the SE */
899 	{
900 	    register char *look = current+2;
901 
902 	    for (;;) {
903 		if ((*look++&0xff) == IAC) {
904 		    if ((*look++&0xff) == SE) {
905 			return look;
906 		    }
907 		}
908 	    }
909 	}
910     default:
911 	return current+2;
912     }
913 }
914 #endif	/* 0 */
915 
916 /*
917  * netclear()
918  *
919  *	We are about to do a TELNET SYNCH operation.  Clear
920  * the path to the network.
921  *
922  *	Things are a bit tricky since we may have sent the first
923  * byte or so of a previous TELNET command into the network.
924  * So, we have to scan the network buffer from the beginning
925  * until we are up to where we want to be.
926  *
927  *	A side effect of what we do, just to keep things
928  * simple, is to clear the urgent data pointer.  The principal
929  * caller should be setting the urgent data pointer AFTER calling
930  * us in any case.
931  */
932 
933 static void
934 netclear()
935 {
936 #if	0	/* XXX */
937     register char *thisitem, *next;
938     char *good;
939 #define	wewant(p)	((nfrontp > p) && ((*p&0xff) == IAC) && \
940 				((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
941 
942     thisitem = netobuf;
943 
944     while ((next = nextitem(thisitem)) <= netobuf.send) {
945 	thisitem = next;
946     }
947 
948     /* Now, thisitem is first before/at boundary. */
949 
950     good = netobuf;	/* where the good bytes go */
951 
952     while (netoring.add > thisitem) {
953 	if (wewant(thisitem)) {
954 	    int length;
955 
956 	    next = thisitem;
957 	    do {
958 		next = nextitem(next);
959 	    } while (wewant(next) && (nfrontp > next));
960 	    length = next-thisitem;
961 	    memcpy(good, thisitem, length);
962 	    good += length;
963 	    thisitem = next;
964 	} else {
965 	    thisitem = nextitem(thisitem);
966 	}
967     }
968 
969 #endif	/* 0 */
970 }
971 
972 /*
973  * These routines add various telnet commands to the data stream.
974  */
975 
976 static void
977 doflush()
978 {
979     NET2ADD(IAC, DO);
980     NETADD(TELOPT_TM);
981     flushline = 1;
982     flushout = 1;
983     ttyflush(1);			/* Flush/drop output */
984     /* do printoption AFTER flush, otherwise the output gets tossed... */
985     printoption("<SENT", doopt, TELOPT_TM, 0);
986 }
987 
988 void
989 xmitAO()
990 {
991     NET2ADD(IAC, AO);
992     if (autoflush) {
993 	doflush();
994     }
995 }
996 
997 
998 void
999 xmitEL()
1000 {
1001     NET2ADD(IAC, EL);
1002 }
1003 
1004 void
1005 xmitEC()
1006 {
1007     NET2ADD(IAC, EC);
1008 }
1009 
1010 
1011 #if	defined(NOT43)
1012 int
1013 #else	/* defined(NOT43) */
1014 void
1015 #endif	/* defined(NOT43) */
1016 dosynch()
1017 {
1018     netclear();			/* clear the path to the network */
1019     NETADD(IAC);
1020     setneturg();
1021     NETADD(DM);
1022 
1023 #if	defined(NOT43)
1024     return 0;
1025 #endif	/* defined(NOT43) */
1026 }
1027 
1028 void
1029 intp()
1030 {
1031     NET2ADD(IAC, IP);
1032     flushline = 1;
1033     if (autoflush) {
1034 	doflush();
1035     }
1036     if (autosynch) {
1037 	dosynch();
1038     }
1039 }
1040 
1041 void
1042 sendbrk()
1043 {
1044     NET2ADD(IAC, BREAK);
1045     flushline = 1;
1046     if (autoflush) {
1047 	doflush();
1048     }
1049     if (autosynch) {
1050 	dosynch();
1051     }
1052 }
1053