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 */ 12 13 #ifndef lint 14 char copyright[] = 15 "@(#) Copyright (c) 1988 Regents of the University of California.\n\ 16 All rights reserved.\n"; 17 #endif /* not lint */ 18 19 #ifndef lint 20 static char sccsid[] = "@(#)telnet.c 6.9 (Berkeley) 03/28/88"; 21 #endif /* not lint */ 22 23 /* 24 * User telnet program, modified for use by tn3270.c. 25 * 26 * Many of the FUNCTIONAL changes in this newest version of TELNET 27 * were suggested by Dave Borman of Cray Research, Inc. 28 * 29 * Other changes in the tn3270 side come from Alan Crosswell (Columbia), 30 * Bob Braden (ISI), Steve Jacobson (Berkeley), and Cliff Frost (Berkeley). 31 * 32 * This code is common between telnet(1c) and tn3270(1c). There are the 33 * following defines used to generate the various versions: 34 * 35 * TN3270 - This is to be linked with tn3270. 36 * 37 * NOT43 - Allows the program to compile and run on 38 * a 4.2BSD system. 39 * 40 * PUTCHAR - Within tn3270, on a NOT43 system, 41 * allows the use of the 4.3 curses 42 * (greater speed updating the screen). 43 * You need the 4.3 curses for this to work. 44 * 45 * FD_SETSIZE - On whichever system, if this isn't defined, 46 * we patch over the FD_SET, etc., macros with 47 * some homebrewed ones. 48 * 49 * SO_OOBINLINE - This is a socket option which we would like 50 * to set to allow TCP urgent data to come 51 * to us "inline". This is NECESSARY for 52 * CORRECT operation, and desireable for 53 * simpler operation. 54 * 55 * LNOFLSH - Detects the presence of the LNOFLSH bit 56 * in the tty structure. 57 * 58 * unix - Compiles in unix specific stuff. 59 * 60 * MSDOS - Compiles in MSDOS specific stuff. 61 * 62 */ 63 64 #if !defined(TN3270) 65 #define ExitString(f,s,r) { fprintf(f, s); exit(r); } 66 #define Exit(x) exit(x) 67 #define SetIn3270() 68 69 void setcommandmode(), command(); /* forward declarations */ 70 #endif /* !defined(TN3270) */ 71 72 #include <sys/types.h> 73 74 #if defined(pyr) 75 #define fd_set fdset_t 76 #endif /* defined(pyr) */ 77 78 #include <sys/socket.h> 79 80 #include <netinet/in.h> 81 82 #if defined(unix) 83 /* By the way, we need to include curses.h before telnet.h since, 84 * among other things, telnet.h #defines 'DO', which is a variable 85 * declared in curses.h. 86 */ 87 #include <curses.h> 88 #endif /* defined(unix) */ 89 90 #define TELOPTS 91 #include <arpa/telnet.h> 92 93 #if !defined(NOT43) 94 #include <arpa/inet.h> 95 #else /* !defined(NOT43) */ 96 extern unsigned long inet_addr(); 97 extern char *inet_ntoa(); 98 #endif /* !defined(NOT43) */ 99 100 #include <stdio.h> 101 #include <ctype.h> 102 #include <errno.h> 103 #include <setjmp.h> 104 #include <netdb.h> 105 106 #if defined(unix) 107 #include <strings.h> 108 #else /* defined(unix) */ 109 #include <string.h> 110 #endif /* defined(unix) */ 111 112 #if defined(TN3270) 113 #include "ascii/termin.ext" 114 #include "ctlr/screen.h" 115 #include "ctlr/oia.h" 116 #include "ctlr/options.ext" 117 #include "ctlr/outbound.ext" 118 #include "general/globals.h" 119 #include "telnet.ext" 120 #endif /* defined(TN3270) */ 121 122 #include "general/general.h" 123 124 125 126 #ifndef FD_SETSIZE 127 /* 128 * The following is defined just in case someone should want to run 129 * this telnet on a 4.2 system. 130 * 131 */ 132 133 #define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) 134 #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) 135 #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) 136 #define FD_ZERO(p) ((p)->fds_bits[0] = 0) 137 138 #endif 139 140 #define strip(x) ((x)&0x7f) 141 #define min(x,y) ((x<y)? x:y) 142 143 #if defined(TN3270) 144 static char Ibuf[8*BUFSIZ], *Ifrontp, *Ibackp; 145 #endif /* defined(TN3270) */ 146 147 static char ttyobuf[2*BUFSIZ], *tfrontp, *tbackp; 148 #define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } } 149 #define TTYLOC() (tfrontp) 150 #define TTYMAX() (ttyobuf+sizeof ttyobuf-1) 151 #define TTYMIN() (netobuf) 152 #define TTYBYTES() (tfrontp-tbackp) 153 #define TTYROOM() (TTYMAX()-TTYLOC()+1) 154 155 static char netobuf[2*BUFSIZ], *nfrontp, *nbackp; 156 #define NETADD(c) { *nfrontp++ = c; } 157 #define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); } 158 #define NETLOC() (nfrontp) 159 #define NETMAX() (netobuf+sizeof netobuf-1) 160 #define NETBYTES() (nfrontp-nbackp) 161 #define NETROOM() (NETMAX()-NETLOC()+1) 162 static char *neturg; /* one past last byte of urgent data */ 163 164 static char subbuffer[100], 165 *subpointer, *subend; /* buffer for sub-options */ 166 #define SB_CLEAR() subpointer = subbuffer; 167 #define SB_TERM() subend = subpointer; 168 #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 169 *subpointer++ = (c); \ 170 } 171 172 static char sb_terminal[] = { IAC, SB, 173 TELOPT_TTYPE, TELQUAL_IS, 174 'I', 'B', 'M', '-', '3', '2', '7', '8', '-', '2', 175 IAC, SE }; 176 #define SBTERMMODEL 13 177 178 179 static char hisopts[256]; 180 static char myopts[256]; 181 182 static char doopt[] = { IAC, DO, '%', 'c', 0 }; 183 static char dont[] = { IAC, DONT, '%', 'c', 0 }; 184 static char will[] = { IAC, WILL, '%', 'c', 0 }; 185 static char wont[] = { IAC, WONT, '%', 'c', 0 }; 186 187 struct cmd { 188 char *name; /* command name */ 189 char *help; /* help string */ 190 int (*handler)(); /* routine which executes command */ 191 int dohelp; /* Should we give general help information? */ 192 int needconnect; /* Do we need to be connected to execute? */ 193 }; 194 195 static char sibuf[BUFSIZ], *sbp; 196 static char tibuf[BUFSIZ], *tbp; 197 static fd_set ibits, obits, xbits; 198 199 200 static int 201 connected, 202 net, 203 scc, 204 tcc, 205 showoptions, 206 In3270, /* Are we in 3270 mode? */ 207 ISend, /* trying to send network data in */ 208 debug = 0, 209 crmod, 210 netdata, /* Print out network data flow */ 211 crlf, /* Should '\r' be mapped to <CR><LF> (or <CR><NUL>)? */ 212 noasynch = 0, /* User specified "-noasynch" on command line */ 213 askedSGA = 0, /* We have talked about suppress go ahead */ 214 telnetport = 1; 215 216 static FILE *NetTrace = 0; /* Not in bss, since needs to stay */ 217 218 #define CONTROL(x) ((x)&0x1f) /* CTRL(x) is not portable */ 219 220 static char 221 *prompt = 0, 222 escape, 223 echoc; 224 225 static int 226 SYNCHing, /* we are in TELNET SYNCH mode */ 227 flushout, /* flush output */ 228 autoflush = 0, /* flush output when interrupting? */ 229 autosynch, /* send interrupt characters with SYNCH? */ 230 localchars, /* we recognize interrupt/quit */ 231 donelclchars, /* the user has set "localchars" */ 232 donebinarytoggle, /* the user has put us in binary */ 233 dontlecho, /* do we suppress local echoing right now? */ 234 globalmode; 235 236 /* The following are some tn3270 specific flags */ 237 #if defined(TN3270) 238 239 static int 240 Sent3270TerminalType; /* Have we said we are a 3270? */ 241 242 /* Some real, live, globals. */ 243 int 244 tout, /* Output file descriptor */ 245 tin; /* Input file descriptor */ 246 247 #else /* defined(TN3270) */ 248 static int tin, tout; /* file descriptors */ 249 #endif /* defined(TN3270) */ 250 251 252 /* 253 * Telnet receiver states for fsm 254 */ 255 #define TS_DATA 0 256 #define TS_IAC 1 257 #define TS_WILL 2 258 #define TS_WONT 3 259 #define TS_DO 4 260 #define TS_DONT 5 261 #define TS_CR 6 262 #define TS_SB 7 /* sub-option collection */ 263 #define TS_SE 8 /* looking for sub-option end */ 264 265 static int telrcv_state = TS_DATA; 266 267 static char line[200]; 268 static int margc; 269 static char *margv[20]; 270 271 static jmp_buf toplevel = { 0 }; 272 static jmp_buf peerdied; 273 274 extern int errno; 275 276 277 static struct sockaddr_in sin; 278 279 static struct servent *sp = 0; 280 281 static int flushline; 282 283 static char *hostname; 284 static char hnamebuf[32]; 285 286 /* 287 * The following are some clocks used to decide how to interpret 288 * the relationship between various variables. 289 */ 290 291 static struct { 292 int 293 system, /* what the current time is */ 294 echotoggle, /* last time user entered echo character */ 295 modenegotiated, /* last time operating mode negotiated */ 296 didnetreceive, /* last time we read data from network */ 297 gotDM; /* when did we last see a data mark */ 298 } clocks; 299 300 #define settimer(x) clocks.x = clocks.system++ 301 302 /* Various modes */ 303 #define MODE_LINE(m) (modelist[m].modetype & LINE) 304 #define MODE_LOCAL_CHARS(m) (modelist[m].modetype & LOCAL_CHARS) 305 #define MODE_LOCAL_ECHO(m) (modelist[m].modetype & LOCAL_ECHO) 306 #define MODE_COMMAND_LINE(m) (modelist[m].modetype & COMMAND_LINE) 307 308 #define LOCAL_CHARS 0x01 /* Characters processed locally */ 309 #define LINE 0x02 /* Line-by-line mode of operation */ 310 #define LOCAL_ECHO 0x04 /* Echoing locally */ 311 #define COMMAND_LINE 0x08 /* Command line mode */ 312 313 static struct { 314 char *modedescriptions; 315 char modetype; 316 } modelist[] = { 317 { "telnet command mode", COMMAND_LINE }, 318 { "character-at-a-time mode", 0 }, 319 { "character-at-a-time mode (local echo)", LOCAL_ECHO|LOCAL_CHARS }, 320 { "line-by-line mode (remote echo)", LINE | LOCAL_CHARS }, 321 { "line-by-line mode", LINE | LOCAL_ECHO | LOCAL_CHARS }, 322 { "line-by-line mode (local echoing suppressed)", LINE | LOCAL_CHARS }, 323 { "3270 mode", 0 }, 324 }; 325 326 327 /* 328 * The following routines try to encapsulate what is system dependent 329 * (at least between 4.x and dos) which is used in telnet.c. 330 */ 331 332 #if defined(unix) 333 #include <sys/ioctl.h> 334 #include <sys/time.h> 335 #include <signal.h> 336 337 int 338 HaveInput; /* There is input available to scan */ 339 340 #if defined(TN3270) 341 static char tline[200]; 342 char *transcom = 0; /* transparent mode command (default: none) */ 343 #endif /* defined(TN3270) */ 344 345 static struct tchars otc = { 0 }, ntc = { 0 }; 346 static struct ltchars oltc = { 0 }, nltc = { 0 }; 347 static struct sgttyb ottyb = { 0 }, nttyb = { 0 }; 348 349 350 #define TerminalWrite(fd,buf,n) write(fd,buf,n) 351 #define TerminalRead(fd,buf,n) read(fd,buf,n) 352 353 /* 354 * 355 */ 356 357 static int 358 TerminalAutoFlush() /* unix */ 359 { 360 #if defined(LNOFLSH) 361 ioctl(0, TIOCLGET, (char *)&autoflush); 362 return !(autoflush&LNOFLSH); /* if LNOFLSH, no autoflush */ 363 #else /* LNOFLSH */ 364 return 1; 365 #endif /* LNOFLSH */ 366 } 367 368 /* 369 * TerminalSpecialChars() 370 * 371 * Look at an input character to see if it is a special character 372 * and decide what to do. 373 * 374 * Output: 375 * 376 * 0 Don't add this character. 377 * 1 Do add this character 378 */ 379 380 int 381 TerminalSpecialChars(c) /* unix */ 382 int c; 383 { 384 void doflush(), intp(), sendbrk(); 385 386 if (c == ntc.t_intrc) { 387 intp(); 388 return 0; 389 } else if (c == ntc.t_quitc) { 390 sendbrk(); 391 return 0; 392 } else if (c == nltc.t_flushc) { 393 NET2ADD(IAC, AO); 394 if (autoflush) { 395 doflush(); 396 } 397 return 0; 398 } else if (!MODE_LOCAL_CHARS(globalmode)) { 399 if (c == nttyb.sg_kill) { 400 NET2ADD(IAC, EL); 401 return 0; 402 } else if (c == nttyb.sg_erase) { 403 NET2ADD(IAC, EC); 404 return 0; 405 } 406 } 407 return 1; 408 } 409 410 411 /* 412 * Flush output to the terminal 413 */ 414 415 static void 416 TerminalFlushOutput() /* unix */ 417 { 418 (void) ioctl(fileno(stdout), TIOCFLUSH, (char *) 0); 419 } 420 421 static void 422 TerminalSaveState() /* unix */ 423 { 424 ioctl(0, TIOCGETP, (char *)&ottyb); 425 ioctl(0, TIOCGETC, (char *)&otc); 426 ioctl(0, TIOCGLTC, (char *)&oltc); 427 428 ntc = otc; 429 nltc = oltc; 430 nttyb = ottyb; 431 } 432 433 static void 434 TerminalRestoreState() /* unix */ 435 { 436 } 437 438 /* 439 * TerminalNewMode - set up terminal to a specific mode. 440 */ 441 442 443 static void 444 TerminalNewMode(f) /* unix */ 445 register int f; 446 { 447 static int prevmode = 0; 448 struct tchars *tc; 449 struct tchars tc3; 450 struct ltchars *ltc; 451 struct sgttyb sb; 452 int onoff; 453 int old; 454 struct tchars notc2; 455 struct ltchars noltc2; 456 static struct tchars notc = { -1, -1, -1, -1, -1, -1 }; 457 static struct ltchars noltc = { -1, -1, -1, -1, -1, -1 }; 458 459 globalmode = f; 460 if (prevmode == f) 461 return; 462 old = prevmode; 463 prevmode = f; 464 sb = nttyb; 465 466 switch (f) { 467 468 case 0: 469 onoff = 0; 470 tc = &otc; 471 ltc = &oltc; 472 break; 473 474 case 1: /* remote character processing, remote echo */ 475 case 2: /* remote character processing, local echo */ 476 case 6: /* 3270 mode - like 1, but with xon/xoff local */ 477 /* (might be nice to have "6" in telnet also...) */ 478 sb.sg_flags |= CBREAK; 479 if ((f == 1) || (f == 6)) { 480 sb.sg_flags &= ~(ECHO|CRMOD); 481 } else { 482 sb.sg_flags |= ECHO|CRMOD; 483 } 484 sb.sg_erase = sb.sg_kill = -1; 485 if (f == 6) { 486 tc = &tc3; 487 tc3 = notc; 488 /* get XON, XOFF characters */ 489 tc3.t_startc = otc.t_startc; 490 tc3.t_stopc = otc.t_stopc; 491 } else { 492 /* 493 * If user hasn't specified one way or the other, 494 * then default to not trapping signals. 495 */ 496 if (!donelclchars) { 497 localchars = 0; 498 } 499 if (localchars) { 500 notc2 = notc; 501 notc2.t_intrc = ntc.t_intrc; 502 notc2.t_quitc = ntc.t_quitc; 503 tc = ¬c2; 504 } else { 505 tc = ¬c; 506 } 507 } 508 ltc = &noltc; 509 onoff = 1; 510 break; 511 case 3: /* local character processing, remote echo */ 512 case 4: /* local character processing, local echo */ 513 case 5: /* local character processing, no echo */ 514 sb.sg_flags &= ~CBREAK; 515 sb.sg_flags |= CRMOD; 516 if (f == 4) 517 sb.sg_flags |= ECHO; 518 else 519 sb.sg_flags &= ~ECHO; 520 notc2 = ntc; 521 tc = ¬c2; 522 noltc2 = oltc; 523 ltc = &noltc2; 524 /* 525 * If user hasn't specified one way or the other, 526 * then default to trapping signals. 527 */ 528 if (!donelclchars) { 529 localchars = 1; 530 } 531 if (localchars) { 532 notc2.t_brkc = nltc.t_flushc; 533 noltc2.t_flushc = -1; 534 } else { 535 notc2.t_intrc = notc2.t_quitc = -1; 536 } 537 noltc2.t_suspc = escape; 538 noltc2.t_dsuspc = -1; 539 onoff = 1; 540 break; 541 542 default: 543 return; 544 } 545 ioctl(tin, TIOCSLTC, (char *)ltc); 546 ioctl(tin, TIOCSETC, (char *)tc); 547 ioctl(tin, TIOCSETP, (char *)&sb); 548 #if (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) 549 ioctl(tin, FIONBIO, (char *)&onoff); 550 ioctl(tout, FIONBIO, (char *)&onoff); 551 #endif /* (!defined(TN3270)) || ((!defined(NOT43)) || defined(PUTCHAR)) */ 552 #if defined(TN3270) 553 if (noasynch == 0) { 554 ioctl(tin, FIOASYNC, (char *)&onoff); 555 } 556 #endif /* defined(TN3270) */ 557 558 if (MODE_LINE(f)) { 559 void doescape(); 560 561 signal(SIGTSTP, doescape); 562 } else if (MODE_LINE(old)) { 563 signal(SIGTSTP, SIG_DFL); 564 sigsetmask(sigblock(0) & ~(1<<(SIGTSTP-1))); 565 } 566 } 567 568 569 int 570 NetClose(net) 571 int net; 572 { 573 return close(net); 574 } 575 576 577 static void 578 NetNonblockingIO(fd, onoff) /* unix */ 579 int 580 fd, 581 onoff; 582 { 583 ioctl(net, FIONBIO, (char *)&onoff); 584 } 585 586 static void 587 NetSigIO(fd, onoff) /* unix */ 588 int 589 fd, 590 onoff; 591 { 592 ioctl(net, FIOASYNC, (char *)&onoff); /* hear about input */ 593 } 594 595 static void 596 NetSetPgrp(fd) /* unix */ 597 int fd; 598 { 599 int myPid; 600 601 myPid = getpid(); 602 #if defined(NOT43) 603 myPid = -myPid; 604 #endif /* defined(NOT43) */ 605 ioctl(net, SIOCSPGRP, (char *)&myPid); /* set my pid */ 606 } 607 608 609 #endif /* defined(unix) */ 610 611 #if defined(MSDOS) 612 #include <time.h> 613 #include <signal.h> 614 #include <process.h> 615 #include <fcntl.h> 616 #include <io.h> 617 #include <dos.h> 618 619 #if !defined(SO_OOBINLINE) 620 #define SO_OOBINLINE 621 #endif /* !defined(SO_OOBINLINE) */ 622 623 624 static char 625 termEofChar, 626 termEraseChar, 627 termFlushChar, 628 termIntChar, 629 termKillChar, 630 termLiteralNextChar, 631 termQuitChar; 632 633 static char 634 savedInState, savedOutState; 635 636 /* 637 * MSDOS doesn't have anyway of deciding whether a full-edited line 638 * is ready to be read in, so we need to do character-by-character 639 * reads, and then do the editing in the program (in the case where 640 * we are supporting line-by-line mode). 641 * 642 * The following routines, which are internal to the MSDOS-specific 643 * code, accomplish this miracle. 644 */ 645 646 #define Hex(c) HEX[(c)&0xff] 647 648 static survivorSetup = 0; /* Do we have ^C hooks in? */ 649 650 static int 651 lineend = 0, /* There is a line terminator */ 652 ctrlCCount = 0; 653 654 static char linein[200], /* Where input line is assembled */ 655 *nextin = linein, /* Next input character */ 656 *nextout = linein; /* Next character to be consumed */ 657 658 #define consumechar() \ 659 if ((++nextout) >= nextin) { \ 660 nextout = nextin = linein; \ 661 lineend = 0; \ 662 } 663 664 #define characteratatime() (!MODE_LINE(globalmode)) /* one by one */ 665 666 667 /* 668 * killone() 669 * 670 * Erase the last character on the line. 671 */ 672 673 static void 674 killone() 675 { 676 if (lineend) { 677 return; /* ??? XXX */ 678 } 679 if (nextin == linein) { 680 return; /* Nothing to do */ 681 } 682 nextin--; 683 if (!(isspace(*nextin) || isprint(*nextin))) { 684 putchar('\b'); 685 putchar(' '); 686 putchar('\b'); 687 } 688 putchar('\b'); 689 putchar(' '); 690 putchar('\b'); 691 } 692 693 694 /* 695 * setlineend() 696 * 697 * Decide if it's time to send the current line up to the user 698 * process. 699 */ 700 701 static void 702 setlineend() 703 { 704 if (nextin == nextout) { 705 return; 706 } 707 if (characteratatime()) { 708 lineend = 1; 709 } else if (nextin >= (linein+sizeof linein)) { 710 lineend = 1; 711 } else { 712 int c = *(nextin-1); 713 if ((c == termIntChar) 714 || (c == termQuitChar) 715 || (c == termEofChar)) { 716 lineend = 1; 717 } else if (c == termFlushChar) { 718 lineend = 1; 719 } else if ((c == '\n') || (c == '\r')) { 720 lineend = 1; 721 } 722 } 723 /* Otherwise, leave it alone (reset by 'consumechar') */ 724 } 725 726 /* 727 * OK, what we do here is: 728 * 729 * o If we are echoing, then 730 * o Look for character erase, line kill characters 731 * o Echo the character (using '^' if a control character) 732 * o Put the character in the input buffer 733 * o Set 'lineend' as necessary 734 */ 735 736 static void 737 DoNextChar(c) 738 int c; /* Character to process */ 739 { 740 static char literalnextcharacter = 0; 741 742 if (nextin >= (linein+sizeof linein)) { 743 putchar('\7'); /* Ring bell */ 744 setlineend(); 745 return; 746 } 747 if (MODE_LOCAL_CHARS(globalmode)) { 748 /* Look for some special character */ 749 if (!literalnextcharacter) { 750 if (c == termEraseChar) { 751 killone(); 752 setlineend(); 753 return; 754 } else if (c == termKillChar) { 755 while (nextin != linein) { 756 killone(); 757 } 758 setlineend(); 759 return; 760 } else if (c == termLiteralNextChar) { 761 literalnextcharacter = 1; 762 return; 763 } 764 } 765 766 if (MODE_LOCAL_ECHO(globalmode)) { 767 if ((literalnextcharacter == 0) && ((c == '\r') || (c == '\n'))) { 768 putchar('\r'); 769 putchar('\n'); 770 c = '\n'; 771 } else if (!isprint(c) && !isspace(c)) { 772 putchar('^'); 773 putchar(c^0x40); 774 } else { 775 putchar(c); 776 } 777 } 778 literalnextcharacter = 0; 779 } 780 *nextin++ = c; 781 setlineend(); 782 } 783 784 static int 785 inputExists() 786 { 787 int input; 788 static state = 0; 789 790 while (ctrlCCount) { 791 DoNextChar(0x03); 792 ctrlCCount--; 793 } 794 if (lineend) { 795 return 1; 796 } 797 #if 1 /* For BIOS variety of calls */ 798 if (kbhit() == 0) { 799 return lineend; 800 } 801 input = getch(); /* MSC - get console character */ 802 if ((input&0xff) == 0) { 803 DoNextChar(0x01); /* ^A */ 804 } else { 805 DoNextChar(input&0xff); 806 } 807 #else /* 0 */ 808 if ((input = dirconio()) == -1) { 809 return lineend; 810 } 811 if ((input&0xff) == 0) { 812 if ((input&0xff00) == 0x0300) { /* Null */ 813 DoNextChar(0); 814 } else { 815 DoNextChar(0x01); 816 if (input&0x8000) { 817 DoNextChar(0x01); 818 DoNextChar((input>>8)&0x7f); 819 } else { 820 DoNextChar((input>>8)&0xff); 821 } 822 } 823 } else { 824 DoNextChar(input&0xff); 825 } 826 #endif /* 0 */ 827 return lineend; 828 } 829 830 831 void 832 CtrlCInterrupt() 833 { 834 if (!MODE_COMMAND_LINE(globalmode)) { 835 char far *Bios_Break = (char far *) (((long)0x40<<16)|0x71); 836 837 *Bios_Break = 0; 838 ctrlCCount++; /* XXX */ 839 signal(SIGINT, CtrlCInterrupt); 840 } else { 841 closeallsockets(); 842 exit(1); 843 } 844 } 845 846 int 847 dosbinary(fd, onoff) 848 int fd; 849 int onoff; 850 { 851 union REGS regs; 852 int oldstate; 853 854 /* Get old stuff */ 855 regs.h.ah = 0x44; 856 regs.h.al = 0; 857 regs.x.bx = fd; 858 intdos(®s, ®s); 859 oldstate = regs.h.dl&(1<<5); /* Save state */ 860 861 /* Set correct bits in new mode */ 862 regs.h.dh = 0; 863 if (onoff) { 864 regs.h.dl |= 1<<5; 865 } else { 866 regs.h.dl &= ~(1<<5); 867 } 868 869 /* Set in new mode */ 870 regs.h.ah = 0x44; 871 regs.h.al = 1; 872 regs.x.bx = fd; 873 intdos(®s, ®s); 874 875 return oldstate; 876 } 877 878 /* 879 * The MSDOS routines, called from elsewhere. 880 */ 881 882 883 static int 884 TerminalAutoFlush() /* MSDOS */ 885 { 886 return 1; 887 } 888 889 static int 890 TerminalCanRead() 891 { 892 return inputExists(); 893 } 894 895 896 /* 897 * Flush output to the terminal 898 */ 899 900 static void 901 TerminalFlushOutput() /* MSDOS */ 902 { 903 } 904 905 906 static void 907 TerminalNewMode(f) /* MSDOS */ 908 register int f; 909 { 910 union REGS inregs; 911 struct SREGS segregs; 912 static old_1b_offset = 0, old_1b_segment = 0; 913 914 globalmode = f; 915 if (MODE_COMMAND_LINE(f)) { 916 signal(SIGINT, SIG_DFL); 917 if (old_1b_segment|old_1b_offset) { 918 inregs.h.ah = 0x25; 919 inregs.h.al = 0x1b; 920 inregs.x.dx = old_1b_offset; 921 segregs.ds = old_1b_segment; 922 intdosx(&inregs, &inregs, &segregs); 923 old_1b_segment = old_1b_offset = 0; 924 } 925 if (setmode(fileno(stdout), O_TEXT) == -1) { 926 ExitPerror("setmode (text)", 1); 927 } 928 (void) dosbinary(fileno(stdout), 0); 929 if (setmode(fileno(stdin), O_TEXT) == -1) { 930 ExitPerror("setmode (text)", 1); 931 } 932 (void) dosbinary(fileno(stdin), 0); 933 } else { 934 signal(SIGINT, CtrlCInterrupt); 935 if ((old_1b_segment|old_1b_offset) == 0) { 936 extern void iret_subr(); 937 void (far *foo_subr)() = iret_subr; 938 939 inregs.h.ah = 0x35; 940 inregs.h.al = 0x1b; 941 intdosx(&inregs, &inregs, &segregs); 942 old_1b_segment = segregs.es; 943 old_1b_offset = inregs.x.bx; 944 inregs.h.ah = 0x25; 945 inregs.h.al = 0x1b; 946 inregs.x.dx = FP_OFF(foo_subr); 947 segregs.ds = FP_SEG(foo_subr); 948 intdosx(&inregs, &inregs, &segregs); 949 } 950 if (MODE_LOCAL_CHARS(f)) { 951 if (setmode(fileno(stdout), O_TEXT) == -1) { 952 ExitPerror("setmode (text)", 1); 953 } 954 (void) dosbinary(fileno(stdout), 0); 955 if (setmode(fileno(stdin), O_TEXT) == -1) { 956 ExitPerror("setmode (text)", 1); 957 } 958 (void) dosbinary(fileno(stdin), 0); 959 } else { 960 if (setmode(fileno(stdout), O_BINARY) == -1) { 961 ExitPerror("setmode (binary)", 1); 962 } 963 (void) dosbinary(fileno(stdout), 1); 964 if (setmode(fileno(stdin), O_BINARY) == -1) { 965 ExitPerror("setmode (binary)", 1); 966 } 967 (void) dosbinary(fileno(stdin), 1); 968 } 969 } 970 } 971 972 973 int 974 TerminalRead(fd, buffer, count) 975 int fd; 976 char *buffer; 977 int count; 978 { 979 int done = 0; 980 981 for (;;) { 982 while (inputExists() && (done < count)) { 983 *buffer++ = *nextout; 984 consumechar(); 985 done++; 986 } 987 if (done) { 988 return(done); 989 } else { 990 return 0; 991 } 992 } 993 } 994 995 996 static void 997 TerminalSaveState() /* MSDOS */ 998 { 999 savedInState = dosbinary(fileno(stdin), 0); 1000 savedOutState = dosbinary(fileno(stdout), 0); 1001 } 1002 1003 int 1004 TerminalSpecialChars(c) /* MSDOS */ 1005 { 1006 return 1; 1007 } 1008 1009 1010 static void 1011 TerminalRestoreState() /* MSDOS */ 1012 { 1013 (void) dosbinary(fileno(stdin), savedInState); 1014 (void) dosbinary(fileno(stdout), savedOutState); 1015 } 1016 1017 1018 static int 1019 TerminalWrite(fd, buffer, count) /* MSDOS */ 1020 int fd; 1021 char *buffer; 1022 int count; 1023 { 1024 return fwrite(buffer, sizeof (char), count, stdout); 1025 } 1026 1027 1028 static int 1029 NetClose(fd) 1030 { 1031 return closesocket(fd); 1032 } 1033 1034 static void 1035 NetNonblockingIO(fd, onoff) /* MSDOS */ 1036 int 1037 fd, 1038 onoff; 1039 { 1040 if (SetSockOpt(net, SOL_SOCKET, SO_NONBLOCKING, onoff)) { 1041 perror("setsockop (SO_NONBLOCKING) "); 1042 ExitString(stderr, "exiting\n", 1); 1043 } 1044 } 1045 1046 static void 1047 NetSigIO(fd) /* MSDOS */ 1048 int fd; 1049 { 1050 } 1051 1052 static void 1053 NetSetPgrp(fd) /* MSDOS */ 1054 int fd; 1055 { 1056 } 1057 1058 1059 #endif /* defined(MSDOS) */ 1060 1061 /* 1062 * Initialize variables. 1063 */ 1064 1065 static void 1066 tninit() 1067 { 1068 #if defined(TN3270) 1069 Sent3270TerminalType = 0; 1070 Ifrontp = Ibackp = Ibuf; 1071 #endif /* defined(TN3270) */ 1072 1073 tfrontp = tbackp = ttyobuf; 1074 nfrontp = nbackp = netobuf; 1075 1076 /* Don't change telnetport */ 1077 SB_CLEAR(); 1078 ClearArray(hisopts); 1079 ClearArray(myopts); 1080 sbp = sibuf; 1081 tbp = tibuf; 1082 1083 connected = net = scc = tcc = In3270 = ISend = donebinarytoggle = 0; 1084 telnetport = 0; 1085 #if defined(unix) 1086 HaveInput = 0; 1087 #endif /* defined(unix) */ 1088 1089 SYNCHing = 0; 1090 1091 errno = 0; 1092 1093 flushline = 0; 1094 1095 /* Don't change NetTrace */ 1096 1097 escape = CONTROL(']'); 1098 echoc = CONTROL('E'); 1099 1100 flushline = 1; 1101 sp = getservbyname("telnet", "tcp"); 1102 if (sp == 0) { 1103 ExitString(stderr, "telnet: tcp/telnet: unknown service\n",1); 1104 /*NOTREACHED*/ 1105 } 1106 1107 #if defined(TN3270) 1108 init_ctlr(); /* Initialize some things */ 1109 init_keyboard(); 1110 init_screen(); 1111 init_system(); 1112 #endif /* defined(TN3270) */ 1113 } 1114 1115 /* 1116 * Various utility routines. 1117 */ 1118 1119 static void 1120 makeargv() 1121 { 1122 register char *cp; 1123 register char **argp = margv; 1124 1125 margc = 0; 1126 cp = line; 1127 if (*cp == '!') { /* Special case shell escape */ 1128 *argp++ = "!"; /* No room in string to get this */ 1129 margc++; 1130 cp++; 1131 } 1132 while (*cp) { 1133 while (isspace(*cp)) 1134 cp++; 1135 if (*cp == '\0') 1136 break; 1137 *argp++ = cp; 1138 margc += 1; 1139 while (*cp != '\0' && !isspace(*cp)) 1140 cp++; 1141 if (*cp == '\0') 1142 break; 1143 *cp++ = '\0'; 1144 } 1145 *argp++ = 0; 1146 } 1147 1148 static char *ambiguous; /* special return value */ 1149 #define Ambiguous(t) ((t)&ambiguous) 1150 1151 1152 static char ** 1153 genget(name, table, next) 1154 char *name; /* name to match */ 1155 char **table; /* name entry in table */ 1156 char **(*next)(); /* routine to return next entry in table */ 1157 { 1158 register char *p, *q; 1159 register char **c, **found; 1160 register int nmatches, longest; 1161 1162 if (name == 0) { 1163 return 0; 1164 } 1165 longest = 0; 1166 nmatches = 0; 1167 found = 0; 1168 for (c = table; (p = *c) != 0; c = (*next)(c)) { 1169 for (q = name; 1170 (*q == *p) || (isupper(*q) && tolower(*q) == *p); p++, q++) 1171 if (*q == 0) /* exact match? */ 1172 return (c); 1173 if (!*q) { /* the name was a prefix */ 1174 if (q - name > longest) { 1175 longest = q - name; 1176 nmatches = 1; 1177 found = c; 1178 } else if (q - name == longest) 1179 nmatches++; 1180 } 1181 } 1182 if (nmatches > 1) 1183 return Ambiguous(char **); 1184 return (found); 1185 } 1186 1187 /* 1188 * Make a character string into a number. 1189 * 1190 * Todo: 1. Could take random integers (12, 0x12, 012, 0b1). 1191 */ 1192 1193 static 1194 special(s) 1195 register char *s; 1196 { 1197 register char c; 1198 char b; 1199 1200 switch (*s) { 1201 case '^': 1202 b = *++s; 1203 if (b == '?') { 1204 c = b | 0x40; /* DEL */ 1205 } else { 1206 c = b & 0x1f; 1207 } 1208 break; 1209 default: 1210 c = *s; 1211 break; 1212 } 1213 return c; 1214 } 1215 1216 /* 1217 * Construct a control character sequence 1218 * for a special character. 1219 */ 1220 static char * 1221 control(c) 1222 register int c; 1223 { 1224 static char buf[3]; 1225 1226 if (c == 0x7f) 1227 return ("^?"); 1228 if (c == '\377') { 1229 return "off"; 1230 } 1231 if (c >= 0x20) { 1232 buf[0] = c; 1233 buf[1] = 0; 1234 } else { 1235 buf[0] = '^'; 1236 buf[1] = '@'+c; 1237 buf[2] = 0; 1238 } 1239 return (buf); 1240 } 1241 1242 1243 /* 1244 * upcase() 1245 * 1246 * Upcase (in place) the argument. 1247 */ 1248 1249 static void 1250 upcase(argument) 1251 register char *argument; 1252 { 1253 register int c; 1254 1255 while ((c = *argument) != 0) { 1256 if (islower(c)) { 1257 *argument = toupper(c); 1258 } 1259 argument++; 1260 } 1261 } 1262 1263 /* 1264 * SetSockOpt() 1265 * 1266 * Compensate for differences in 4.2 and 4.3 systems. 1267 */ 1268 1269 static int 1270 SetSockOpt(fd, level, option, yesno) 1271 int 1272 fd, 1273 level, 1274 option, 1275 yesno; 1276 { 1277 #ifndef NOT43 1278 return setsockopt(fd, level, option, 1279 (char *)&yesno, sizeof yesno); 1280 #else /* NOT43 */ 1281 if (yesno == 0) { /* Can't do that in 4.2! */ 1282 fprintf(stderr, "Error: attempt to turn off an option 0x%x.\n", 1283 option); 1284 return -1; 1285 } 1286 return setsockopt(fd, level, option, 0, 0); 1287 #endif /* NOT43 */ 1288 } 1289 1290 /* 1291 * The following are routines used to print out debugging information. 1292 */ 1293 1294 1295 static void 1296 Dump(direction, buffer, length) 1297 char direction; 1298 char *buffer; 1299 int length; 1300 { 1301 # define BYTES_PER_LINE 32 1302 # define min(x,y) ((x<y)? x:y) 1303 char *pThis; 1304 int offset; 1305 1306 offset = 0; 1307 1308 while (length) { 1309 /* print one line */ 1310 fprintf(NetTrace, "%c 0x%x\t", direction, offset); 1311 pThis = buffer; 1312 buffer = buffer+min(length, BYTES_PER_LINE); 1313 while (pThis < buffer) { 1314 fprintf(NetTrace, "%.2x", (*pThis)&0xff); 1315 pThis++; 1316 } 1317 fprintf(NetTrace, "\n"); 1318 length -= BYTES_PER_LINE; 1319 offset += BYTES_PER_LINE; 1320 if (length < 0) { 1321 return; 1322 } 1323 /* find next unique line */ 1324 } 1325 } 1326 1327 1328 /*VARARGS*/ 1329 static void 1330 printoption(direction, fmt, option, what) 1331 char *direction, *fmt; 1332 int option, what; 1333 { 1334 if (!showoptions) 1335 return; 1336 fprintf(NetTrace, "%s ", direction+1); 1337 if (fmt == doopt) 1338 fmt = "do"; 1339 else if (fmt == dont) 1340 fmt = "dont"; 1341 else if (fmt == will) 1342 fmt = "will"; 1343 else if (fmt == wont) 1344 fmt = "wont"; 1345 else 1346 fmt = "???"; 1347 if (option < (sizeof telopts/sizeof telopts[0])) 1348 fprintf(NetTrace, "%s %s", fmt, telopts[option]); 1349 else 1350 fprintf(NetTrace, "%s %d", fmt, option); 1351 if (*direction == '<') { 1352 fprintf(NetTrace, "\r\n"); 1353 return; 1354 } 1355 fprintf(NetTrace, " (%s)\r\n", what ? "reply" : "don't reply"); 1356 } 1357 1358 static void 1359 printsub(direction, pointer, length) 1360 char *direction, /* "<" or ">" */ 1361 *pointer; /* where suboption data sits */ 1362 int length; /* length of suboption data */ 1363 { 1364 if (showoptions) { 1365 fprintf(NetTrace, "%s suboption ", 1366 (direction[0] == '<')? "Received":"Sent"); 1367 switch (pointer[0]) { 1368 case TELOPT_TTYPE: 1369 fprintf(NetTrace, "Terminal type "); 1370 switch (pointer[1]) { 1371 case TELQUAL_IS: 1372 { 1373 char tmpbuf[sizeof subbuffer]; 1374 int minlen = min(length-4, sizeof tmpbuf-1); 1375 1376 memcpy(tmpbuf, pointer+2, minlen); 1377 tmpbuf[minlen] = 0; 1378 fprintf(NetTrace, "is %s.\n", tmpbuf); 1379 } 1380 break; 1381 case TELQUAL_SEND: 1382 fprintf(NetTrace, "- request to send.\n"); 1383 break; 1384 default: 1385 fprintf(NetTrace, 1386 "- unknown qualifier %d (0x%x).\n", pointer[1]); 1387 } 1388 break; 1389 default: 1390 fprintf(NetTrace, "Unknown option %d (0x%x)\n", 1391 pointer[0], pointer[0]); 1392 } 1393 } 1394 } 1395 1396 /* 1397 * Check to see if any out-of-band data exists on a socket (for 1398 * Telnet "synch" processing). 1399 */ 1400 1401 static int 1402 stilloob(s) 1403 int s; /* socket number */ 1404 { 1405 static struct timeval timeout = { 0 }; 1406 fd_set excepts; 1407 int value; 1408 1409 do { 1410 FD_ZERO(&excepts); 1411 FD_SET(s, &excepts); 1412 value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 1413 } while ((value == -1) && (errno == EINTR)); 1414 1415 if (value < 0) { 1416 perror("select"); 1417 quit(); 1418 } 1419 if (FD_ISSET(s, &excepts)) { 1420 return 1; 1421 } else { 1422 return 0; 1423 } 1424 } 1425 1426 1427 /* 1428 * netflush 1429 * Send as much data as possible to the network, 1430 * handling requests for urgent data. 1431 * 1432 * The return value indicates whether we did any 1433 * useful work. 1434 */ 1435 1436 1437 int 1438 netflush() 1439 { 1440 int n; 1441 1442 if ((n = nfrontp - nbackp) > 0) { 1443 if (!neturg) { 1444 n = send(net, nbackp, n, 0); /* normal write */ 1445 } else { 1446 n = neturg - nbackp; 1447 /* 1448 * In 4.2 (and 4.3) systems, there is some question about 1449 * what byte in a sendOOB operation is the "OOB" data. 1450 * To make ourselves compatible, we only send ONE byte 1451 * out of band, the one WE THINK should be OOB (though 1452 * we really have more the TCP philosophy of urgent data 1453 * rather than the Unix philosophy of OOB data). 1454 */ 1455 if (n > 1) { 1456 n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 1457 } else { 1458 n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 1459 } 1460 } 1461 } 1462 if (n < 0) { 1463 if (errno != ENOBUFS && errno != EWOULDBLOCK) { 1464 setcommandmode(); 1465 perror(hostname); 1466 NetClose(net); 1467 neturg = 0; 1468 longjmp(peerdied, -1); 1469 /*NOTREACHED*/ 1470 } 1471 n = 0; 1472 } 1473 if (netdata && n) { 1474 Dump('>', nbackp, n); 1475 } 1476 nbackp += n; 1477 if (nbackp >= neturg) { 1478 neturg = 0; 1479 } 1480 if (nbackp == nfrontp) { 1481 nbackp = nfrontp = netobuf; 1482 } 1483 return n > 0; 1484 } 1485 1486 /* 1487 * nextitem() 1488 * 1489 * Return the address of the next "item" in the TELNET data 1490 * stream. This will be the address of the next character if 1491 * the current address is a user data character, or it will 1492 * be the address of the character following the TELNET command 1493 * if the current address is a TELNET IAC ("I Am a Command") 1494 * character. 1495 */ 1496 1497 static char * 1498 nextitem(current) 1499 char *current; 1500 { 1501 if ((*current&0xff) != IAC) { 1502 return current+1; 1503 } 1504 switch (*(current+1)&0xff) { 1505 case DO: 1506 case DONT: 1507 case WILL: 1508 case WONT: 1509 return current+3; 1510 case SB: /* loop forever looking for the SE */ 1511 { 1512 register char *look = current+2; 1513 1514 for (;;) { 1515 if ((*look++&0xff) == IAC) { 1516 if ((*look++&0xff) == SE) { 1517 return look; 1518 } 1519 } 1520 } 1521 } 1522 default: 1523 return current+2; 1524 } 1525 } 1526 /* 1527 * netclear() 1528 * 1529 * We are about to do a TELNET SYNCH operation. Clear 1530 * the path to the network. 1531 * 1532 * Things are a bit tricky since we may have sent the first 1533 * byte or so of a previous TELNET command into the network. 1534 * So, we have to scan the network buffer from the beginning 1535 * until we are up to where we want to be. 1536 * 1537 * A side effect of what we do, just to keep things 1538 * simple, is to clear the urgent data pointer. The principal 1539 * caller should be setting the urgent data pointer AFTER calling 1540 * us in any case. 1541 */ 1542 1543 static void 1544 netclear() 1545 { 1546 register char *thisitem, *next; 1547 char *good; 1548 #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 1549 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 1550 1551 thisitem = netobuf; 1552 1553 while ((next = nextitem(thisitem)) <= nbackp) { 1554 thisitem = next; 1555 } 1556 1557 /* Now, thisitem is first before/at boundary. */ 1558 1559 good = netobuf; /* where the good bytes go */ 1560 1561 while (nfrontp > thisitem) { 1562 if (wewant(thisitem)) { 1563 int length; 1564 1565 next = thisitem; 1566 do { 1567 next = nextitem(next); 1568 } while (wewant(next) && (nfrontp > next)); 1569 length = next-thisitem; 1570 memcpy(good, thisitem, length); 1571 good += length; 1572 thisitem = next; 1573 } else { 1574 thisitem = nextitem(thisitem); 1575 } 1576 } 1577 1578 nbackp = netobuf; 1579 nfrontp = good; /* next byte to be sent */ 1580 neturg = 0; 1581 } 1582 1583 /* 1584 * These routines add various telnet commands to the data stream. 1585 */ 1586 1587 #if defined(NOT43) 1588 static int 1589 #else /* defined(NOT43) */ 1590 static void 1591 #endif /* defined(NOT43) */ 1592 dosynch() 1593 { 1594 netclear(); /* clear the path to the network */ 1595 NET2ADD(IAC, DM); 1596 neturg = NETLOC()-1; /* Some systems are off by one XXX */ 1597 1598 #if defined(NOT43) 1599 return 0; 1600 #endif /* defined(NOT43) */ 1601 } 1602 1603 static void 1604 doflush() 1605 { 1606 NET2ADD(IAC, DO); 1607 NETADD(TELOPT_TM); 1608 flushline = 1; 1609 flushout = 1; 1610 ttyflush(); 1611 /* do printoption AFTER flush, otherwise the output gets tossed... */ 1612 printoption("<SENT", doopt, TELOPT_TM, 0); 1613 } 1614 1615 static void 1616 intp() 1617 { 1618 NET2ADD(IAC, IP); 1619 if (autoflush) { 1620 doflush(); 1621 } 1622 if (autosynch) { 1623 dosynch(); 1624 } 1625 } 1626 1627 static void 1628 sendbrk() 1629 { 1630 NET2ADD(IAC, BREAK); 1631 if (autoflush) { 1632 doflush(); 1633 } 1634 if (autosynch) { 1635 dosynch(); 1636 } 1637 } 1638 1639 /* 1640 * Send as much data as possible to the terminal. 1641 * 1642 * The return value indicates whether we did any 1643 * useful work. 1644 */ 1645 1646 1647 static int 1648 ttyflush() 1649 { 1650 int n; 1651 1652 if ((n = tfrontp - tbackp) > 0) { 1653 if (!(SYNCHing||flushout)) { 1654 n = TerminalWrite(tout, tbackp, n); 1655 } else { 1656 TerminalFlushOutput(); 1657 /* we leave 'n' alone! */ 1658 } 1659 } 1660 if (n >= 0) { 1661 tbackp += n; 1662 if (tbackp == tfrontp) { 1663 tbackp = tfrontp = ttyobuf; 1664 } 1665 } 1666 return n > 0; 1667 } 1668 1669 #if defined(TN3270) 1670 1671 #if defined(unix) 1672 static void 1673 inputAvailable() 1674 { 1675 HaveInput = 1; 1676 } 1677 #endif /* defined(unix) */ 1678 1679 void 1680 outputPurge() 1681 { 1682 int tmp = flushout; 1683 1684 flushout = 1; 1685 1686 ttyflush(); 1687 1688 flushout = tmp; 1689 } 1690 1691 #endif /* defined(TN3270) */ 1692 1693 #if defined(unix) 1694 /* 1695 * Various signal handling routines. 1696 */ 1697 1698 static void 1699 deadpeer() 1700 { 1701 setcommandmode(); 1702 longjmp(peerdied, -1); 1703 } 1704 1705 static void 1706 intr() 1707 { 1708 if (localchars) { 1709 intp(); 1710 return; 1711 } 1712 setcommandmode(); 1713 longjmp(toplevel, -1); 1714 } 1715 1716 static void 1717 intr2() 1718 { 1719 if (localchars) { 1720 sendbrk(); 1721 return; 1722 } 1723 } 1724 1725 static void 1726 doescape() 1727 { 1728 command(0); 1729 } 1730 #endif /* defined(unix) */ 1731 1732 /* 1733 * These routines decides on what the mode should be (based on the values 1734 * of various global variables). 1735 */ 1736 1737 1738 static 1739 getconnmode() 1740 { 1741 static char newmode[16] = 1742 { 4, 5, 3, 3, 2, 2, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6 }; 1743 int modeindex = 0; 1744 1745 if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) { 1746 modeindex += 1; 1747 } 1748 if (hisopts[TELOPT_ECHO]) { 1749 modeindex += 2; 1750 } 1751 if (hisopts[TELOPT_SGA]) { 1752 modeindex += 4; 1753 } 1754 if (In3270) { 1755 modeindex += 8; 1756 } 1757 return newmode[modeindex]; 1758 } 1759 1760 void 1761 setconnmode() 1762 { 1763 TerminalNewMode(getconnmode()); 1764 } 1765 1766 1767 void 1768 setcommandmode() 1769 { 1770 TerminalNewMode(0); 1771 } 1772 1773 static void 1774 willoption(option, reply) 1775 int option, reply; 1776 { 1777 char *fmt; 1778 1779 switch (option) { 1780 1781 case TELOPT_ECHO: 1782 # if defined(TN3270) 1783 /* 1784 * The following is a pain in the rear-end. 1785 * Various IBM servers (some versions of Wiscnet, 1786 * possibly Fibronics/Spartacus, and who knows who 1787 * else) will NOT allow us to send "DO SGA" too early 1788 * in the setup proceedings. On the other hand, 1789 * 4.2 servers (telnetd) won't set SGA correctly. 1790 * So, we are stuck. Empirically (but, based on 1791 * a VERY small sample), the IBM servers don't send 1792 * out anything about ECHO, so we postpone our sending 1793 * "DO SGA" until we see "WILL ECHO" (which 4.2 servers 1794 * DO send). 1795 */ 1796 { 1797 if (askedSGA == 0) { 1798 askedSGA = 1; 1799 if (!hisopts[TELOPT_SGA]) { 1800 willoption(TELOPT_SGA, 0); 1801 } 1802 } 1803 } 1804 /* Fall through */ 1805 case TELOPT_EOR: 1806 case TELOPT_BINARY: 1807 #endif /* defined(TN3270) */ 1808 case TELOPT_SGA: 1809 settimer(modenegotiated); 1810 hisopts[option] = 1; 1811 fmt = doopt; 1812 setconnmode(); /* possibly set new tty mode */ 1813 break; 1814 1815 case TELOPT_TM: 1816 return; /* Never reply to TM will's/wont's */ 1817 1818 default: 1819 fmt = dont; 1820 break; 1821 } 1822 sprintf(nfrontp, fmt, option); 1823 nfrontp += sizeof (dont) - 2; 1824 if (reply) 1825 printoption(">SENT", fmt, option, reply); 1826 else 1827 printoption("<SENT", fmt, option, reply); 1828 } 1829 1830 static void 1831 wontoption(option, reply) 1832 int option, reply; 1833 { 1834 char *fmt; 1835 1836 switch (option) { 1837 1838 case TELOPT_ECHO: 1839 case TELOPT_SGA: 1840 settimer(modenegotiated); 1841 hisopts[option] = 0; 1842 fmt = dont; 1843 setconnmode(); /* Set new tty mode */ 1844 break; 1845 1846 case TELOPT_TM: 1847 return; /* Never reply to TM will's/wont's */ 1848 1849 default: 1850 fmt = dont; 1851 hisopts[option] = 0; 1852 break; 1853 } 1854 sprintf(nfrontp, fmt, option); 1855 nfrontp += sizeof (doopt) - 2; 1856 if (reply) 1857 printoption(">SENT", fmt, option, reply); 1858 else 1859 printoption("<SENT", fmt, option, reply); 1860 } 1861 1862 static void 1863 dooption(option) 1864 int option; 1865 { 1866 char *fmt; 1867 1868 switch (option) { 1869 1870 case TELOPT_TM: 1871 fmt = will; 1872 break; 1873 1874 # if defined(TN3270) 1875 case TELOPT_EOR: 1876 case TELOPT_BINARY: 1877 # endif /* defined(TN3270) */ 1878 case TELOPT_TTYPE: /* terminal type option */ 1879 case TELOPT_SGA: /* no big deal */ 1880 fmt = will; 1881 myopts[option] = 1; 1882 break; 1883 1884 case TELOPT_ECHO: /* We're never going to echo... */ 1885 default: 1886 fmt = wont; 1887 break; 1888 } 1889 sprintf(nfrontp, fmt, option); 1890 nfrontp += sizeof (doopt) - 2; 1891 printoption(">SENT", fmt, option, 0); 1892 } 1893 1894 /* 1895 * suboption() 1896 * 1897 * Look at the sub-option buffer, and try to be helpful to the other 1898 * side. 1899 * 1900 * Currently we recognize: 1901 * 1902 * Terminal type, send request. 1903 */ 1904 1905 static void 1906 suboption() 1907 { 1908 printsub("<", subbuffer, subend-subbuffer+1); 1909 switch (subbuffer[0]&0xff) { 1910 case TELOPT_TTYPE: 1911 if ((subbuffer[1]&0xff) != TELQUAL_SEND) { 1912 ; 1913 } else { 1914 char *name; 1915 char namebuf[41]; 1916 extern char *getenv(); 1917 int len; 1918 1919 #if defined(TN3270) 1920 /* 1921 * Try to send a 3270 type terminal name. Decide which one based 1922 * on the format of our screen, and (in the future) color 1923 * capaiblities. 1924 */ 1925 #if defined(unix) 1926 if (initscr() != ERR) { /* Initialize curses to get line size */ 1927 MaxNumberLines = LINES; 1928 MaxNumberColumns = COLS; 1929 } 1930 #else /* defined(unix) */ 1931 InitTerminal(); 1932 #endif /* defined(unix) */ 1933 if ((MaxNumberLines >= 24) && (MaxNumberColumns >= 80)) { 1934 Sent3270TerminalType = 1; 1935 if ((MaxNumberLines >= 27) && (MaxNumberColumns >= 132)) { 1936 MaxNumberLines = 27; 1937 MaxNumberColumns = 132; 1938 sb_terminal[SBTERMMODEL] = '5'; 1939 } else if (MaxNumberLines >= 43) { 1940 MaxNumberLines = 43; 1941 MaxNumberColumns = 80; 1942 sb_terminal[SBTERMMODEL] = '4'; 1943 } else if (MaxNumberLines >= 32) { 1944 MaxNumberLines = 32; 1945 MaxNumberColumns = 80; 1946 sb_terminal[SBTERMMODEL] = '3'; 1947 } else { 1948 MaxNumberLines = 24; 1949 MaxNumberColumns = 80; 1950 sb_terminal[SBTERMMODEL] = '2'; 1951 } 1952 NumberLines = 24; /* before we start out... */ 1953 NumberColumns = 80; 1954 ScreenSize = NumberLines*NumberColumns; 1955 if ((MaxNumberLines*MaxNumberColumns) > MAXSCREENSIZE) { 1956 ExitString(stderr, 1957 "Programming error: MAXSCREENSIZE too small.\n", 1); 1958 /*NOTREACHED*/ 1959 } 1960 memcpy(nfrontp, sb_terminal, sizeof sb_terminal); 1961 printsub(">", nfrontp+2, sizeof sb_terminal-2); 1962 nfrontp += sizeof sb_terminal; 1963 return; 1964 } 1965 #endif /* defined(TN3270) */ 1966 1967 name = getenv("TERM"); 1968 if ((name == 0) || ((len = strlen(name)) > 40)) { 1969 name = "UNKNOWN"; 1970 } 1971 if ((len + 4+2) < NETROOM()) { 1972 strcpy(namebuf, name); 1973 upcase(namebuf); 1974 sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 1975 TELQUAL_IS, namebuf, IAC, SE); 1976 printsub(">", nfrontp+2, 4+strlen(namebuf)+2-2-2); 1977 nfrontp += 4+strlen(namebuf)+2; 1978 } else { 1979 ExitString(stderr, "No room in buffer for terminal type.\n", 1980 1); 1981 /*NOTREACHED*/ 1982 } 1983 } 1984 1985 default: 1986 break; 1987 } 1988 } 1989 1990 #if defined(TN3270) 1991 static void 1992 SetIn3270() 1993 { 1994 if (Sent3270TerminalType && myopts[TELOPT_BINARY] 1995 && hisopts[TELOPT_BINARY] && !donebinarytoggle) { 1996 if (!In3270) { 1997 In3270 = 1; 1998 Init3270(); /* Initialize 3270 functions */ 1999 /* initialize terminal key mapping */ 2000 InitTerminal(); /* Start terminal going */ 2001 LocalClearScreen(); /* Make sure the screen is clear */ 2002 setconnmode(); 2003 } 2004 } else { 2005 if (In3270) { 2006 StopScreen(1); 2007 In3270 = 0; 2008 Stop3270(); /* Tell 3270 we aren't here anymore */ 2009 setconnmode(); 2010 } 2011 } 2012 } 2013 #endif /* defined(TN3270) */ 2014 2015 2016 static void 2017 telrcv() 2018 { 2019 register int c; 2020 static int telrcv_state = TS_DATA; 2021 # if defined(TN3270) 2022 register int Scc; 2023 register char *Sbp; 2024 # endif /* defined(TN3270) */ 2025 2026 while ((scc > 0) && (TTYROOM() > 2)) { 2027 c = *sbp++ & 0xff, scc--; 2028 switch (telrcv_state) { 2029 2030 case TS_CR: 2031 telrcv_state = TS_DATA; 2032 if (c == '\0') { 2033 break; /* Ignore \0 after CR */ 2034 } else if (c == '\n') { 2035 if (hisopts[TELOPT_ECHO] && !crmod) { 2036 TTYADD(c); 2037 } 2038 break; 2039 } 2040 /* Else, fall through */ 2041 2042 case TS_DATA: 2043 if (c == IAC) { 2044 telrcv_state = TS_IAC; 2045 continue; 2046 } 2047 # if defined(TN3270) 2048 if (In3270) { 2049 *Ifrontp++ = c; 2050 Sbp = sbp; 2051 Scc = scc; 2052 while (Scc > 0) { 2053 c = *Sbp++ & 0377, Scc--; 2054 if (c == IAC) { 2055 telrcv_state = TS_IAC; 2056 break; 2057 } 2058 *Ifrontp++ = c; 2059 } 2060 sbp = Sbp; 2061 scc = Scc; 2062 } else 2063 # endif /* defined(TN3270) */ 2064 /* 2065 * The 'crmod' hack (see following) is needed 2066 * since we can't * set CRMOD on output only. 2067 * Machines like MULTICS like to send \r without 2068 * \n; since we must turn off CRMOD to get proper 2069 * input, the mapping is done here (sigh). 2070 */ 2071 if ((c == '\r') && !hisopts[TELOPT_BINARY]) { 2072 if (scc > 0) { 2073 c = *sbp&0xff; 2074 if (c == 0) { 2075 sbp++, scc--; 2076 /* a "true" CR */ 2077 TTYADD('\r'); 2078 } else if (!hisopts[TELOPT_ECHO] && 2079 (c == '\n')) { 2080 sbp++, scc--; 2081 TTYADD('\n'); 2082 } else { 2083 TTYADD('\r'); 2084 if (crmod) { 2085 TTYADD('\n'); 2086 } 2087 } 2088 } else { 2089 telrcv_state = TS_CR; 2090 TTYADD('\r'); 2091 if (crmod) { 2092 TTYADD('\n'); 2093 } 2094 } 2095 } else { 2096 TTYADD(c); 2097 } 2098 continue; 2099 2100 case TS_IAC: 2101 switch (c) { 2102 2103 case WILL: 2104 telrcv_state = TS_WILL; 2105 continue; 2106 2107 case WONT: 2108 telrcv_state = TS_WONT; 2109 continue; 2110 2111 case DO: 2112 telrcv_state = TS_DO; 2113 continue; 2114 2115 case DONT: 2116 telrcv_state = TS_DONT; 2117 continue; 2118 2119 case DM: 2120 /* 2121 * We may have missed an urgent notification, 2122 * so make sure we flush whatever is in the 2123 * buffer currently. 2124 */ 2125 SYNCHing = 1; 2126 ttyflush(); 2127 SYNCHing = stilloob(net); 2128 settimer(gotDM); 2129 break; 2130 2131 case NOP: 2132 case GA: 2133 break; 2134 2135 case SB: 2136 SB_CLEAR(); 2137 telrcv_state = TS_SB; 2138 continue; 2139 2140 # if defined(TN3270) 2141 case EOR: 2142 if (In3270) { 2143 Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, 1); 2144 if (Ibackp == Ifrontp) { 2145 Ibackp = Ifrontp = Ibuf; 2146 ISend = 0; /* should have been! */ 2147 } else { 2148 ISend = 1; 2149 } 2150 } 2151 break; 2152 # endif /* defined(TN3270) */ 2153 2154 case IAC: 2155 # if !defined(TN3270) 2156 TTYADD(IAC); 2157 # else /* !defined(TN3270) */ 2158 if (In3270) { 2159 *Ifrontp++ = IAC; 2160 } else { 2161 TTYADD(IAC); 2162 } 2163 # endif /* !defined(TN3270) */ 2164 break; 2165 2166 default: 2167 break; 2168 } 2169 telrcv_state = TS_DATA; 2170 continue; 2171 2172 case TS_WILL: 2173 printoption(">RCVD", will, c, !hisopts[c]); 2174 if (c == TELOPT_TM) { 2175 if (flushout) { 2176 flushout = 0; 2177 } 2178 } else if (!hisopts[c]) { 2179 willoption(c, 1); 2180 } 2181 SetIn3270(); 2182 telrcv_state = TS_DATA; 2183 continue; 2184 2185 case TS_WONT: 2186 printoption(">RCVD", wont, c, hisopts[c]); 2187 if (c == TELOPT_TM) { 2188 if (flushout) { 2189 flushout = 0; 2190 } 2191 } else if (hisopts[c]) { 2192 wontoption(c, 1); 2193 } 2194 SetIn3270(); 2195 telrcv_state = TS_DATA; 2196 continue; 2197 2198 case TS_DO: 2199 printoption(">RCVD", doopt, c, !myopts[c]); 2200 if (!myopts[c]) 2201 dooption(c); 2202 SetIn3270(); 2203 telrcv_state = TS_DATA; 2204 continue; 2205 2206 case TS_DONT: 2207 printoption(">RCVD", dont, c, myopts[c]); 2208 if (myopts[c]) { 2209 myopts[c] = 0; 2210 sprintf(nfrontp, wont, c); 2211 nfrontp += sizeof (wont) - 2; 2212 flushline = 1; 2213 setconnmode(); /* set new tty mode (maybe) */ 2214 printoption(">SENT", wont, c, 0); 2215 } 2216 SetIn3270(); 2217 telrcv_state = TS_DATA; 2218 continue; 2219 2220 case TS_SB: 2221 if (c == IAC) { 2222 telrcv_state = TS_SE; 2223 } else { 2224 SB_ACCUM(c); 2225 } 2226 continue; 2227 2228 case TS_SE: 2229 if (c != SE) { 2230 if (c != IAC) { 2231 SB_ACCUM(IAC); 2232 } 2233 SB_ACCUM(c); 2234 telrcv_state = TS_SB; 2235 } else { 2236 SB_TERM(); 2237 suboption(); /* handle sub-option */ 2238 SetIn3270(); 2239 telrcv_state = TS_DATA; 2240 } 2241 } 2242 } 2243 } 2244 2245 #if defined(TN3270) 2246 2247 /* 2248 * The following routines are places where the various tn3270 2249 * routines make calls into telnet.c. 2250 */ 2251 2252 /* TtyChars() - returns the number of characters in the TTY buffer */ 2253 TtyChars() 2254 { 2255 return(tfrontp-tbackp); 2256 } 2257 2258 /* 2259 * DataToNetwork - queue up some data to go to network. If "done" is set, 2260 * then when last byte is queued, we add on an IAC EOR sequence (so, 2261 * don't call us with "done" until you want that done...) 2262 * 2263 * We actually do send all the data to the network buffer, since our 2264 * only client needs for us to do that. 2265 */ 2266 2267 int 2268 DataToNetwork(buffer, count, done) 2269 register char *buffer; /* where the data is */ 2270 register int count; /* how much to send */ 2271 int done; /* is this the last of a logical block */ 2272 { 2273 register int c; 2274 int origCount; 2275 fd_set o; 2276 2277 origCount = count; 2278 FD_ZERO(&o); 2279 2280 while (count) { 2281 if ((netobuf+sizeof netobuf - nfrontp) < 6) { 2282 netflush(); 2283 while ((netobuf+sizeof netobuf - nfrontp) < 6) { 2284 FD_SET(net, &o); 2285 (void) select(net+1, (fd_set *) 0, &o, (fd_set *) 0, 2286 (struct timeval *) 0); 2287 netflush(); 2288 } 2289 } 2290 c = *buffer++; 2291 count--; 2292 if (c == IAC) { 2293 *nfrontp++ = IAC; 2294 *nfrontp++ = IAC; 2295 } else { 2296 *nfrontp++ = c; 2297 } 2298 } 2299 2300 if (done && !count) { 2301 *nfrontp++ = IAC; 2302 *nfrontp++ = EOR; 2303 netflush(); /* try to move along as quickly as ... */ 2304 } 2305 return(origCount - count); 2306 } 2307 2308 /* DataToTerminal - queue up some data to go to terminal. */ 2309 2310 int 2311 DataToTerminal(buffer, count) 2312 register char *buffer; /* where the data is */ 2313 register int count; /* how much to send */ 2314 { 2315 int origCount; 2316 #if defined(unix) 2317 fd_set o; 2318 2319 FD_ZERO(&o); 2320 #endif /* defined(unix) */ 2321 origCount = count; 2322 2323 while (count) { 2324 if (tfrontp >= ttyobuf+sizeof ttyobuf) { 2325 ttyflush(); 2326 while (tfrontp >= ttyobuf+sizeof ttyobuf) { 2327 #if defined(unix) 2328 FD_SET(tout, &o); 2329 (void) select(tout+1, (fd_set *) 0, &o, (fd_set *) 0, 2330 (struct timeval *) 0); 2331 #endif /* defined(unix) */ 2332 ttyflush(); 2333 } 2334 } 2335 *tfrontp++ = *buffer++; 2336 count--; 2337 } 2338 return(origCount - count); 2339 } 2340 2341 /* EmptyTerminal - called to make sure that the terminal buffer is empty. 2342 * Note that we consider the buffer to run all the 2343 * way to the kernel (thus the select). 2344 */ 2345 2346 void 2347 EmptyTerminal() 2348 { 2349 #if defined(unix) 2350 fd_set o; 2351 2352 FD_ZERO(&o); 2353 #endif /* defined(unix) */ 2354 2355 if (tfrontp == tbackp) { 2356 #if defined(unix) 2357 FD_SET(tout, &o); 2358 (void) select(tout+1, (int *) 0, &o, (int *) 0, 2359 (struct timeval *) 0); /* wait for TTLOWAT */ 2360 #endif /* defined(unix) */ 2361 } else { 2362 while (tfrontp != tbackp) { 2363 ttyflush(); 2364 #if defined(unix) 2365 FD_SET(tout, &o); 2366 (void) select(tout+1, (int *) 0, &o, (int *) 0, 2367 (struct timeval *) 0); /* wait for TTLOWAT */ 2368 #endif /* defined(unix) */ 2369 } 2370 } 2371 } 2372 2373 /* 2374 * Push3270 - Try to send data along the 3270 output (to screen) direction. 2375 */ 2376 2377 static int 2378 Push3270() 2379 { 2380 int save = scc; 2381 2382 if (scc) { 2383 if (Ifrontp+scc > Ibuf+sizeof Ibuf) { 2384 if (Ibackp != Ibuf) { 2385 memcpy(Ibuf, Ibackp, Ifrontp-Ibackp); 2386 Ifrontp -= (Ibackp-Ibuf); 2387 Ibackp = Ibuf; 2388 } 2389 } 2390 if (Ifrontp+scc < Ibuf+sizeof Ibuf) { 2391 telrcv(); 2392 } 2393 } 2394 return save != scc; 2395 } 2396 2397 2398 /* 2399 * Finish3270 - get the last dregs of 3270 data out to the terminal 2400 * before quitting. 2401 */ 2402 2403 static void 2404 Finish3270() 2405 { 2406 while (Push3270() || !DoTerminalOutput()) { 2407 #if defined(unix) 2408 HaveInput = 0; 2409 #endif /* defined(unix) */ 2410 ; 2411 } 2412 } 2413 2414 2415 2416 /* StringToTerminal - output a null terminated string to the terminal */ 2417 2418 void 2419 StringToTerminal(s) 2420 char *s; 2421 { 2422 int count; 2423 2424 count = strlen(s); 2425 if (count) { 2426 (void) DataToTerminal(s, count); /* we know it always goes... */ 2427 } 2428 } 2429 2430 2431 #if defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) 2432 /* _putchar - output a single character to the terminal. This name is so that 2433 * curses(3x) can call us to send out data. 2434 */ 2435 2436 void 2437 _putchar(c) 2438 char c; 2439 { 2440 if (tfrontp >= ttyobuf+sizeof ttyobuf) { 2441 (void) DataToTerminal(&c, 1); 2442 } else { 2443 *tfrontp++ = c; /* optimize if possible. */ 2444 } 2445 } 2446 #endif /* defined(TN3270) && ((!defined(NOT43)) || defined(PUTCHAR)) */ 2447 2448 static void 2449 SetForExit() 2450 { 2451 setconnmode(); 2452 if (In3270) { 2453 Finish3270(); 2454 } 2455 setcommandmode(); 2456 fflush(stdout); 2457 fflush(stderr); 2458 if (In3270) { 2459 StopScreen(1); 2460 } 2461 setconnmode(); 2462 setcommandmode(); 2463 } 2464 2465 static void 2466 Exit(returnCode) 2467 int returnCode; 2468 { 2469 SetForExit(); 2470 exit(returnCode); 2471 } 2472 2473 void 2474 ExitString(file, string, returnCode) 2475 FILE *file; 2476 char *string; 2477 int returnCode; 2478 { 2479 SetForExit(); 2480 fwrite(string, 1, strlen(string), file); 2481 exit(returnCode); 2482 } 2483 2484 void 2485 ExitPerror(string, returnCode) 2486 char *string; 2487 int returnCode; 2488 { 2489 SetForExit(); 2490 perror(string); 2491 exit(returnCode); 2492 } 2493 2494 #endif /* defined(TN3270) */ 2495 2496 /* 2497 * Scheduler() 2498 * 2499 * Try to do something. 2500 * 2501 * If we do something useful, return 1; else return 0. 2502 * 2503 */ 2504 2505 2506 int 2507 Scheduler(block) 2508 int block; /* should we block in the select ? */ 2509 { 2510 register int c; 2511 /* One wants to be a bit careful about setting returnValue 2512 * to one, since a one implies we did some useful work, 2513 * and therefore probably won't be called to block next 2514 * time (TN3270 mode only). 2515 */ 2516 int returnValue = 0; 2517 static struct timeval TimeValue = { 0 }; 2518 2519 if (scc < 0 && tcc < 0) { 2520 return -1; 2521 } 2522 2523 if ((!MODE_LINE(globalmode) || flushline) && NETBYTES()) { 2524 FD_SET(net, &obits); 2525 } 2526 #if !defined(MSDOS) 2527 if (TTYBYTES()) { 2528 FD_SET(tout, &obits); 2529 } 2530 #if defined(TN3270) 2531 if ((tcc == 0) && NETROOM() && (shell_active == 0)) { 2532 FD_SET(tin, &ibits); 2533 } 2534 #else /* defined(TN3270) */ 2535 if ((tcc == 0) && NETROOM()) { 2536 FD_SET(tin, &ibits); 2537 } 2538 #endif /* defined(TN3270) */ 2539 #endif /* !defined(MSDOS) */ 2540 # if !defined(TN3270) 2541 if (TTYROOM()) { 2542 FD_SET(net, &ibits); 2543 } 2544 # else /* !defined(TN3270) */ 2545 if (!ISend && TTYROOM()) { 2546 FD_SET(net, &ibits); 2547 } 2548 # endif /* !defined(TN3270) */ 2549 if (!SYNCHing) { 2550 FD_SET(net, &xbits); 2551 } 2552 # if defined(TN3270) && defined(unix) 2553 if (HaveInput) { 2554 HaveInput = 0; 2555 signal(SIGIO, inputAvailable); 2556 } 2557 #endif /* defined(TN3270) && defined(unix) */ 2558 if ((c = select(16, &ibits, &obits, &xbits, 2559 block? (struct timeval *)0 : &TimeValue)) < 0) { 2560 if (c == -1) { 2561 /* 2562 * we can get EINTR if we are in line mode, 2563 * and the user does an escape (TSTP), or 2564 * some other signal generator. 2565 */ 2566 if (errno == EINTR) { 2567 return 0; 2568 } 2569 # if defined(TN3270) 2570 /* 2571 * we can get EBADF if we were in transparent 2572 * mode, and the transcom process died. 2573 */ 2574 if (errno == EBADF) { 2575 /* 2576 * zero the bits (even though kernel does it) 2577 * to make sure we are selecting on the right 2578 * ones. 2579 */ 2580 FD_ZERO(&ibits); 2581 FD_ZERO(&obits); 2582 FD_ZERO(&xbits); 2583 return 0; 2584 } 2585 # endif /* defined(TN3270) */ 2586 /* I don't like this, does it ever happen? */ 2587 printf("sleep(5) from telnet, after select\r\n"); 2588 #if defined(unix) 2589 sleep(5); 2590 #endif /* defined(unix) */ 2591 } 2592 return 0; 2593 } 2594 2595 /* 2596 * Any urgent data? 2597 */ 2598 if (FD_ISSET(net, &xbits)) { 2599 FD_CLR(net, &xbits); 2600 SYNCHing = 1; 2601 ttyflush(); /* flush already enqueued data */ 2602 } 2603 2604 /* 2605 * Something to read from the network... 2606 */ 2607 if (FD_ISSET(net, &ibits)) { 2608 int canread; 2609 2610 FD_CLR(net, &ibits); 2611 if (scc == 0) { 2612 sbp = sibuf; 2613 } 2614 canread = sibuf + sizeof sibuf - (sbp+scc); 2615 #if !defined(SO_OOBINLINE) 2616 /* 2617 * In 4.2 (and some early 4.3) systems, the 2618 * OOB indication and data handling in the kernel 2619 * is such that if two separate TCP Urgent requests 2620 * come in, one byte of TCP data will be overlaid. 2621 * This is fatal for Telnet, but we try to live 2622 * with it. 2623 * 2624 * In addition, in 4.2 (and...), a special protocol 2625 * is needed to pick up the TCP Urgent data in 2626 * the correct sequence. 2627 * 2628 * What we do is: if we think we are in urgent 2629 * mode, we look to see if we are "at the mark". 2630 * If we are, we do an OOB receive. If we run 2631 * this twice, we will do the OOB receive twice, 2632 * but the second will fail, since the second 2633 * time we were "at the mark", but there wasn't 2634 * any data there (the kernel doesn't reset 2635 * "at the mark" until we do a normal read). 2636 * Once we've read the OOB data, we go ahead 2637 * and do normal reads. 2638 * 2639 * There is also another problem, which is that 2640 * since the OOB byte we read doesn't put us 2641 * out of OOB state, and since that byte is most 2642 * likely the TELNET DM (data mark), we would 2643 * stay in the TELNET SYNCH (SYNCHing) state. 2644 * So, clocks to the rescue. If we've "just" 2645 * received a DM, then we test for the 2646 * presence of OOB data when the receive OOB 2647 * fails (and AFTER we did the normal mode read 2648 * to clear "at the mark"). 2649 */ 2650 if (SYNCHing) { 2651 int atmark; 2652 2653 ioctl(net, SIOCATMARK, (char *)&atmark); 2654 if (atmark) { 2655 c = recv(net, sbp+scc, canread, MSG_OOB); 2656 if ((c == -1) && (errno == EINVAL)) { 2657 c = recv(net, sbp+scc, canread, 0); 2658 if (clocks.didnetreceive < clocks.gotDM) { 2659 SYNCHing = stilloob(net); 2660 } 2661 } 2662 } else { 2663 c = recv(net, sbp+scc, canread, 0); 2664 } 2665 } else { 2666 c = recv(net, sbp+scc, canread, 0); 2667 } 2668 settimer(didnetreceive); 2669 #else /* !defined(SO_OOBINLINE) */ 2670 c = recv(net, sbp+scc, canread, 0); 2671 #endif /* !defined(SO_OOBINLINE) */ 2672 if (c < 0 && errno == EWOULDBLOCK) { 2673 c = 0; 2674 } else if (c <= 0) { 2675 return -1; 2676 } 2677 if (netdata) { 2678 Dump('<', sbp+scc, c); 2679 } 2680 scc += c; 2681 returnValue = 1; 2682 } 2683 2684 /* 2685 * Something to read from the tty... 2686 */ 2687 #if defined(MSDOS) 2688 if ((tcc == 0) && NETROOM() && (shell_active == 0) && TerminalCanRead()) 2689 #else /* defined(MSDOS) */ 2690 if (FD_ISSET(tin, &ibits)) 2691 #endif /* defined(MSDOS) */ 2692 { 2693 FD_CLR(tin, &ibits); 2694 if (tcc == 0) { 2695 tbp = tibuf; /* nothing left, reset */ 2696 } 2697 c = TerminalRead(tin, tbp, tibuf+sizeof tibuf - tbp); 2698 if (c < 0 && errno == EWOULDBLOCK) { 2699 c = 0; 2700 } else { 2701 #if defined(unix) 2702 /* EOF detection for line mode!!!! */ 2703 if (c == 0 && MODE_LOCAL_CHARS(globalmode)) { 2704 /* must be an EOF... */ 2705 *tbp = ntc.t_eofc; 2706 c = 1; 2707 } 2708 #endif /* defined(unix) */ 2709 if (c <= 0) { 2710 tcc = c; 2711 return -1; 2712 } 2713 } 2714 tcc += c; 2715 returnValue = 1; /* did something useful */ 2716 } 2717 2718 # if defined(TN3270) 2719 if (tcc > 0) { 2720 if (In3270) { 2721 c = DataFromTerminal(tbp, tcc); 2722 if (c) { 2723 returnValue = 1; 2724 } 2725 tcc -= c; 2726 tbp += c; 2727 } else { 2728 # endif /* defined(TN3270) */ 2729 returnValue = 1; 2730 while (tcc > 0) { 2731 register int sc; 2732 2733 if (NETROOM() < 2) { 2734 flushline = 1; 2735 break; 2736 } 2737 c = *tbp++ & 0xff, sc = strip(c), tcc--; 2738 if (sc == escape) { 2739 command(0); 2740 tcc = 0; 2741 flushline = 1; 2742 break; 2743 } else if (MODE_LINE(globalmode) && (sc == echoc)) { 2744 if (tcc > 0 && strip(*tbp) == echoc) { 2745 tbp++; 2746 tcc--; 2747 } else { 2748 dontlecho = !dontlecho; 2749 settimer(echotoggle); 2750 setconnmode(); 2751 tcc = 0; 2752 flushline = 1; 2753 break; 2754 } 2755 } 2756 if (localchars) { 2757 if (TerminalSpecialChars(sc) == 0) { 2758 break; 2759 } 2760 } 2761 if (!myopts[TELOPT_BINARY]) { 2762 switch (c) { 2763 case '\n': 2764 /* 2765 * If we are in CRMOD mode (\r ==> \n) 2766 * on our local machine, then probably 2767 * a newline (unix) is CRLF (TELNET). 2768 */ 2769 if (MODE_LOCAL_CHARS(globalmode)) { 2770 NETADD('\r'); 2771 } 2772 NETADD('\n'); 2773 flushline = 1; 2774 break; 2775 case '\r': 2776 if (!crlf) { 2777 NET2ADD('\r', '\0'); 2778 } else { 2779 NET2ADD('\r', '\n'); 2780 } 2781 flushline = 1; 2782 break; 2783 case IAC: 2784 NET2ADD(IAC, IAC); 2785 break; 2786 default: 2787 NETADD(c); 2788 break; 2789 } 2790 } else if (c == IAC) { 2791 NET2ADD(IAC, IAC); 2792 } else { 2793 NETADD(c); 2794 } 2795 } 2796 # if defined(TN3270) 2797 } 2798 } 2799 # endif /* defined(TN3270) */ 2800 2801 if ((!MODE_LINE(globalmode) || flushline || myopts[TELOPT_BINARY]) && 2802 FD_ISSET(net, &obits) && (NETBYTES() > 0)) { 2803 FD_CLR(net, &obits); 2804 returnValue = netflush(); 2805 } 2806 if (scc > 0) { 2807 # if !defined(TN3270) 2808 telrcv(); 2809 returnValue = 1; 2810 # else /* !defined(TN3270) */ 2811 returnValue = Push3270(); 2812 # endif /* !defined(TN3270) */ 2813 } 2814 #if defined(MSDOS) 2815 if (TTYBYTES()) 2816 #else /* defined(MSDOS) */ 2817 if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) 2818 #endif /* defined(MSDOS) */ 2819 { 2820 FD_CLR(tout, &obits); 2821 returnValue = ttyflush(); 2822 } 2823 return returnValue; 2824 } 2825 2826 /* 2827 * Select from tty and network... 2828 */ 2829 static void 2830 telnet() 2831 { 2832 #if defined(MSDOS) 2833 #define SCHED_BLOCK 0 /* Don't block in MSDOS */ 2834 #else /* defined(MSDOS) */ 2835 #define SCHED_BLOCK 1 2836 #endif /* defined(MSDOS) */ 2837 2838 #if defined(TN3270) && defined(unix) 2839 int myPid; 2840 #endif /* defined(TN3270) */ 2841 2842 tout = fileno(stdout); 2843 tin = fileno(stdin); 2844 setconnmode(); 2845 scc = 0; 2846 tcc = 0; 2847 FD_ZERO(&ibits); 2848 FD_ZERO(&obits); 2849 FD_ZERO(&xbits); 2850 2851 NetNonblockingIO(net, 1); 2852 2853 #if defined(TN3270) 2854 if (noasynch == 0) { /* DBX can't handle! */ 2855 NetSigIO(net, 1); 2856 } 2857 NetSetPgrp(net); 2858 #endif /* defined(TN3270) */ 2859 2860 2861 #if defined(SO_OOBINLINE) && !defined(MSDOS) 2862 SetSockOpt(net, SOL_SOCKET, SO_OOBINLINE, 1); 2863 #endif /* defined(SO_OOBINLINE) && !defined(MSDOS) */ 2864 2865 # if !defined(TN3270) 2866 if (telnetport) { 2867 if (!hisopts[TELOPT_SGA]) { 2868 willoption(TELOPT_SGA, 0); 2869 } 2870 if (!myopts[TELOPT_TTYPE]) { 2871 dooption(TELOPT_TTYPE, 0); 2872 } 2873 } 2874 # endif /* !defined(TN3270) */ 2875 2876 # if !defined(TN3270) 2877 for (;;) { 2878 if (Scheduler(SCHED_BLOCK) == -1) { 2879 setcommandmode(); 2880 return; 2881 } 2882 } 2883 # else /* !defined(TN3270) */ 2884 for (;;) { 2885 int schedValue; 2886 2887 while (!In3270 && !shell_active) { 2888 if (Scheduler(SCHED_BLOCK) == -1) { 2889 setcommandmode(); 2890 return; 2891 } 2892 } 2893 2894 while ((schedValue = Scheduler(0)) != 0) { 2895 if (schedValue == -1) { 2896 setcommandmode(); 2897 return; 2898 } 2899 } 2900 /* If there is data waiting to go out to terminal, don't 2901 * schedule any more data for the terminal. 2902 */ 2903 if (tfrontp-tbackp) { 2904 schedValue = 1; 2905 } else { 2906 if (shell_active) { 2907 if (shell_continue() == 0) { 2908 ConnectScreen(); 2909 } 2910 } else if (In3270) { 2911 schedValue = DoTerminalOutput(); 2912 } 2913 } 2914 if (schedValue && (shell_active == 0)) { 2915 if (Scheduler(SCHED_BLOCK) == -1) { 2916 setcommandmode(); 2917 return; 2918 } 2919 } 2920 } 2921 # endif /* !defined(TN3270) */ 2922 } 2923 2924 /* 2925 * The following are data structures and routines for 2926 * the "send" command. 2927 * 2928 */ 2929 2930 struct sendlist { 2931 char *name; /* How user refers to it (case independent) */ 2932 int what; /* Character to be sent (<0 ==> special) */ 2933 char *help; /* Help information (0 ==> no help) */ 2934 #if defined(NOT43) 2935 int (*routine)(); /* Routine to perform (for special ops) */ 2936 #else /* defined(NOT43) */ 2937 void (*routine)(); /* Routine to perform (for special ops) */ 2938 #endif /* defined(NOT43) */ 2939 }; 2940 2941 #define SENDQUESTION -1 2942 #define SENDESCAPE -3 2943 2944 static struct sendlist Sendlist[] = { 2945 { "ao", AO, "Send Telnet Abort output" }, 2946 { "ayt", AYT, "Send Telnet 'Are You There'" }, 2947 { "brk", BREAK, "Send Telnet Break" }, 2948 { "ec", EC, "Send Telnet Erase Character" }, 2949 { "el", EL, "Send Telnet Erase Line" }, 2950 { "escape", SENDESCAPE, "Send current escape character" }, 2951 { "ga", GA, "Send Telnet 'Go Ahead' sequence" }, 2952 { "ip", IP, "Send Telnet Interrupt Process" }, 2953 { "nop", NOP, "Send Telnet 'No operation'" }, 2954 { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch }, 2955 { "?", SENDQUESTION, "Display send options" }, 2956 { 0 } 2957 }; 2958 2959 static struct sendlist Sendlist2[] = { /* some synonyms */ 2960 { "break", BREAK, 0 }, 2961 2962 { "intp", IP, 0 }, 2963 { "interrupt", IP, 0 }, 2964 { "intr", IP, 0 }, 2965 2966 { "help", SENDQUESTION, 0 }, 2967 2968 { 0 } 2969 }; 2970 2971 static char ** 2972 getnextsend(name) 2973 char *name; 2974 { 2975 struct sendlist *c = (struct sendlist *) name; 2976 2977 return (char **) (c+1); 2978 } 2979 2980 static struct sendlist * 2981 getsend(name) 2982 char *name; 2983 { 2984 struct sendlist *sl; 2985 2986 if ((sl = (struct sendlist *) 2987 genget(name, (char **) Sendlist, getnextsend)) != 0) { 2988 return sl; 2989 } else { 2990 return (struct sendlist *) 2991 genget(name, (char **) Sendlist2, getnextsend); 2992 } 2993 } 2994 2995 static 2996 sendcmd(argc, argv) 2997 int argc; 2998 char **argv; 2999 { 3000 int what; /* what we are sending this time */ 3001 int count; /* how many bytes we are going to need to send */ 3002 int i; 3003 int question = 0; /* was at least one argument a question */ 3004 struct sendlist *s; /* pointer to current command */ 3005 3006 if (argc < 2) { 3007 printf("need at least one argument for 'send' command\n"); 3008 printf("'send ?' for help\n"); 3009 return 0; 3010 } 3011 /* 3012 * First, validate all the send arguments. 3013 * In addition, we see how much space we are going to need, and 3014 * whether or not we will be doing a "SYNCH" operation (which 3015 * flushes the network queue). 3016 */ 3017 count = 0; 3018 for (i = 1; i < argc; i++) { 3019 s = getsend(argv[i]); 3020 if (s == 0) { 3021 printf("Unknown send argument '%s'\n'send ?' for help.\n", 3022 argv[i]); 3023 return 0; 3024 } else if (s == Ambiguous(struct sendlist *)) { 3025 printf("Ambiguous send argument '%s'\n'send ?' for help.\n", 3026 argv[i]); 3027 return 0; 3028 } 3029 switch (s->what) { 3030 case SENDQUESTION: 3031 break; 3032 case SENDESCAPE: 3033 count += 1; 3034 break; 3035 case SYNCH: 3036 count += 2; 3037 break; 3038 default: 3039 count += 2; 3040 break; 3041 } 3042 } 3043 /* Now, do we have enough room? */ 3044 if (NETROOM() < count) { 3045 printf("There is not enough room in the buffer TO the network\n"); 3046 printf("to process your request. Nothing will be done.\n"); 3047 printf("('send synch' will throw away most data in the network\n"); 3048 printf("buffer, if this might help.)\n"); 3049 return 0; 3050 } 3051 /* OK, they are all OK, now go through again and actually send */ 3052 for (i = 1; i < argc; i++) { 3053 if ((s = getsend(argv[i])) == 0) { 3054 fprintf(stderr, "Telnet 'send' error - argument disappeared!\n"); 3055 quit(); 3056 /*NOTREACHED*/ 3057 } 3058 if (s->routine) { 3059 (*s->routine)(s); 3060 } else { 3061 switch (what = s->what) { 3062 case SYNCH: 3063 dosynch(); 3064 break; 3065 case SENDQUESTION: 3066 for (s = Sendlist; s->name; s++) { 3067 if (s->help) { 3068 printf(s->name); 3069 if (s->help) { 3070 printf("\t%s", s->help); 3071 } 3072 printf("\n"); 3073 } 3074 } 3075 question = 1; 3076 break; 3077 case SENDESCAPE: 3078 NETADD(escape); 3079 break; 3080 default: 3081 NET2ADD(IAC, what); 3082 break; 3083 } 3084 } 3085 } 3086 return !question; 3087 } 3088 3089 /* 3090 * The following are the routines and data structures referred 3091 * to by the arguments to the "toggle" command. 3092 */ 3093 3094 static 3095 lclchars() 3096 { 3097 donelclchars = 1; 3098 return 1; 3099 } 3100 3101 static 3102 togdebug() 3103 { 3104 #ifndef NOT43 3105 if (net > 0 && 3106 (SetSockOpt(net, SOL_SOCKET, SO_DEBUG, debug)) < 0) { 3107 perror("setsockopt (SO_DEBUG)"); 3108 } 3109 #else /* NOT43 */ 3110 if (debug) { 3111 if (net > 0 && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) 3112 perror("setsockopt (SO_DEBUG)"); 3113 } else 3114 printf("Cannot turn off socket debugging\n"); 3115 #endif /* NOT43 */ 3116 return 1; 3117 } 3118 3119 3120 static int 3121 togcrlf() 3122 { 3123 if (crlf) { 3124 printf("Will send carriage returns as telnet <CR><LF>.\n"); 3125 } else { 3126 printf("Will send carriage returns as telnet <CR><NUL>.\n"); 3127 } 3128 return 1; 3129 } 3130 3131 3132 static int 3133 togbinary() 3134 { 3135 donebinarytoggle = 1; 3136 3137 if (myopts[TELOPT_BINARY] == 0) { /* Go into binary mode */ 3138 NET2ADD(IAC, DO); 3139 NETADD(TELOPT_BINARY); 3140 printoption("<SENT", doopt, TELOPT_BINARY, 0); 3141 NET2ADD(IAC, WILL); 3142 NETADD(TELOPT_BINARY); 3143 printoption("<SENT", doopt, TELOPT_BINARY, 0); 3144 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 1; 3145 printf("Negotiating binary mode with remote host.\n"); 3146 } else { /* Turn off binary mode */ 3147 NET2ADD(IAC, DONT); 3148 NETADD(TELOPT_BINARY); 3149 printoption("<SENT", dont, TELOPT_BINARY, 0); 3150 NET2ADD(IAC, DONT); 3151 NETADD(TELOPT_BINARY); 3152 printoption("<SENT", dont, TELOPT_BINARY, 0); 3153 hisopts[TELOPT_BINARY] = myopts[TELOPT_BINARY] = 0; 3154 printf("Negotiating network ascii mode with remote host.\n"); 3155 } 3156 return 1; 3157 } 3158 3159 3160 3161 extern int togglehelp(); 3162 3163 struct togglelist { 3164 char *name; /* name of toggle */ 3165 char *help; /* help message */ 3166 int (*handler)(); /* routine to do actual setting */ 3167 int dohelp; /* should we display help information */ 3168 int *variable; 3169 char *actionexplanation; 3170 }; 3171 3172 static struct togglelist Togglelist[] = { 3173 { "autoflush", 3174 "toggle flushing of output when sending interrupt characters", 3175 0, 3176 1, 3177 &autoflush, 3178 "flush output when sending interrupt characters" }, 3179 { "autosynch", 3180 "toggle automatic sending of interrupt characters in urgent mode", 3181 0, 3182 1, 3183 &autosynch, 3184 "send interrupt characters in urgent mode" }, 3185 { "binary", 3186 "toggle sending and receiving of binary data", 3187 togbinary, 3188 1, 3189 0, 3190 0 }, 3191 { "crlf", 3192 "toggle sending carriage returns as telnet <CR><LF>", 3193 togcrlf, 3194 1, 3195 &crlf, 3196 0 }, 3197 { "crmod", 3198 "toggle mapping of received carriage returns", 3199 0, 3200 1, 3201 &crmod, 3202 "map carriage return on output" }, 3203 { "localchars", 3204 "toggle local recognition of certain control characters", 3205 lclchars, 3206 1, 3207 &localchars, 3208 "recognize certain control characters" }, 3209 { " ", "", 0, 1 }, /* empty line */ 3210 { "debug", 3211 "(debugging) toggle debugging", 3212 togdebug, 3213 1, 3214 &debug, 3215 "turn on socket level debugging" }, 3216 { "netdata", 3217 "(debugging) toggle printing of hexadecimal network data", 3218 0, 3219 1, 3220 &netdata, 3221 "print hexadecimal representation of network traffic" }, 3222 { "options", 3223 "(debugging) toggle viewing of options processing", 3224 0, 3225 1, 3226 &showoptions, 3227 "show option processing" }, 3228 { " ", "", 0, 1 }, /* empty line */ 3229 { "?", 3230 "display help information", 3231 togglehelp, 3232 1 }, 3233 { "help", 3234 "display help information", 3235 togglehelp, 3236 0 }, 3237 { 0 } 3238 }; 3239 3240 static 3241 togglehelp() 3242 { 3243 struct togglelist *c; 3244 3245 for (c = Togglelist; c->name; c++) { 3246 if (c->dohelp) { 3247 printf("%s\t%s\n", c->name, c->help); 3248 } 3249 } 3250 return 0; 3251 } 3252 3253 static char ** 3254 getnexttoggle(name) 3255 char *name; 3256 { 3257 struct togglelist *c = (struct togglelist *) name; 3258 3259 return (char **) (c+1); 3260 } 3261 3262 static struct togglelist * 3263 gettoggle(name) 3264 char *name; 3265 { 3266 return (struct togglelist *) 3267 genget(name, (char **) Togglelist, getnexttoggle); 3268 } 3269 3270 static 3271 toggle(argc, argv) 3272 int argc; 3273 char *argv[]; 3274 { 3275 int retval = 1; 3276 char *name; 3277 struct togglelist *c; 3278 3279 if (argc < 2) { 3280 fprintf(stderr, 3281 "Need an argument to 'toggle' command. 'toggle ?' for help.\n"); 3282 return 0; 3283 } 3284 argc--; 3285 argv++; 3286 while (argc--) { 3287 name = *argv++; 3288 c = gettoggle(name); 3289 if (c == Ambiguous(struct togglelist *)) { 3290 fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n", 3291 name); 3292 return 0; 3293 } else if (c == 0) { 3294 fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n", 3295 name); 3296 return 0; 3297 } else { 3298 if (c->variable) { 3299 *c->variable = !*c->variable; /* invert it */ 3300 if (c->actionexplanation) { 3301 printf("%s %s.\n", *c->variable? "Will" : "Won't", 3302 c->actionexplanation); 3303 } 3304 } 3305 if (c->handler) { 3306 retval &= (*c->handler)(c); 3307 } 3308 } 3309 } 3310 return retval; 3311 } 3312 3313 /* 3314 * The following perform the "set" command. 3315 */ 3316 3317 struct setlist { 3318 char *name; /* name */ 3319 char *help; /* help information */ 3320 char *charp; /* where it is located at */ 3321 }; 3322 3323 static struct setlist Setlist[] = { 3324 { "echo", "character to toggle local echoing on/off", &echoc }, 3325 { "escape", "character to escape back to telnet command mode", &escape }, 3326 { " ", "" }, 3327 { " ", "The following need 'localchars' to be toggled true", 0 }, 3328 #if defined(unix) 3329 { "erase", "character to cause an Erase Character", &nttyb.sg_erase }, 3330 { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc }, 3331 { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc }, 3332 { "kill", "character to cause an Erase Line", &nttyb.sg_kill }, 3333 { "quit", "character to cause a Break", &ntc.t_quitc }, 3334 { "eof", "character to cause an EOF ", &ntc.t_eofc }, 3335 #endif /* defined(unix) */ 3336 #if defined(MSDOS) 3337 { "erase", "character to cause an Erase Character", &termEraseChar }, 3338 { "flushoutput", "character to cause an Abort Oubput", &termFlushChar }, 3339 { "interrupt", "character to cause an Interrupt Process", &termIntChar }, 3340 { "kill", "character to cause an Erase Line", &termKillChar }, 3341 { "quit", "character to cause a Break", &termQuitChar }, 3342 { "eof", "character to cause an EOF ", &termEofChar }, 3343 #endif /* defined(MSDOS) */ 3344 { 0 } 3345 }; 3346 3347 static char ** 3348 getnextset(name) 3349 char *name; 3350 { 3351 struct setlist *c = (struct setlist *)name; 3352 3353 return (char **) (c+1); 3354 } 3355 3356 static struct setlist * 3357 getset(name) 3358 char *name; 3359 { 3360 return (struct setlist *) genget(name, (char **) Setlist, getnextset); 3361 } 3362 3363 static 3364 setcmd(argc, argv) 3365 int argc; 3366 char *argv[]; 3367 { 3368 int value; 3369 struct setlist *ct; 3370 3371 /* XXX back we go... sigh */ 3372 if (argc != 3) { 3373 if ((argc == 2) && 3374 ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) { 3375 for (ct = Setlist; ct->name; ct++) { 3376 printf("%s\t%s\n", ct->name, ct->help); 3377 } 3378 printf("?\tdisplay help information\n"); 3379 } else { 3380 printf("Format is 'set Name Value'\n'set ?' for help.\n"); 3381 } 3382 return 0; 3383 } 3384 3385 ct = getset(argv[1]); 3386 if (ct == 0) { 3387 fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n", 3388 argv[1]); 3389 return 0; 3390 } else if (ct == Ambiguous(struct setlist *)) { 3391 fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n", 3392 argv[1]); 3393 return 0; 3394 } else { 3395 if (strcmp("off", argv[2])) { 3396 value = special(argv[2]); 3397 } else { 3398 value = -1; 3399 } 3400 *(ct->charp) = value; 3401 printf("%s character is '%s'.\n", ct->name, control(*(ct->charp))); 3402 } 3403 return 1; 3404 } 3405 3406 /* 3407 * The following are the data structures and routines for the 3408 * 'mode' command. 3409 */ 3410 3411 static 3412 dolinemode() 3413 { 3414 if (hisopts[TELOPT_SGA]) { 3415 wontoption(TELOPT_SGA, 0); 3416 } 3417 if (hisopts[TELOPT_ECHO]) { 3418 wontoption(TELOPT_ECHO, 0); 3419 } 3420 return 1; 3421 } 3422 3423 static 3424 docharmode() 3425 { 3426 if (!hisopts[TELOPT_SGA]) { 3427 willoption(TELOPT_SGA, 0); 3428 } 3429 if (!hisopts[TELOPT_ECHO]) { 3430 willoption(TELOPT_ECHO, 0); 3431 } 3432 return 1; 3433 } 3434 3435 static struct cmd Modelist[] = { 3436 { "character", "character-at-a-time mode", docharmode, 1, 1 }, 3437 { "line", "line-by-line mode", dolinemode, 1, 1 }, 3438 { 0 }, 3439 }; 3440 3441 static char ** 3442 getnextmode(name) 3443 char *name; 3444 { 3445 struct cmd *c = (struct cmd *) name; 3446 3447 return (char **) (c+1); 3448 } 3449 3450 static struct cmd * 3451 getmodecmd(name) 3452 char *name; 3453 { 3454 return (struct cmd *) genget(name, (char **) Modelist, getnextmode); 3455 } 3456 3457 static 3458 modecmd(argc, argv) 3459 int argc; 3460 char *argv[]; 3461 { 3462 struct cmd *mt; 3463 3464 if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) { 3465 printf("format is: 'mode Mode', where 'Mode' is one of:\n\n"); 3466 for (mt = Modelist; mt->name; mt++) { 3467 printf("%s\t%s\n", mt->name, mt->help); 3468 } 3469 return 0; 3470 } 3471 mt = getmodecmd(argv[1]); 3472 if (mt == 0) { 3473 fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]); 3474 return 0; 3475 } else if (mt == Ambiguous(struct cmd *)) { 3476 fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]); 3477 return 0; 3478 } else { 3479 (*mt->handler)(); 3480 } 3481 return 1; 3482 } 3483 3484 /* 3485 * The following data structures and routines implement the 3486 * "display" command. 3487 */ 3488 3489 static 3490 display(argc, argv) 3491 int argc; 3492 char *argv[]; 3493 { 3494 #define dotog(tl) if (tl->variable && tl->actionexplanation) { \ 3495 if (*tl->variable) { \ 3496 printf("will"); \ 3497 } else { \ 3498 printf("won't"); \ 3499 } \ 3500 printf(" %s.\n", tl->actionexplanation); \ 3501 } 3502 3503 #define doset(sl) if (sl->name && *sl->name != ' ') { \ 3504 printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \ 3505 } 3506 3507 struct togglelist *tl; 3508 struct setlist *sl; 3509 3510 if (argc == 1) { 3511 for (tl = Togglelist; tl->name; tl++) { 3512 dotog(tl); 3513 } 3514 printf("\n"); 3515 for (sl = Setlist; sl->name; sl++) { 3516 doset(sl); 3517 } 3518 } else { 3519 int i; 3520 3521 for (i = 1; i < argc; i++) { 3522 sl = getset(argv[i]); 3523 tl = gettoggle(argv[i]); 3524 if ((sl == Ambiguous(struct setlist *)) || 3525 (tl == Ambiguous(struct togglelist *))) { 3526 printf("?Ambiguous argument '%s'.\n", argv[i]); 3527 return 0; 3528 } else if (!sl && !tl) { 3529 printf("?Unknown argument '%s'.\n", argv[i]); 3530 return 0; 3531 } else { 3532 if (tl) { 3533 dotog(tl); 3534 } 3535 if (sl) { 3536 doset(sl); 3537 } 3538 } 3539 } 3540 } 3541 return 1; 3542 #undef doset 3543 #undef dotog 3544 } 3545 3546 /* 3547 * The following are the data structures, and many of the routines, 3548 * relating to command processing. 3549 */ 3550 3551 /* 3552 * Set the escape character. 3553 */ 3554 static 3555 setescape(argc, argv) 3556 int argc; 3557 char *argv[]; 3558 { 3559 register char *arg; 3560 char buf[50]; 3561 3562 printf( 3563 "Deprecated usage - please use 'set escape%s%s' in the future.\n", 3564 (argc > 2)? " ":"", (argc > 2)? argv[1]: ""); 3565 if (argc > 2) 3566 arg = argv[1]; 3567 else { 3568 printf("new escape character: "); 3569 gets(buf); 3570 arg = buf; 3571 } 3572 if (arg[0] != '\0') 3573 escape = arg[0]; 3574 if (!In3270) { 3575 printf("Escape character is '%s'.\n", control(escape)); 3576 } 3577 fflush(stdout); 3578 return 1; 3579 } 3580 3581 /*VARARGS*/ 3582 static 3583 togcrmod() 3584 { 3585 crmod = !crmod; 3586 printf("Deprecated usage - please use 'toggle crmod' in the future.\n"); 3587 printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't"); 3588 fflush(stdout); 3589 return 1; 3590 } 3591 3592 /*VARARGS*/ 3593 suspend() 3594 { 3595 setcommandmode(); 3596 #if defined(unix) 3597 kill(0, SIGTSTP); 3598 #endif /* defined(unix) */ 3599 /* reget parameters in case they were changed */ 3600 TerminalSaveState(); 3601 setconnmode(); 3602 return 1; 3603 } 3604 3605 /*VARARGS*/ 3606 static 3607 bye(argc, argv) 3608 int argc; /* Number of arguments */ 3609 char *argv[]; /* arguments */ 3610 { 3611 if (connected) { 3612 shutdown(net, 2); 3613 printf("Connection closed.\n"); 3614 NetClose(net); 3615 connected = 0; 3616 /* reset options */ 3617 tninit(); 3618 #if defined(TN3270) 3619 SetIn3270(); /* Get out of 3270 mode */ 3620 #endif /* defined(TN3270) */ 3621 } 3622 if ((argc != 2) || (strcmp(argv[1], "fromquit") != 0)) { 3623 longjmp(toplevel, 1); 3624 /* NOTREACHED */ 3625 } 3626 return 1; /* Keep lint, etc., happy */ 3627 } 3628 3629 /*VARARGS*/ 3630 quit() 3631 { 3632 (void) call(bye, "bye", "fromquit", 0); 3633 Exit(0); 3634 /*NOTREACHED*/ 3635 return 1; /* just to keep lint happy */ 3636 } 3637 3638 /* 3639 * Print status about the connection. 3640 */ 3641 static 3642 status(argc, argv) 3643 int argc; 3644 char *argv[]; 3645 { 3646 if (connected) { 3647 printf("Connected to %s.\n", hostname); 3648 if (argc < 2) { 3649 printf("Operating in %s.\n", 3650 modelist[getconnmode()].modedescriptions); 3651 if (localchars) { 3652 printf("Catching signals locally.\n"); 3653 } 3654 } 3655 } else { 3656 printf("No connection.\n"); 3657 } 3658 # if !defined(TN3270) 3659 printf("Escape character is '%s'.\n", control(escape)); 3660 fflush(stdout); 3661 # else /* !defined(TN3270) */ 3662 if ((!In3270) && ((argc < 2) || strcmp(argv[1], "notmuch"))) { 3663 printf("Escape character is '%s'.\n", control(escape)); 3664 } 3665 # if defined(unix) 3666 if (In3270 && transcom) { 3667 printf("Transparent mode command is '%s'.\n", transcom); 3668 } 3669 # endif /* defined(unix) */ 3670 fflush(stdout); 3671 if (In3270) { 3672 return 0; 3673 } 3674 # endif /* defined(TN3270) */ 3675 return 1; 3676 } 3677 3678 #if defined(TN3270) && defined(unix) 3679 static 3680 settranscom(argc, argv) 3681 int argc; 3682 char *argv[]; 3683 { 3684 int i, len = 0; 3685 char *strcpy(), *strcat(); 3686 3687 if (argc == 1 && transcom) { 3688 transcom = 0; 3689 } 3690 if (argc == 1) { 3691 return; 3692 } 3693 for (i = 1; i < argc; ++i) { 3694 len += 1 + strlen(argv[1]); 3695 } 3696 transcom = tline; 3697 (void) strcpy(transcom, argv[1]); 3698 for (i = 2; i < argc; ++i) { 3699 (void) strcat(transcom, " "); 3700 (void) strcat(transcom, argv[i]); 3701 } 3702 } 3703 #endif /* defined(TN3270) && defined(unix) */ 3704 3705 3706 3707 static 3708 tn(argc, argv) 3709 int argc; 3710 char *argv[]; 3711 { 3712 register struct hostent *host = 0; 3713 #if defined(MSDOS) 3714 char *cp; 3715 #endif /* defined(MSDOS) */ 3716 3717 if (connected) { 3718 printf("?Already connected to %s\n", hostname); 3719 return 0; 3720 } 3721 if (argc < 2) { 3722 (void) strcpy(line, "Connect "); 3723 printf("(to) "); 3724 gets(&line[strlen(line)]); 3725 makeargv(); 3726 argc = margc; 3727 argv = margv; 3728 } 3729 if ((argc < 2) || (argc > 3)) { 3730 printf("usage: %s host-name [port]\n", argv[0]); 3731 return 0; 3732 } 3733 #if defined(MSDOS) 3734 for (cp = argv[1]; *cp; cp++) { 3735 if (isupper(*cp)) { 3736 *cp = tolower(*cp); 3737 } 3738 } 3739 #endif /* defined(MSDOS) */ 3740 sin.sin_addr.s_addr = inet_addr(argv[1]); 3741 if (sin.sin_addr.s_addr != -1) { 3742 sin.sin_family = AF_INET; 3743 (void) strcpy(hnamebuf, argv[1]); 3744 hostname = hnamebuf; 3745 } else { 3746 host = gethostbyname(argv[1]); 3747 if (host) { 3748 sin.sin_family = host->h_addrtype; 3749 #if defined(h_addr) /* In 4.3, this is a #define */ 3750 memcpy((caddr_t)&sin.sin_addr, 3751 host->h_addr_list[0], host->h_length); 3752 #else /* defined(h_addr) */ 3753 memcpy((caddr_t)&sin.sin_addr, host->h_addr, host->h_length); 3754 #endif /* defined(h_addr) */ 3755 hostname = host->h_name; 3756 } else { 3757 printf("%s: unknown host\n", argv[1]); 3758 return 0; 3759 } 3760 } 3761 sin.sin_port = sp->s_port; 3762 if (argc == 3) { 3763 sin.sin_port = atoi(argv[2]); 3764 if (sin.sin_port == 0) { 3765 sp = getservbyname(argv[2], "tcp"); 3766 if (sp) 3767 sin.sin_port = sp->s_port; 3768 else { 3769 printf("%s: bad port number\n", argv[2]); 3770 return 0; 3771 } 3772 } else { 3773 sin.sin_port = atoi(argv[2]); 3774 sin.sin_port = htons(sin.sin_port); 3775 } 3776 telnetport = 0; 3777 } else { 3778 telnetport = 1; 3779 } 3780 #if defined(unix) 3781 signal(SIGINT, intr); 3782 signal(SIGQUIT, intr2); 3783 signal(SIGPIPE, deadpeer); 3784 #endif /* defined(unix) */ 3785 printf("Trying...\n"); 3786 do { 3787 net = socket(AF_INET, SOCK_STREAM, 0); 3788 if (net < 0) { 3789 perror("telnet: socket"); 3790 return 0; 3791 } 3792 if (debug && SetSockOpt(net, SOL_SOCKET, SO_DEBUG, 1) < 0) { 3793 perror("setsockopt (SO_DEBUG)"); 3794 } 3795 3796 if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) { 3797 #if defined(h_addr) /* In 4.3, this is a #define */ 3798 if (host && host->h_addr_list[1]) { 3799 int oerrno = errno; 3800 3801 fprintf(stderr, "telnet: connect to address %s: ", 3802 inet_ntoa(sin.sin_addr)); 3803 errno = oerrno; 3804 perror((char *)0); 3805 host->h_addr_list++; 3806 memcpy((caddr_t)&sin.sin_addr, 3807 host->h_addr_list[0], host->h_length); 3808 fprintf(stderr, "Trying %s...\n", 3809 inet_ntoa(sin.sin_addr)); 3810 (void) NetClose(net); 3811 continue; 3812 } 3813 #endif /* defined(h_addr) */ 3814 perror("telnet: Unable to connect to remote host"); 3815 #if defined(unix) 3816 signal(SIGINT, SIG_DFL); 3817 signal(SIGQUIT, SIG_DFL); 3818 #endif /* defined(unix) */ 3819 return 0; 3820 } 3821 connected++; 3822 } while (connected == 0); 3823 call(status, "status", "notmuch", 0); 3824 if (setjmp(peerdied) == 0) 3825 telnet(); 3826 NetClose(net); 3827 ExitString(stderr, "Connection closed by foreign host.\n",1); 3828 /*NOTREACHED*/ 3829 } 3830 3831 3832 #define HELPINDENT (sizeof ("connect")) 3833 3834 static char 3835 openhelp[] = "connect to a site", 3836 closehelp[] = "close current connection", 3837 quithelp[] = "exit telnet", 3838 statushelp[] = "print status information", 3839 helphelp[] = "print help information", 3840 sendhelp[] = "transmit special characters ('send ?' for more)", 3841 sethelp[] = "set operating parameters ('set ?' for more)", 3842 togglestring[] ="toggle operating parameters ('toggle ?' for more)", 3843 displayhelp[] = "display operating parameters", 3844 #if defined(TN3270) && defined(unix) 3845 transcomhelp[] = "specify Unix command for transparent mode pipe", 3846 #endif /* defined(TN3270) && defined(unix) */ 3847 #if defined(unix) 3848 zhelp[] = "suspend telnet", 3849 #endif /* defined(unix */ 3850 #if defined(TN3270) 3851 shellhelp[] = "invoke a subshell", 3852 #endif /* defined(TN3270) */ 3853 modehelp[] = "try to enter line-by-line or character-at-a-time mode"; 3854 3855 extern int help(), shell(); 3856 3857 static struct cmd cmdtab[] = { 3858 { "close", closehelp, bye, 1, 1 }, 3859 { "display", displayhelp, display, 1, 0 }, 3860 { "mode", modehelp, modecmd, 1, 1 }, 3861 { "open", openhelp, tn, 1, 0 }, 3862 { "quit", quithelp, quit, 1, 0 }, 3863 { "send", sendhelp, sendcmd, 1, 1 }, 3864 { "set", sethelp, setcmd, 1, 0 }, 3865 { "status", statushelp, status, 1, 0 }, 3866 { "toggle", togglestring, toggle, 1, 0 }, 3867 #if defined(TN3270) && defined(unix) 3868 { "transcom", transcomhelp, settranscom, 1, 0 }, 3869 #endif /* defined(TN3270) && defined(unix) */ 3870 #if defined(unix) 3871 { "z", zhelp, suspend, 1, 0 }, 3872 #endif /* defined(unix) */ 3873 #if defined(TN3270) 3874 { "!", shellhelp, shell, 1, 1 }, 3875 #endif /* defined(TN3270) */ 3876 { "?", helphelp, help, 1, 0 }, 3877 0 3878 }; 3879 3880 static char crmodhelp[] = "deprecated command -- use 'toggle crmod' instead"; 3881 static char escapehelp[] = "deprecated command -- use 'set escape' instead"; 3882 3883 static struct cmd cmdtab2[] = { 3884 { "help", helphelp, help, 0, 0 }, 3885 { "escape", escapehelp, setescape, 1, 0 }, 3886 { "crmod", crmodhelp, togcrmod, 1, 0 }, 3887 0 3888 }; 3889 3890 /* 3891 * Call routine with argc, argv set from args (terminated by 0). 3892 * VARARGS2 3893 */ 3894 static 3895 call(routine, args) 3896 int (*routine)(); 3897 char *args; 3898 { 3899 register char **argp; 3900 register int argc; 3901 3902 for (argc = 0, argp = &args; *argp++ != 0; argc++) 3903 ; 3904 return (*routine)(argc, &args); 3905 } 3906 3907 static char ** 3908 getnextcmd(name) 3909 char *name; 3910 { 3911 struct cmd *c = (struct cmd *) name; 3912 3913 return (char **) (c+1); 3914 } 3915 3916 static struct cmd * 3917 getcmd(name) 3918 char *name; 3919 { 3920 struct cmd *cm; 3921 3922 if ((cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) != 0) { 3923 return cm; 3924 } else { 3925 return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd); 3926 } 3927 } 3928 3929 void 3930 command(top) 3931 int top; 3932 { 3933 register struct cmd *c; 3934 3935 setcommandmode(); 3936 if (!top) { 3937 putchar('\n'); 3938 } else { 3939 #if defined(unix) 3940 signal(SIGINT, SIG_DFL); 3941 signal(SIGQUIT, SIG_DFL); 3942 #endif /* defined(unix) */ 3943 } 3944 for (;;) { 3945 printf("%s> ", prompt); 3946 if (gets(line) == NULL) { 3947 if (feof(stdin) || ferror(stdin)) 3948 quit(); 3949 break; 3950 } 3951 if (line[0] == 0) 3952 break; 3953 makeargv(); 3954 c = getcmd(margv[0]); 3955 if (c == Ambiguous(struct cmd *)) { 3956 printf("?Ambiguous command\n"); 3957 continue; 3958 } 3959 if (c == 0) { 3960 printf("?Invalid command\n"); 3961 continue; 3962 } 3963 if (c->needconnect && !connected) { 3964 printf("?Need to be connected first.\n"); 3965 continue; 3966 } 3967 if ((*c->handler)(margc, margv)) { 3968 break; 3969 } 3970 } 3971 if (!top) { 3972 if (!connected) { 3973 longjmp(toplevel, 1); 3974 /*NOTREACHED*/ 3975 } 3976 #if defined(TN3270) 3977 if (shell_active == 0) { 3978 setconnmode(); 3979 } 3980 #else /* defined(TN3270) */ 3981 setconnmode(); 3982 #endif /* defined(TN3270) */ 3983 } 3984 } 3985 3986 /* 3987 * Help command. 3988 */ 3989 static 3990 help(argc, argv) 3991 int argc; 3992 char *argv[]; 3993 { 3994 register struct cmd *c; 3995 3996 if (argc == 1) { 3997 printf("Commands may be abbreviated. Commands are:\n\n"); 3998 for (c = cmdtab; c->name; c++) 3999 if (c->dohelp) { 4000 printf("%-*s\t%s\n", HELPINDENT, c->name, 4001 c->help); 4002 } 4003 return 0; 4004 } 4005 while (--argc > 0) { 4006 register char *arg; 4007 arg = *++argv; 4008 c = getcmd(arg); 4009 if (c == Ambiguous(struct cmd *)) 4010 printf("?Ambiguous help command %s\n", arg); 4011 else if (c == (struct cmd *)0) 4012 printf("?Invalid help command %s\n", arg); 4013 else 4014 printf("%s\n", c->help); 4015 } 4016 return 0; 4017 } 4018 4019 /* 4020 * main. Parse arguments, invoke the protocol or command parser. 4021 */ 4022 4023 4024 void 4025 main(argc, argv) 4026 int argc; 4027 char *argv[]; 4028 { 4029 tninit(); /* Clear out things */ 4030 4031 NetTrace = stdout; 4032 TerminalSaveState(); 4033 autoflush = TerminalAutoFlush(); 4034 4035 prompt = argv[0]; 4036 while ((argc > 1) && (argv[1][0] == '-')) { 4037 if (!strcmp(argv[1], "-d")) { 4038 debug = 1; 4039 } else if (!strcmp(argv[1], "-n")) { 4040 if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */ 4041 NetTrace = fopen(argv[2], "w"); 4042 argv++; 4043 argc--; 4044 if (NetTrace == NULL) { 4045 NetTrace = stdout; 4046 } 4047 } 4048 } else { 4049 #if defined(TN3270) && defined(unix) 4050 if (!strcmp(argv[1], "-t")) { 4051 if ((argc > 1) && (argv[2][0] != '-')) { /* get file name */ 4052 transcom = tline; 4053 (void) strcpy(transcom, argv[1]); 4054 argv++; 4055 argc--; 4056 } 4057 } else if (!strcmp(argv[1], "-noasynch")) { 4058 noasynch = 1; 4059 } else 4060 #endif /* defined(TN3270) && defined(unix) */ 4061 if (argv[1][1] != '\0') { 4062 fprintf(stderr, "Unknown option *%s*.\n", argv[1]); 4063 } 4064 } 4065 argc--; 4066 argv++; 4067 } 4068 if (argc != 1) { 4069 if (setjmp(toplevel) != 0) 4070 Exit(0); 4071 tn(argc, argv); 4072 } 4073 setjmp(toplevel); 4074 for (;;) { 4075 #if !defined(TN3270) 4076 command(1); 4077 #else /* !defined(TN3270) */ 4078 if (!shell_active) { 4079 command(1); 4080 } else { 4081 #if defined(TN3270) 4082 shell_continue(); 4083 #endif /* defined(TN3270) */ 4084 } 4085 #endif /* !defined(TN3270) */ 4086 } 4087 } 4088