Fix up lint errors.
[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
6 * provided that this notice is preserved and that due credit is given
7 * to the University of California at Berkeley. The name of the University
8 * may not be used to endorse or promote products derived from this
9 * software without specific prior written permission. This software
10 * is provided ``as is'' without express or implied warranty.
11 */
22e155fc 12
f18adf63 13#ifndef lint
80a47e22 14static char sccsid[] = "@(#)telnet.c 5.32 (Berkeley) %G%";
897ce52e 15#endif /* not lint */
f18adf63 16
de3b21e8 17#include <sys/types.h>
de3b21e8 18
890e6cf8 19#if defined(unix)
2e48960f 20#include <signal.h>
890e6cf8
GM
21/* By the way, we need to include curses.h before telnet.h since,
22 * among other things, telnet.h #defines 'DO', which is a variable
23 * declared in curses.h.
24 */
25#include <curses.h>
26#endif /* defined(unix) */
27
9c1dab9e 28#include <arpa/telnet.h>
890e6cf8 29
890e6cf8 30#if defined(unix)
f2bf20fe 31#include <strings.h>
890e6cf8
GM
32#else /* defined(unix) */
33#include <string.h>
34#endif /* defined(unix) */
de3b21e8 35
115a5494
GM
36#include "ring.h"
37
890e6cf8
GM
38#include "defines.h"
39#include "externs.h"
40#include "types.h"
41#include "general.h"
040b6435 42
040b6435 43\f
792ffc71 44#define strip(x) ((x)&0x7f)
890e6cf8 45
a19db822 46
890e6cf8
GM
47static char subbuffer[SUBBUFSIZE],
48 *subpointer, *subend; /* buffer for sub-options */
0ea293c6
GM
49#define SB_CLEAR() subpointer = subbuffer;
50#define SB_TERM() subend = subpointer;
51#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
52 *subpointer++ = (c); \
53 }
54
a19db822
BJ
55char hisopts[256];
56char myopts[256];
57
58char doopt[] = { IAC, DO, '%', 'c', 0 };
59char dont[] = { IAC, DONT, '%', 'c', 0 };
60char will[] = { IAC, WILL, '%', 'c', 0 };
61char wont[] = { IAC, WONT, '%', 'c', 0 };
62
890e6cf8
GM
63int
64 connected,
890e6cf8
GM
65 showoptions,
66 In3270, /* Are we in 3270 mode? */
67 ISend, /* trying to send network data in */
68 debug = 0,
69 crmod,
70 netdata, /* Print out network data flow */
71 crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */
80a47e22 72#if defined(TN3270)
890e6cf8
GM
73 noasynch = 0, /* User specified "-noasynch" on command line */
74 askedSGA = 0, /* We have talked about suppress go ahead */
80a47e22 75#endif /* defined(TN3270) */
448f9c06 76 telnetport,
b307f09e
GM
77 SYNCHing, /* we are in TELNET SYNCH mode */
78 flushout, /* flush output */
79 autoflush = 0, /* flush output when interrupting? */
80 autosynch, /* send interrupt characters with SYNCH? */
81 localchars, /* we recognize interrupt/quit */
82 donelclchars, /* the user has set "localchars" */
83 donebinarytoggle, /* the user has put us in binary */
84 dontlecho, /* do we suppress local echoing right now? */
85 globalmode;
890e6cf8
GM
86
87#define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */
88
89char
90 *prompt = 0,
91 escape,
92 echoc;
93
890e6cf8
GM
94/*
95 * Telnet receiver states for fsm
96 */
97#define TS_DATA 0
98#define TS_IAC 1
99#define TS_WILL 2
100#define TS_WONT 3
101#define TS_DO 4
102#define TS_DONT 5
103#define TS_CR 6
104#define TS_SB 7 /* sub-option collection */
105#define TS_SE 8 /* looking for sub-option end */
a19db822 106
890e6cf8 107static int telrcv_state;
a19db822 108
890e6cf8
GM
109jmp_buf toplevel = { 0 };
110jmp_buf peerdied;
40e8c2a3 111
890e6cf8 112int flushline;
40e8c2a3
GM
113
114/*
115 * The following are some clocks used to decide how to interpret
040b6435 116 * the relationship between various variables.
40e8c2a3
GM
117 */
118
890e6cf8
GM
119Clocks clocks;
120\f
121Modelist modelist[] = {
122 { "telnet command mode", COMMAND_LINE },
123 { "character-at-a-time mode", 0 },
124 { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS },
125 { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS },
126 { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS },
127 { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS },
128 { "3270 mode", 0 },
129};
40e8c2a3 130
40e8c2a3
GM
131\f
132/*
890e6cf8 133 * Initialize telnet environment.
40e8c2a3 134 */
3e16c811 135
890e6cf8
GM
136init_telnet()
137{
890e6cf8
GM
138 SB_CLEAR();
139 ClearArray(hisopts);
140 ClearArray(myopts);
f2bf20fe 141
f8362f44 142 connected = In3270 = ISend = donebinarytoggle = 0;
f2bf20fe 143
890e6cf8
GM
144#if defined(unix) && defined(TN3270)
145 HaveInput = 0;
146#endif /* defined(unix) && defined(TN3270) */
a19db822 147
890e6cf8
GM
148 SYNCHing = 0;
149
890e6cf8
GM
150 /* Don't change NetTrace */
151
152 escape = CONTROL(']');
153 echoc = CONTROL('E');
154
155 flushline = 1;
156 telrcv_state = TS_DATA;
a19db822 157}
f8362f44
GM
158\f
159
160#include <varargs.h>
161
80a47e22 162/*VARARGS*/
f8362f44
GM
163static void
164printring(va_alist)
165va_dcl
166{
167 va_list ap;
168 char buffer[100]; /* where things go */
169 char *ptr;
170 char *format;
171 char *string;
172 Ring *ring;
173 int i;
174
175 va_start(ap);
176
177 ring = va_arg(ap, Ring *);
178 format = va_arg(ap, char *);
179 ptr = buffer;
180
181 while ((i = *format++) != 0) {
182 if (i == '%') {
183 i = *format++;
184 switch (i) {
185 case 'c':
186 *ptr++ = va_arg(ap, int);
187 break;
188 case 's':
189 string = va_arg(ap, char *);
190 ring_supply_data(ring, buffer, ptr-buffer);
191 ring_supply_data(ring, string, strlen(string));
192 ptr = buffer;
193 break;
194 case 0:
195 ExitString("printring: trailing %%.\n", 1);
196 /*NOTREACHED*/
197 default:
198 ExitString("printring: unknown format character.\n", 1);
199 /*NOTREACHED*/
200 }
201 } else {
202 *ptr++ = i;
203 }
204 }
205 ring_supply_data(ring, buffer, ptr-buffer);
206}
a19db822 207
40e8c2a3 208
890e6cf8
GM
209void
210willoption(option, reply)
211 int option, reply;
40e8c2a3 212{
890e6cf8
GM
213 char *fmt;
214
215 switch (option) {
216
217 case TELOPT_ECHO:
218# if defined(TN3270)
219 /*
220 * The following is a pain in the rear-end.
221 * Various IBM servers (some versions of Wiscnet,
222 * possibly Fibronics/Spartacus, and who knows who
223 * else) will NOT allow us to send "DO SGA" too early
224 * in the setup proceedings. On the other hand,
225 * 4.2 servers (telnetd) won't set SGA correctly.
226 * So, we are stuck. Empirically (but, based on
227 * a VERY small sample), the IBM servers don't send
228 * out anything about ECHO, so we postpone our sending
229 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers
230 * DO send).
231 */
232 {
233 if (askedSGA == 0) {
234 askedSGA = 1;
235 if (!hisopts[TELOPT_SGA]) {
236 willoption(TELOPT_SGA, 0);
237 }
40e8c2a3 238 }
890e6cf8
GM
239 }
240 /* Fall through */
241 case TELOPT_EOR:
242 case TELOPT_BINARY:
243#endif /* defined(TN3270) */
244 case TELOPT_SGA:
245 settimer(modenegotiated);
246 hisopts[option] = 1;
247 fmt = doopt;
248 setconnmode(); /* possibly set new tty mode */
40e8c2a3 249 break;
890e6cf8
GM
250
251 case TELOPT_TM:
252 return; /* Never reply to TM will's/wont's */
253
40e8c2a3 254 default:
890e6cf8 255 fmt = dont;
40e8c2a3
GM
256 break;
257 }
f8362f44 258 printring(&netoring, fmt, option);
890e6cf8
GM
259 if (reply)
260 printoption(">SENT", fmt, option, reply);
261 else
262 printoption("<SENT", fmt, option, reply);
40e8c2a3 263}
f2bf20fe 264
890e6cf8
GM
265void
266wontoption(option, reply)
267 int option, reply;
f2bf20fe 268{
890e6cf8 269 char *fmt;
0ea293c6 270
890e6cf8 271 switch (option) {
0ea293c6 272
890e6cf8
GM
273 case TELOPT_ECHO:
274 case TELOPT_SGA:
275 settimer(modenegotiated);
276 hisopts[option] = 0;
277 fmt = dont;
278 setconnmode(); /* Set new tty mode */
279 break;
0ea293c6 280
890e6cf8
GM
281 case TELOPT_TM:
282 return; /* Never reply to TM will's/wont's */
0ea293c6 283
890e6cf8
GM
284 default:
285 fmt = dont;
0ea293c6 286 }
f8362f44 287 printring(&netoring, fmt, option);
890e6cf8
GM
288 if (reply)
289 printoption(">SENT", fmt, option, reply);
290 else
291 printoption("<SENT", fmt, option, reply);
0ea293c6 292}
f2bf20fe 293
890e6cf8
GM
294static void
295dooption(option)
296 int option;
f2bf20fe 297{
890e6cf8 298 char *fmt;
f2bf20fe 299
890e6cf8 300 switch (option) {
f2bf20fe 301
890e6cf8
GM
302 case TELOPT_TM:
303 fmt = will;
304 break;
f2bf20fe 305
890e6cf8
GM
306# if defined(TN3270)
307 case TELOPT_EOR:
308 case TELOPT_BINARY:
309# endif /* defined(TN3270) */
310 case TELOPT_TTYPE: /* terminal type option */
311 case TELOPT_SGA: /* no big deal */
312 fmt = will;
313 myopts[option] = 1;
314 break;
f2bf20fe 315
890e6cf8
GM
316 case TELOPT_ECHO: /* We're never going to echo... */
317 default:
318 fmt = wont;
319 break;
f2bf20fe 320 }
f8362f44 321 printring(&netoring, fmt, option);
890e6cf8 322 printoption(">SENT", fmt, option, 0);
f2bf20fe 323}
3087f2df 324
3087f2df 325/*
890e6cf8 326 * suboption()
3087f2df 327 *
890e6cf8
GM
328 * Look at the sub-option buffer, and try to be helpful to the other
329 * side.
3087f2df 330 *
890e6cf8 331 * Currently we recognize:
3087f2df 332 *
890e6cf8 333 * Terminal type, send request.
3087f2df
GM
334 */
335
890e6cf8
GM
336static void
337suboption()
3087f2df 338{
890e6cf8
GM
339 printsub("<", subbuffer, subend-subbuffer+1);
340 switch (subbuffer[0]&0xff) {
341 case TELOPT_TTYPE:
342 if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
343 ;
3087f2df 344 } else {
890e6cf8
GM
345 char *name;
346 char namebuf[41];
347 extern char *getenv();
348 int len;
f2bf20fe 349
890e6cf8 350#if defined(TN3270)
b307f09e 351 if (tn3270_ttype()) {
890e6cf8
GM
352 return;
353 }
354#endif /* defined(TN3270) */
890e6cf8
GM
355 name = getenv("TERM");
356 if ((name == 0) || ((len = strlen(name)) > 40)) {
357 name = "UNKNOWN";
a044ddbc 358 len = strlen(name);
890e6cf8
GM
359 }
360 if ((len + 4+2) < NETROOM()) {
361 strcpy(namebuf, name);
362 upcase(namebuf);
f8362f44 363 printring(&netoring, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
890e6cf8 364 TELQUAL_IS, namebuf, IAC, SE);
115a5494
GM
365 /* XXX */
366 /* printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); */
890e6cf8 367 } else {
115a5494 368 ExitString("No room in buffer for terminal type.\n",
890e6cf8
GM
369 1);
370 /*NOTREACHED*/
371 }
f2bf20fe 372 }
40e8c2a3 373
890e6cf8
GM
374 default:
375 break;
40e8c2a3 376 }
40e8c2a3 377}
40e8c2a3 378\f
f2bf20fe 379
2e48960f 380int
890e6cf8 381telrcv()
f2bf20fe 382{
890e6cf8 383 register int c;
d5c9ed55
GM
384 register int scc;
385 register char *sbp;
386 int count;
387 int returnValue = 0;
388
389 scc = 0;
390 count = 0;
391 while (TTYROOM() > 2) {
392 if (scc == 0) {
393 if (count) {
8b6750f5 394 ring_consumed(&netiring, count);
d5c9ed55
GM
395 returnValue = 1;
396 count = 0;
397 }
8b6750f5
GM
398 sbp = netiring.consume;
399 scc = ring_full_consecutive(&netiring);
d5c9ed55
GM
400 if (scc == 0) {
401 /* No more data coming in */
402 break;
403 }
404 }
405
406 c = *sbp++ & 0xff, scc--; count++;
890e6cf8 407
890e6cf8
GM
408 switch (telrcv_state) {
409
410 case TS_CR:
411 telrcv_state = TS_DATA;
412 if (c == '\0') {
413 break; /* Ignore \0 after CR */
414 } else if (c == '\n') {
807a3a7d 415 if ((!hisopts[TELOPT_ECHO]) && !crmod) {
890e6cf8 416 TTYADD(c);
40e8c2a3 417 }
3e16c811 418 break;
0ea293c6 419 }
890e6cf8
GM
420 /* Else, fall through */
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) */
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 */
447 if ((c == '\r') && !hisopts[TELOPT_BINARY]) {
448 if (scc > 0) {
449 c = *sbp&0xff;
450 if (c == 0) {
d5c9ed55 451 sbp++, scc--; count++;
890e6cf8
GM
452 /* a "true" CR */
453 TTYADD('\r');
454 } else if (!hisopts[TELOPT_ECHO] &&
455 (c == '\n')) {
d5c9ed55 456 sbp++, scc--; count++;
890e6cf8 457 TTYADD('\n');
040b6435 458 } else {
890e6cf8
GM
459 TTYADD('\r');
460 if (crmod) {
461 TTYADD('\n');
462 }
040b6435 463 }
890e6cf8
GM
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}