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