Flush out the last dregs in the terminal before quitting when
[unix-history] / usr / src / usr.bin / telnet / telnet.c
CommitLineData
897ce52e
KB
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
b36fc510
KB
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.
897ce52e 16 */
22e155fc 17
f18adf63 18#ifndef lint
811c5ce5 19static char sccsid[] = "@(#)telnet.c 5.33 (Berkeley) 6/29/88";
897ce52e 20#endif /* not lint */
f18adf63 21
de3b21e8 22#include <sys/types.h>
de3b21e8 23
890e6cf8 24#if defined(unix)
2e48960f 25#include <signal.h>
890e6cf8
GM
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
9c1dab9e 33#include <arpa/telnet.h>
890e6cf8 34
890e6cf8 35#if defined(unix)
f2bf20fe 36#include <strings.h>
890e6cf8
GM
37#else /* defined(unix) */
38#include <string.h>
39#endif /* defined(unix) */
de3b21e8 40
115a5494
GM
41#include "ring.h"
42
890e6cf8
GM
43#include "defines.h"
44#include "externs.h"
45#include "types.h"
46#include "general.h"
040b6435 47
040b6435 48\f
792ffc71 49#define strip(x) ((x)&0x7f)
890e6cf8 50
a19db822 51
890e6cf8
GM
52static char subbuffer[SUBBUFSIZE],
53 *subpointer, *subend; /* buffer for sub-options */
0ea293c6
GM
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
a19db822
BJ
60char hisopts[256];
61char myopts[256];
62
63char doopt[] = { IAC, DO, '%', 'c', 0 };
64char dont[] = { IAC, DONT, '%', 'c', 0 };
65char will[] = { IAC, WILL, '%', 'c', 0 };
66char wont[] = { IAC, WONT, '%', 'c', 0 };
67
890e6cf8
GM
68int
69 connected,
890e6cf8
GM
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>)? */
80a47e22 77#if defined(TN3270)
4ad36dea
GM
78 noasynchtty = 0,/* User specified "-noasynch" on command line */
79 noasynchnet = 0,/* User specified "-noasynch" on command line */
890e6cf8 80 askedSGA = 0, /* We have talked about suppress go ahead */
80a47e22 81#endif /* defined(TN3270) */
448f9c06 82 telnetport,
b307f09e
GM
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;
890e6cf8
GM
92
93#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */
94
95char
96 *prompt = 0,
97 escape,
98 echoc;
99
890e6cf8
GM
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 */
a19db822 112
890e6cf8 113static int telrcv_state;
a19db822 114
890e6cf8
GM
115jmp_buf toplevel = { 0 };
116jmp_buf peerdied;
40e8c2a3 117
890e6cf8 118int flushline;
40e8c2a3
GM
119
120/*
121 * The following are some clocks used to decide how to interpret
040b6435 122 * the relationship between various variables.
40e8c2a3
GM
123 */
124
890e6cf8
GM
125Clocks clocks;
126\f
127Modelist 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};
40e8c2a3 136
40e8c2a3
GM
137\f
138/*
890e6cf8 139 * Initialize telnet environment.
40e8c2a3 140 */
3e16c811 141
890e6cf8
GM
142init_telnet()
143{
890e6cf8
GM
144 SB_CLEAR();
145 ClearArray(hisopts);
146 ClearArray(myopts);
f2bf20fe 147
f8362f44 148 connected = In3270 = ISend = donebinarytoggle = 0;
f2bf20fe 149
890e6cf8
GM
150 SYNCHing = 0;
151
890e6cf8
GM
152 /* Don't change NetTrace */
153
154 escape = CONTROL(']');
155 echoc = CONTROL('E');
156
157 flushline = 1;
158 telrcv_state = TS_DATA;
a19db822 159}
f8362f44
GM
160\f
161
162#include <varargs.h>
163
80a47e22 164/*VARARGS*/
f8362f44
GM
165static void
166printring(va_alist)
167va_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}
a19db822 209
40e8c2a3 210
890e6cf8
GM
211void
212willoption(option, reply)
213 int option, reply;
40e8c2a3 214{
890e6cf8
GM
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 }
40e8c2a3 240 }
890e6cf8
GM
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 */
40e8c2a3 251 break;
890e6cf8
GM
252
253 case TELOPT_TM:
254 return; /* Never reply to TM will's/wont's */
255
40e8c2a3 256 default:
890e6cf8 257 fmt = dont;
40e8c2a3
GM
258 break;
259 }
f8362f44 260 printring(&netoring, fmt, option);
890e6cf8
GM
261 if (reply)
262 printoption(">SENT", fmt, option, reply);
263 else
264 printoption("<SENT", fmt, option, reply);
40e8c2a3 265}
f2bf20fe 266
890e6cf8
GM
267void
268wontoption(option, reply)
269 int option, reply;
f2bf20fe 270{
890e6cf8 271 char *fmt;
0ea293c6 272
890e6cf8 273 switch (option) {
0ea293c6 274
890e6cf8
GM
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;
0ea293c6 282
890e6cf8
GM
283 case TELOPT_TM:
284 return; /* Never reply to TM will's/wont's */
0ea293c6 285
890e6cf8
GM
286 default:
287 fmt = dont;
0ea293c6 288 }
f8362f44 289 printring(&netoring, fmt, option);
890e6cf8
GM
290 if (reply)
291 printoption(">SENT", fmt, option, reply);
292 else
293 printoption("<SENT", fmt, option, reply);
0ea293c6 294}
f2bf20fe 295
890e6cf8
GM
296static void
297dooption(option)
298 int option;
f2bf20fe 299{
890e6cf8 300 char *fmt;
f2bf20fe 301
890e6cf8 302 switch (option) {
f2bf20fe 303
890e6cf8
GM
304 case TELOPT_TM:
305 fmt = will;
306 break;
f2bf20fe 307
890e6cf8
GM
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;
f2bf20fe 317
890e6cf8
GM
318 case TELOPT_ECHO: /* We're never going to echo... */
319 default:
320 fmt = wont;
321 break;
f2bf20fe 322 }
f8362f44 323 printring(&netoring, fmt, option);
890e6cf8 324 printoption(">SENT", fmt, option, 0);
f2bf20fe 325}
3087f2df 326
3087f2df 327/*
890e6cf8 328 * suboption()
3087f2df 329 *
890e6cf8
GM
330 * Look at the sub-option buffer, and try to be helpful to the other
331 * side.
3087f2df 332 *
890e6cf8 333 * Currently we recognize:
3087f2df 334 *
890e6cf8 335 * Terminal type, send request.
3087f2df
GM
336 */
337
890e6cf8
GM
338static void
339suboption()
3087f2df 340{
890e6cf8
GM
341 printsub("<", subbuffer, subend-subbuffer+1);
342 switch (subbuffer[0]&0xff) {
343 case TELOPT_TTYPE:
344 if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
345 ;
3087f2df 346 } else {
890e6cf8
GM
347 char *name;
348 char namebuf[41];
349 extern char *getenv();
350 int len;
f2bf20fe 351
890e6cf8 352#if defined(TN3270)
b307f09e 353 if (tn3270_ttype()) {
890e6cf8
GM
354 return;
355 }
356#endif /* defined(TN3270) */
890e6cf8
GM
357 name = getenv("TERM");
358 if ((name == 0) || ((len = strlen(name)) > 40)) {
359 name = "UNKNOWN";
a044ddbc 360 len = strlen(name);
890e6cf8
GM
361 }
362 if ((len + 4+2) < NETROOM()) {
363 strcpy(namebuf, name);
364 upcase(namebuf);
f8362f44 365 printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
890e6cf8 366 TELQUAL_IS, namebuf, IAC, SE);
115a5494
GM
367 /* XXX */
368 /* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
890e6cf8 369 } else {
115a5494 370 ExitString("No room in buffer for terminal type.\n",
890e6cf8
GM
371 1);
372 /*NOTREACHED*/
373 }
f2bf20fe 374 }
40e8c2a3 375
890e6cf8
GM
376 default:
377 break;
40e8c2a3 378 }
40e8c2a3 379}
40e8c2a3 380\f
f2bf20fe 381
2e48960f 382int
890e6cf8 383telrcv()
f2bf20fe 384{
890e6cf8 385 register int c;
d5c9ed55
GM
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) {
8b6750f5 396 ring_consumed(&netiring, count);
d5c9ed55
GM
397 returnValue = 1;
398 count = 0;
399 }
8b6750f5
GM
400 sbp = netiring.consume;
401 scc = ring_full_consecutive(&netiring);
d5c9ed55
GM
402 if (scc == 0) {
403 /* No more data coming in */
404 break;
405 }
406 }
407
408 c = *sbp++ & 0xff, scc--; count++;
890e6cf8 409
890e6cf8
GM
410 switch (telrcv_state) {
411
412 case TS_CR:
413 telrcv_state = TS_DATA;
811c5ce5
GM
414 if (c == '\0') {
415 break; /* Ignore \0 after CR */
416 } else if ((c == '\n') && (!hisopts[TELOPT_ECHO]) && !crmod) {
417 TTYADD(c);
418 break;
0ea293c6 419 }
811c5ce5 420 /* Else, fall through */
890e6cf8
GM
421
422 case TS_DATA:
423 if (c == IAC) {
424 telrcv_state = TS_IAC;
2e48960f 425 break;
0ea293c6 426 }
890e6cf8
GM
427# if defined(TN3270)
428 if (In3270) {
429 *Ifrontp++ = c;
d5c9ed55
GM
430 while (scc > 0) {
431 c = *sbp++ & 0377, scc--; count++;
890e6cf8
GM
432 if (c == IAC) {
433 telrcv_state = TS_IAC;
eb1b2833 434 break;
890e6cf8
GM
435 }
436 *Ifrontp++ = c;
40e8c2a3 437 }
890e6cf8
GM
438 } else
439# endif /* defined(TN3270) */
811c5ce5
GM
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 */
890e6cf8 447 if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
811c5ce5
GM
448 if (scc > 0) {
449 c = *sbp&0xff;
450 if (c == 0) {
451 sbp++, scc--; count++;
452 /* a "true" CR */
890e6cf8 453 TTYADD('\r');
811c5ce5
GM
454 } else if (!hisopts[TELOPT_ECHO] &&
455 (c == '\n')) {
456 sbp++, scc--; count++;
890e6cf8 457 TTYADD('\n');
811c5ce5
GM
458 } else {
459 TTYADD('\r');
460 if (crmod) {
461 TTYADD('\n');
890e6cf8 462 }
811c5ce5
GM
463 }
464 } else {
465 telrcv_state = TS_CR;
466 TTYADD('\r');
467 if (crmod) {
468 TTYADD('\n');
792ffc71 469 }
a19db822 470 }
890e6cf8
GM
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);
f8362f44 503 SYNCHing = stilloob();
890e6cf8 504 settimer(gotDM);
a19db822
BJ
505 break;
506
890e6cf8
GM
507 case NOP:
508 case GA:
a19db822 509 break;
3087f2df 510
890e6cf8
GM
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;
3e16c811
GM
525 }
526 }
527 break;
890e6cf8
GM
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) */
3e16c811 540 break;
890e6cf8 541
3e16c811 542 default:
3e16c811
GM
543 break;
544 }
890e6cf8
GM
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);
f2bf20fe 556 }
890e6cf8
GM
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);
f2bf20fe 569 }
890e6cf8
GM
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;
f8362f44 586 printring(&netoring, wont, c);
890e6cf8
GM
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;
40e8c2a3 594
890e6cf8
GM
595 case TS_SB:
596 if (c == IAC) {
597 telrcv_state = TS_SE;
598 } else {
599 SB_ACCUM(c);
3087f2df 600 }
890e6cf8 601 continue;
40e8c2a3 602
890e6cf8
GM
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 }
40e8c2a3 616 }
40e8c2a3 617 }
ad1c581e
GM
618 if (count)
619 ring_consumed(&netiring, count);
d5c9ed55
GM
620 return returnValue||count;
621}
622
623static int
f8362f44 624telsnd()
d5c9ed55
GM
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) {
8b6750f5 639 ring_consumed(&ttyiring, count);
d5c9ed55
GM
640 returnValue = 1;
641 count = 0;
642 }
8b6750f5
GM
643 tbp = ttyiring.consume;
644 tcc = ring_full_consecutive(&ttyiring);
d5c9ed55
GM
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 }
ad1c581e
GM
706 if (count)
707 ring_consumed(&ttyiring, count);
d5c9ed55 708 return returnValue||count; /* Non-zero if we did anything */
40e8c2a3
GM
709}
710\f
890e6cf8
GM
711/*
712 * Scheduler()
713 *
714 * Try to do something.
715 *
716 * If we do something useful, return 1; else return 0.
717 *
718 */
40e8c2a3 719
890e6cf8
GM
720
721int
722Scheduler(block)
723int block; /* should we block in the select ? */
40e8c2a3 724{
890e6cf8
GM
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 */
b307f09e
GM
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
890e6cf8 739#if defined(TN3270)
b307f09e 740 ttyin = ring_empty_count(&ttyiring) && (shell_active == 0);
890e6cf8 741#else /* defined(TN3270) */
b307f09e 742 ttyin = ring_empty_count(&ttyiring);
890e6cf8 743#endif /* defined(TN3270) */
b307f09e
GM
744
745#if defined(TN3270)
746 netin = ring_empty_count(&netiring);
890e6cf8 747# else /* !defined(TN3270) */
b307f09e 748 netin = !ISend && ring_empty_count(&netiring);
890e6cf8 749# endif /* !defined(TN3270) */
b307f09e
GM
750
751 netex = !SYNCHing;
752
753 /* If we have seen a signal recently, reset things */
890e6cf8
GM
754# if defined(TN3270) && defined(unix)
755 if (HaveInput) {
756 HaveInput = 0;
757 signal(SIGIO, inputAvailable);
758 }
759#endif /* defined(TN3270) && defined(unix) */
040b6435 760
b307f09e 761 /* Call to system code to process rings */
040b6435 762
b307f09e 763 returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
040b6435 764
b307f09e 765 /* Now, look at the input rings, looking for work to do. */
890e6cf8 766
8b6750f5 767 if (ring_full_count(&ttyiring)) {
b307f09e 768# if defined(TN3270)
890e6cf8 769 if (In3270) {
80a47e22
GM
770 int c;
771
2e48960f 772 c = DataFromTerminal(ttyiring.consume,
8b6750f5 773 ring_full_consecutive(&ttyiring));
890e6cf8
GM
774 if (c) {
775 returnValue = 1;
ad1c581e 776 ring_consumed(&ttyiring, c);
890e6cf8 777 }
890e6cf8
GM
778 } else {
779# endif /* defined(TN3270) */
f8362f44 780 returnValue |= telsnd();
890e6cf8 781# if defined(TN3270)
040b6435 782 }
890e6cf8 783# endif /* defined(TN3270) */
890e6cf8 784 }
b307f09e 785
8b6750f5 786 if (ring_full_count(&netiring)) {
890e6cf8 787# if !defined(TN3270)
d5c9ed55 788 returnValue |= telrcv();
890e6cf8
GM
789# else /* !defined(TN3270) */
790 returnValue = Push3270();
791# endif /* !defined(TN3270) */
792 }
890e6cf8 793 return returnValue;
040b6435
GM
794}
795\f
3e16c811 796/*
890e6cf8 797 * Select from tty and network...
3e16c811 798 */
890e6cf8
GM
799void
800telnet()
3e16c811 801{
b307f09e 802 sys_telnet_init();
3e16c811 803
890e6cf8
GM
804# if !defined(TN3270)
805 if (telnetport) {
806 if (!hisopts[TELOPT_SGA]) {
807 willoption(TELOPT_SGA, 0);
3e16c811 808 }
890e6cf8 809 if (!myopts[TELOPT_TTYPE]) {
80a47e22 810 dooption(TELOPT_TTYPE);
40e8c2a3 811 }
040b6435 812 }
890e6cf8 813# endif /* !defined(TN3270) */
3e16c811 814
890e6cf8
GM
815# if !defined(TN3270)
816 for (;;) {
d5c9ed55
GM
817 int schedValue;
818
819 while ((schedValue = Scheduler(0)) != 0) {
820 if (schedValue == -1) {
821 setcommandmode();
822 return;
823 }
824 }
825
b307f09e 826 if (Scheduler(1) == -1) {
890e6cf8
GM
827 setcommandmode();
828 return;
3e16c811 829 }
890e6cf8
GM
830 }
831# else /* !defined(TN3270) */
832 for (;;) {
833 int schedValue;
834
835 while (!In3270 && !shell_active) {
b307f09e 836 if (Scheduler(1) == -1) {
890e6cf8
GM
837 setcommandmode();
838 return;
839 }
3e16c811 840 }
890e6cf8
GM
841
842 while ((schedValue = Scheduler(0)) != 0) {
843 if (schedValue == -1) {
844 setcommandmode();
845 return;
846 }
3e16c811 847 }
890e6cf8
GM
848 /* If there is data waiting to go out to terminal, don't
849 * schedule any more data for the terminal.
850 */
eb1b2833 851 if (ring_full_count(&ttyoring)) {
890e6cf8 852 schedValue = 1;
3e16c811 853 } else {
890e6cf8
GM
854 if (shell_active) {
855 if (shell_continue() == 0) {
856 ConnectScreen();
3e16c811 857 }
890e6cf8
GM
858 } else if (In3270) {
859 schedValue = DoTerminalOutput();
860 }
3e16c811 861 }
890e6cf8 862 if (schedValue && (shell_active == 0)) {
b307f09e 863 if (Scheduler(1) == -1) {
890e6cf8
GM
864 setcommandmode();
865 return;
866 }
3e16c811 867 }
890e6cf8
GM
868 }
869# endif /* !defined(TN3270) */
3e16c811 870}
890e6cf8 871\f
80a47e22 872#if 0 /* XXX - this not being in is a bug */
f8362f44
GM
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
884static char *
885nextitem(current)
886char *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}
80a47e22 913#endif /* 0 */
f8362f44
GM
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
932static void
933netclear()
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\f
3e16c811 971/*
890e6cf8 972 * These routines add various telnet commands to the data stream.
3e16c811 973 */
890e6cf8 974
f8362f44
GM
975static void
976doflush()
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
890e6cf8
GM
987void
988xmitAO()
3e16c811 989{
890e6cf8
GM
990 NET2ADD(IAC, AO);
991 if (autoflush) {
992 doflush();
993 }
3e16c811 994}
3e16c811 995
3e16c811 996
890e6cf8
GM
997void
998xmitEL()
3e16c811 999{
890e6cf8 1000 NET2ADD(IAC, EL);
3e16c811
GM
1001}
1002
890e6cf8
GM
1003void
1004xmitEC()
3e16c811 1005{
890e6cf8 1006 NET2ADD(IAC, EC);
3e16c811
GM
1007}
1008
890e6cf8
GM
1009
1010#if defined(NOT43)
1011int
1012#else /* defined(NOT43) */
1013void
1014#endif /* defined(NOT43) */
1015dosynch()
3e16c811 1016{
890e6cf8 1017 netclear(); /* clear the path to the network */
218b1a4c
GM
1018 NETADD(IAC);
1019 setneturg();
1020 NETADD(DM);
3087f2df 1021
890e6cf8
GM
1022#if defined(NOT43)
1023 return 0;
1024#endif /* defined(NOT43) */
3e16c811
GM
1025}
1026
890e6cf8
GM
1027void
1028intp()
1029{
1030 NET2ADD(IAC, IP);
1031 flushline = 1;
1032 if (autoflush) {
1033 doflush();
1034 }
1035 if (autosynch) {
1036 dosynch();
1037 }
1038}
f2bf20fe 1039
890e6cf8
GM
1040void
1041sendbrk()
f2bf20fe 1042{
890e6cf8
GM
1043 NET2ADD(IAC, BREAK);
1044 flushline = 1;
1045 if (autoflush) {
1046 doflush();
1047 }
1048 if (autosynch) {
1049 dosynch();
1050 }
f2bf20fe 1051}