1: /*
   2:  *	Copyright 1984, 1985 by the Regents of the University of
   3:  *	California and by Gregory Glenn Minshall.
   4:  *
   5:  *	Permission to use, copy, modify, and distribute these
   6:  *	programs and their documentation for any purpose and
   7:  *	without fee is hereby granted, provided that this
   8:  *	copyright and permission appear on all copies and
   9:  *	supporting documentation, the name of the Regents of
  10:  *	the University of California not be used in advertising
  11:  *	or publicity pertaining to distribution of the programs
  12:  *	without specific prior permission, and notice be given in
  13:  *	supporting documentation that copying and distribution is
  14:  *	by permission of the Regents of the University of California
  15:  *	and by Gregory Glenn Minshall.  Neither the Regents of the
  16:  *	University of California nor Gregory Glenn Minshall make
  17:  *	representations about the suitability of this software
  18:  *	for any purpose.  It is provided "as is" without
  19:  *	express or implied warranty.
  20:  */
  21: 
  22: 
  23: #ifndef lint
  24: static char sccsid[] = "@(#)tn3270.c	2.7\t5/13/86";
  25: #endif
  26: 
  27: /*
  28:  * User telnet program, specially modified for tn3270.
  29:  */
  30: #include <sys/types.h>
  31: #include <sys/socket.h>
  32: #include <sys/ioctl.h>
  33: #include <sys/time.h>
  34: 
  35: #include <netinet/in.h>
  36: 
  37: #define TELOPTS
  38: #include <arpa/telnet.h>
  39: 
  40: #include <stdio.h>
  41: #include <ctype.h>
  42: #include <errno.h>
  43: #include <signal.h>
  44: #include <setjmp.h>
  45: #include <netdb.h>
  46: 
  47: #define strip(x)    ((x)&0177)
  48: #define min(x,y)    ((x<y)? x:y)
  49: 
  50: static char Ibuf[8*BUFSIZ], *Ifrontp = Ibuf, *Ibackp = Ibuf;
  51: static char ttyobuf[BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
  52: static char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
  53: 
  54: static char SbBuffer[100], *pSb = SbBuffer;
  55: #define Sb_Option SbBuffer[0]
  56: #define Sb_Command SbBuffer[1]
  57: 
  58: 
  59: static char hisopts[256];
  60: static char myopts[256];
  61: 
  62: static char doopt[] = { IAC, DO, '%', 'c', 0 };
  63: static char dont[] = { IAC, DONT, '%', 'c', 0 };
  64: static char will[] = { IAC, WILL, '%', 'c', 0 };
  65: static char wont[] = { IAC, WONT, '%', 'c', 0 };
  66: static char sb_terminal[] = { IAC, SB,
  67:             TELOPT_TTYPE, TELQUAL_IS,
  68:             'I', 'B', 'M', '-', '3', '2', '7', '7', '-', '2',
  69:             IAC, SE };
  70: 
  71: /* The following is a real, live, global. */
  72: 
  73: /* The point of HaveInput is to give a hint to the terminal output processor
  74:  * that some input from some source (network or terminal) has come in.
  75:  */
  76: 
  77: int HaveInput = 1;  /* we have received input in the not too distant past */
  78: 
  79: 
  80: static int  connected;
  81: static int  SentTerminalType = 0;       /* returned sb_terminal to other? */
  82: static int  In3270 = 0;         /* we are in 3270 binary mode */
  83: static int  ISend = 0;          /* trying to send network data in */
  84: static int  ForceMode = -1;         /* for debugging */
  85: static int  net;
  86: static int  showoptions = 0;
  87: static int  debug = 0;
  88: static int  crmod = 0;
  89: static int  printnet = 0;
  90: static FILE *NetTrace;
  91: static char *prompt;
  92: static char escape = CTRL(]);
  93: 
  94: static char line[200];
  95: static int  margc;
  96: static char *margv[20];
  97: 
  98: static jmp_buf  toplevel;
  99: static jmp_buf  peerdied;
 100: 
 101: extern  int errno;
 102: 
 103: int quit(), suspend();
 104: static int  tn(), bye(), help();
 105: static int  setescape(), status(), toggle(), setoptions();
 106: static int  setcrmod(), setdebug(), SetPrintNet();
 107: 
 108: #define HELPINDENT (sizeof ("connect"))
 109: 
 110: struct cmd {
 111:     char    *name;      /* command name */
 112:     char    *help;      /* help string */
 113:     int (*handler)();   /* routine which executes command */
 114:     int dohelp;     /* Should we give general help information? */
 115: };
 116: 
 117: static char openhelp[] =    "connect to a site";
 118: static char closehelp[] =   "close current connection";
 119: static char quithelp[] =    "exit telnet";
 120: static char zhelp[] =   "suspend telnet";
 121: static char debughelp[] =   "toggle debugging";
 122: static char escapehelp[] =  "set escape character";
 123: static char statushelp[] =  "print status information";
 124: static char helphelp[] =    "print help information";
 125: static char optionshelp[] = "toggle viewing of options processing";
 126: static char crmodhelp[] =   "toggle mapping of received carriage returns";
 127: static char printnethelp[] = "print out raw data to/from net";
 128: 
 129: static struct cmd cmdtab[] = {
 130:     { "open",   openhelp,   tn, 1 },
 131:     { "close",  closehelp,  bye, 1 },
 132:     { "quit",   quithelp,   quit, 1 },
 133:     { "z",      zhelp,      suspend, 1 },
 134:     { "suspend",    zhelp,      suspend, 0 },
 135:     { "escape", escapehelp, setescape, 0 },
 136:     { "status", statushelp, status, 1 },
 137:     { "options",    optionshelp,    setoptions, 0 },
 138:     { "crmod",  crmodhelp,  setcrmod, 0 },
 139:     { "debug",  debughelp,  setdebug, 0 },
 140:     { "printnet",   printnethelp,   SetPrintNet, 0 },
 141:     { "?",      helphelp,   help, 1 },
 142:     { "help",   helphelp,   help, 0 },
 143:     0
 144: };
 145: 
 146: static struct sockaddr_in sin;
 147: 
 148: static int  intr(), deadpeer(), inputAvailable();
 149: static char *control();
 150: static struct   cmd *getcmd();
 151: static struct   servent *sp;
 152: 
 153: static struct   tchars otc;
 154: static struct   ltchars oltc;
 155: static struct   sgttyb ottyb;
 156: 
 157: main(argc, argv)
 158:     int argc;
 159:     char *argv[];
 160: {
 161:     ioctl(0, TIOCGETP, (char *)&ottyb);
 162:     ioctl(0, TIOCGETC, (char *)&otc);
 163:     ioctl(0, TIOCGLTC, (char *)&oltc);
 164:     sp = getservbyname("telnet", "tcp");
 165:     if (sp == 0) {
 166:         ExitString(stderr, "telnet: tcp/telnet: unknown service\n", 1);
 167:     }
 168:     NetTrace = stdout;
 169:     prompt = argv[0];
 170:     if (argc > 1 && !strcmp(argv[1], "-d")) {
 171:         debug = SO_DEBUG, argv++, argc--;
 172:     }
 173:     if (argc > 1 && !strcmp(argv[1], "-n")) {
 174:         argv++;
 175:         argc--;
 176:         if (argc > 1) {     /* get file name */
 177:         NetTrace = fopen(argv[1], "w");
 178:         argv++;
 179:         argc--;
 180:         if (NetTrace == NULL) {
 181:             NetTrace = stdout;
 182:         }
 183:         }
 184:     }
 185:     if (argc != 1) {
 186:         if (setjmp(toplevel) != 0)
 187:             Exit(0);
 188:         tn(argc, argv);
 189:     }
 190:     setjmp(toplevel);
 191:     for (;;)
 192:         command(1);
 193: }
 194: 
 195: static char *hostname;
 196: static char hnamebuf[32];
 197: 
 198: static
 199: tn(argc, argv)
 200:     int argc;
 201:     char *argv[];
 202: {
 203:     register struct hostent *host;
 204:     char *strcpy();
 205: 
 206:     if (connected) {
 207:         printf("?Already connected to %s\n", hostname);
 208:         return;
 209:     }
 210:     if (argc < 2) {
 211:         (void) strcpy(line, "Connect ");
 212:         printf("(to) ");
 213:         gets(&line[strlen(line)]);
 214:         makeargv();
 215:         argc = margc;
 216:         argv = margv;
 217:     }
 218:     if (argc > 3) {
 219:         printf("usage: %s host-name [port]\n", argv[0]);
 220:         return;
 221:     }
 222:     host = gethostbyname(argv[1]);
 223:     if (host) {
 224:         sin.sin_family = host->h_addrtype;
 225:         bcopy(host->h_addr, (caddr_t)&sin.sin_addr, host->h_length);
 226:         hostname = host->h_name;
 227:     } else {
 228:         sin.sin_family = AF_INET;
 229:         sin.sin_addr.s_addr = inet_addr(argv[1]);
 230:         if (sin.sin_addr.s_addr == -1) {
 231:             printf("%s: unknown host\n", argv[1]);
 232:             return;
 233:         }
 234:         (void) strcpy(hnamebuf, argv[1]);
 235:         hostname = hnamebuf;
 236:     }
 237:     sin.sin_port = sp->s_port;
 238:     if (argc == 3) {
 239:         sin.sin_port = atoi(argv[2]);
 240:         sin.sin_port = htons(sin.sin_port);
 241:     }
 242:     net = socket(AF_INET, SOCK_STREAM, 0);
 243:     if (net < 0) {
 244:         perror("telnet: socket");
 245:         return;
 246:     }
 247:     if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
 248:         perror("setsockopt (SO_DEBUG)");
 249:     signal(SIGINT, intr);
 250:     signal(SIGPIPE, deadpeer);
 251:     printf("Trying...\n");
 252:     if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
 253:         perror("telnet: connect");
 254:         signal(SIGINT, SIG_DFL);
 255:         return;
 256:     }
 257:     connected++;
 258:     call(status, "status", 0);
 259:     if (setjmp(peerdied) == 0)
 260:         telnet();
 261:     if (In3270) {
 262:         Stop3270(1);
 263:     }
 264:     ExitString(stderr, "Connection closed by foreign host.\n", 1);
 265: }
 266: 
 267: /*
 268:  * Print status about the connection.
 269:  */
 270: /*VARARGS*/
 271: static
 272: status()
 273: {
 274:     if (connected)
 275:         printf("Connected to %s.\n", hostname);
 276:     else
 277:         printf("No connection.\n");
 278:     /*printf("Escape character is '%s'.\n", control(escape));*/
 279:     fflush(stdout);
 280: }
 281: 
 282: static
 283: makeargv()
 284: {
 285:     register char *cp;
 286:     register char **argp = margv;
 287: 
 288:     margc = 0;
 289:     for (cp = line; *cp;) {
 290:         while (isspace(*cp))
 291:             cp++;
 292:         if (*cp == '\0')
 293:             break;
 294:         *argp++ = cp;
 295:         margc += 1;
 296:         while (*cp != '\0' && !isspace(*cp))
 297:             cp++;
 298:         if (*cp == '\0')
 299:             break;
 300:         *cp++ = '\0';
 301:     }
 302:     *argp++ = 0;
 303: }
 304: 
 305: /*VARARGS*/
 306: suspend()
 307: {
 308:     register int save;
 309: 
 310:     save = mode(0);
 311:     kill(0, SIGTSTP);
 312:     /* reget parameters in case they were changed */
 313:     ioctl(0, TIOCGETP, (char *)&ottyb);
 314:     ioctl(0, TIOCGETC, (char *)&otc);
 315:     ioctl(0, TIOCGLTC, (char *)&oltc);
 316:     (void) mode(save);
 317: }
 318: 
 319: /*VARARGS*/
 320: static
 321: bye()
 322: {
 323:     register char *op;
 324: 
 325:     (void) mode(0);
 326:     if (connected) {
 327:         shutdown(net, 2);
 328:         printf("Connection closed.\n");
 329:         close(net);
 330:         connected = 0;
 331:         /* reset his options */
 332:         for (op = hisopts; op < &hisopts[256]; op++)
 333:             *op = 0;
 334:     }
 335: }
 336: 
 337: /*VARARGS*/
 338: quit()
 339: {
 340:     call(bye, "bye", 0);
 341:     Exit(0);
 342: }
 343: 
 344: /*
 345:  * Help command.
 346:  */
 347: static
 348: help(argc, argv)
 349:     int argc;
 350:     char *argv[];
 351: {
 352:     register struct cmd *c;
 353: 
 354:     if (argc == 1) {
 355:         printf("Commands may be abbreviated.  Commands are:\n\n");
 356:         for (c = cmdtab; c->name; c++)
 357:             if (c->dohelp) {
 358:                 printf("%-*s\t%s\n", HELPINDENT, c->name, c->help);
 359:             }
 360:         return;
 361:     }
 362:     while (--argc > 0) {
 363:         register char *arg;
 364:         arg = *++argv;
 365:         c = getcmd(arg);
 366:         if (c == (struct cmd *)-1)
 367:             printf("?Ambiguous help command %s\n", arg);
 368:         else if (c == (struct cmd *)0)
 369:             printf("?Invalid help command %s\n", arg);
 370:         else
 371:             printf("%s\n", c->help);
 372:     }
 373: }
 374: 
 375: /*
 376:  * Call routine with argc, argv set from args (terminated by 0).
 377:  * VARARGS2
 378:  */
 379: static
 380: call(routine, args)
 381:     int (*routine)();
 382:     int args;
 383: {
 384:     register int *argp;
 385:     register int argc;
 386: 
 387:     for (argc = 0, argp = &args; *argp++ != 0; argc++)
 388:         ;
 389:     (*routine)(argc, &args);
 390: }
 391: 
 392: static struct   tchars notc =   { -1, -1, -1, -1, -1, -1 };
 393: static struct   ltchars noltc = { -1, -1, -1, -1, -1, -1 };
 394: 
 395: mode(f)
 396:     register int f;
 397: {
 398:     static int prevmode = 0;
 399:     struct tchars *tc, tc3;
 400:     struct ltchars *ltc;
 401:     struct sgttyb sb;
 402:     int onoff, old;
 403: 
 404:     if (prevmode == f)
 405:         return (f);
 406:     old = prevmode;
 407:     prevmode = f;
 408:     sb = ottyb;
 409:     if (ForceMode != -1) {
 410:         f = ForceMode;
 411:         ForceMode = -1;
 412:     }
 413:     switch (f) {
 414: 
 415:     case 0:
 416:         onoff = 0;
 417:         tc = &otc;
 418:         ltc = &oltc;
 419:         break;
 420: 
 421:     case 1:     /* the rawest */
 422:     case 2:     /* allows for local echoing, newline mapping */
 423:     case 3:     /* like 1, but with XON/XOFF */
 424: 
 425:         sb.sg_flags |= CBREAK;
 426:         if ((f == 1) || (f == 3)) {
 427:             sb.sg_flags &= ~(ECHO|CRMOD);
 428:         } else {
 429:             sb.sg_flags |= ECHO|CRMOD;
 430:         }
 431:         sb.sg_erase = sb.sg_kill = -1;
 432:         if (f == 3) {
 433:             tc = &tc3;
 434:             tc3 = notc;
 435:             /* get XON, XOFF characters */
 436:             tc3.t_startc = otc.t_startc;
 437:             tc3.t_stopc = otc.t_stopc;
 438:         } else {
 439:             tc = &notc;
 440:         }
 441:         ltc = &noltc;
 442:         onoff = 1;
 443:         break;
 444: 
 445:     default:
 446:         return(old);
 447:     }
 448:     ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
 449:     ioctl(fileno(stdin), TIOCSETC, (char *)tc);
 450:     ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
 451:     ioctl(fileno(stdin), FIONBIO, (char *)&onoff);
 452:     ioctl(fileno(stdout), FIONBIO, (char *)&onoff);
 453:     ioctl(fileno(stdin), FIOASYNC, (char *)&onoff);
 454:     return (old);
 455: }
 456: 
 457: static char sibuf[BUFSIZ], *sbp;
 458: static char tibuf[BUFSIZ], *tbp;
 459: static int  scc, tcc;
 460: static int tin, tout;           /* file descriptors */
 461: 
 462: /*
 463:  * Select from tty and network...
 464:  */
 465: static
 466: telnet()
 467: {
 468:     int on = 1;
 469:     int negativePid = -getpid();
 470:     int schedValue;
 471: 
 472:     (void) mode(2);
 473:     ioctl(net, FIONBIO, (char *)&on);
 474:     ioctl(net, FIOASYNC, (char *)&on);  /* hear about input */
 475:     ioctl(net, SIOCSPGRP, (char *)&negativePid);    /* set my pid */
 476:     tin = fileno(stdin);
 477:     tout = fileno(stdout);
 478: 
 479:     for (;;) {
 480:     while (schedValue = Scheduler(0)) {
 481:         if (schedValue == -1) {
 482:         (void) mode(0);
 483:         return;
 484:         }
 485:     }
 486:         /* If there is data waiting to go out to terminal, don't
 487: 		 * schedule any more data for the terminal.
 488: 		 */
 489:     if (tfrontp-tbackp) {
 490:         schedValue = 1;
 491:     } else {
 492:         schedValue = DoTerminalOutput();
 493:     }
 494:     if (schedValue) {
 495:         if (Scheduler(1) == -1) {
 496:         (void) mode(0);
 497:         return;
 498:         }
 499:     }
 500:     }
 501: }
 502: 
 503: 
 504: /* Loop around once. */
 505: 
 506: static
 507: Scheduler(block)
 508: int block;      /* should we block in the select? */
 509: {
 510:     register int c;
 511:     int ibits = 0, obits = 0;
 512:         /* One wants to be a bit careful about setting returnValue
 513: 		 * to one, since a one implies we did some useful work,
 514: 		 * and therefore probably won't be called to block next
 515: 		 * time.
 516: 		 */
 517:     int returnValue = 0;
 518:     static struct timeval TimeValue = {0};
 519: 
 520:     if (!In3270) {
 521:     if (nfrontp - nbackp)
 522:         obits |= (1 << net);
 523:     else if (tcc == 0) {
 524:         ibits |= (1 << tin);
 525:     }
 526:     if (tfrontp - tbackp)
 527:         obits |= (1 << tout);
 528:     else if (!ISend)
 529:         ibits |= (1 << net);
 530:     } else {
 531:     if (nfrontp - nbackp) { /* something for network? */
 532:         obits |= 1<<net;        /* yes - wait for space */
 533:     }
 534:     if (tcc == 0) {     /* any pending tty input? */
 535:         ibits |= 1<<tin;    /* no, look for new input */
 536:     }
 537:     if (tfrontp-tbackp) {   /* any pending tty output? */
 538:         obits |= 1<<tout;   /* yes - wait for space */
 539:     }
 540:     if (!ISend) {       /* any pending net input? */
 541:         ibits |= 1<<net;        /* no, look for new input */
 542:     }
 543:     }
 544:     if (scc < 0 && tcc < 0) {
 545:         return(-1);
 546:     }
 547:     if (HaveInput) {        /* Reprime SIGIO handler if appropriate */
 548:     HaveInput = 0;
 549:     signal(SIGIO, inputAvailable);
 550:     }
 551:     select(16, &ibits, &obits, (int *) 0,
 552:                 (block)? (struct timeval *)0:&TimeValue);
 553:     if (ibits == 0 && obits == 0 && block) {
 554:             /* I don't like this, does it ever happen? */
 555:         printf("sleep(5) from tn3270, after select\n");
 556:         sleep(5);
 557:         return(0);
 558:     }
 559: 
 560:     /*
 561:      * Something to read from the network...
 562:      */
 563:     if (ibits & (1 << net)) {
 564:         scc = read(net, sibuf, sizeof (sibuf));
 565:         if (scc < 0 && errno == EWOULDBLOCK)
 566:             scc = 0;
 567:         else {
 568:             if (scc <= 0)
 569:                 return(-1);
 570:             sbp = sibuf;
 571:             if (printnet) {
 572:                 Dump('<', sbp, scc);
 573:             }
 574:             returnValue = 1;        /* did something usefull */
 575:         }
 576:     }
 577: 
 578:     /*
 579:      * Something to read from the tty...
 580:      */
 581:     if (ibits & (1 << tin)) {
 582:         tcc = read(tin, tibuf, sizeof tibuf);
 583:         if (tcc < 0 && errno == EWOULDBLOCK)
 584:             tcc = 0;
 585:         else {
 586:             if (tcc <= 0)
 587:                 return(-1);
 588:             tbp = tibuf;
 589:             returnValue = 1;        /* did something usefull */
 590:         }
 591:     }
 592: 
 593:     if (tcc > 0) {
 594:     if (In3270) {
 595:         c = DataFromTerminal(tbp, tcc);
 596:         if (c) {
 597:         returnValue = 1;        /* did something usefull */
 598:         }
 599:         tcc -= c;
 600:         tbp += c;
 601:     } else {
 602:         returnValue = 1;        /* did something usefull */
 603:         while (tcc > 0) {
 604:         if ((&netobuf[BUFSIZ] - nfrontp) < 2)
 605:             break;
 606:         c = *tbp++ & 0377, tcc--;
 607:         if (strip(c) == escape) {
 608:             command(0);
 609:             tcc = 0;
 610:             break;
 611:         }
 612:         if (c == IAC)
 613:             *nfrontp++ = c;
 614:         *nfrontp++ = c;
 615:         }
 616:     }
 617:     }
 618:     if ((obits & (1 << net)) && (c = (int) (nfrontp - nbackp)) > 0) {
 619:     netflush();
 620:     if (c != (int) (nfrontp-nbackp)) {
 621:         returnValue = 1;
 622:     }
 623:     }
 624:     if (scc > 0) {
 625:     if (Ifrontp+scc >= Ibuf+sizeof Ibuf) {
 626:         if (Ibackp != Ibuf) {   /* do some copying */
 627:         bcopy(Ibackp, Ibuf, Ifrontp-Ibackp);
 628:         Ifrontp -= (Ibackp-Ibuf);
 629:         Ibackp = Ibuf;
 630:         }
 631:     }
 632:     if (Ifrontp+scc < Ibuf+sizeof Ibuf) {
 633:         returnValue = 1;        /* doing something useful */
 634:         telrcv();
 635:     }       /* Else - we may never recover */
 636:     }
 637:     if ((obits & (1 << tout)) && (c = (int) (tfrontp - tbackp)) > 0) {
 638:     ttyflush();
 639:     if (c != (int) (tfrontp-tbackp)) {
 640:         returnValue = 1;
 641:     }
 642:     }
 643:     if (In3270 && (c = (int) (Ifrontp-Ibackp))) {
 644:         Ibackp += DataFromNetwork(Ibackp, Ifrontp-Ibackp, ISend);
 645:         if (c != (int) (Ifrontp-Ibackp)) {
 646:         returnValue = 1;
 647:         }
 648:         if (Ibackp == Ifrontp) {
 649:             Ibackp = Ifrontp = Ibuf;
 650:             ISend = 0;  /* take data from network */
 651:         }
 652:     }
 653:     return(returnValue);                /* good return */
 654: }
 655: 
 656: command(top)
 657:     int top;
 658: {
 659:     register struct cmd *c;
 660:     int oldmode;
 661: 
 662:     oldmode = mode(0);
 663:     if (!top)
 664:         putchar('\n');
 665:     else
 666:         signal(SIGINT, SIG_DFL);
 667:     for (;;) {
 668:         printf("%s> ", prompt);
 669:         if (gets(line) == 0) {
 670:             if (feof(stdin)) {
 671:                 clearerr(stdin);
 672:                 putchar('\n');
 673:             }
 674:             break;
 675:         }
 676:         if (line[0] == 0)
 677:             break;
 678:         makeargv();
 679:         c = getcmd(margv[0]);
 680:         if (c == (struct cmd *)-1) {
 681:             printf("?Ambiguous command\n");
 682:             continue;
 683:         }
 684:         if (c == 0) {
 685:             printf("?Invalid command\n");
 686:             continue;
 687:         }
 688:         (*c->handler)(margc, margv);
 689:         if (c->handler != help)
 690:             break;
 691:     }
 692:     if (!top) {
 693:         if (!connected)
 694:             longjmp(toplevel, 1);
 695:         (void) mode(oldmode);
 696:     }
 697: }
 698: 
 699: /*
 700:  * Telnet receiver states for fsm
 701:  */
 702: #define TS_DATA     0
 703: #define TS_IAC      1
 704: #define TS_WILL     2
 705: #define TS_WONT     3
 706: #define TS_DO       4
 707: #define TS_DONT     5
 708: #define TS_SB       6   /* in sub-negotiation */
 709: #define TS_SE       7   /* coming out of sub-negotiation */
 710: 
 711: #define SB_ACCUM(c) {*pSb = c;  /* accumulate character */ \
 712:             if (pSb >= SbBuffer+sizeof (SbBuffer)) { \
 713:                 /* can't accept any more */ \
 714:                 pSb = SbBuffer; \
 715:             } \
 716:             pSb++;}
 717: 
 718: static
 719: telrcv()
 720: {
 721:     register int c;
 722:     register char *Sbp;
 723:     register int Scc;
 724:     static int state = TS_DATA;
 725: 
 726:     while (scc > 0) {
 727:         c = *sbp++ & 0377, scc--;
 728:         switch (state) {
 729: 
 730:         case TS_DATA:
 731:             if (c == IAC) {
 732:                 state = TS_IAC;
 733:                 continue;
 734:             }
 735:                 /* We optimize this loop, since it is
 736: 				 * where we spend 99% of this routine.
 737: 				 */
 738:             if (In3270) {
 739:                 *Ifrontp++ = c;
 740:                 Sbp = sbp;
 741:                 Scc = scc;
 742:                 while (Scc > 0) {
 743:                 c = *Sbp++ & 0377, Scc--;
 744:                 if (c == IAC) {
 745:                     state = TS_IAC;
 746:                     break;
 747:                 }
 748:                 *Ifrontp++ = c;
 749:                 }
 750:                 sbp = Sbp;
 751:                 scc = Scc;
 752:             } else {
 753:                 *tfrontp++ = c;
 754:                 /*
 755: 			     * This hack is needed since we can't set
 756: 			     * CRMOD on output only.  Machines like MULTICS
 757: 			     * like to send \r without \n; since we must
 758: 			     * turn off CRMOD to get proper input, the mapping
 759: 			     * is done here (sigh).
 760: 			     */
 761:                 if (c == '\r' && crmod && !In3270)
 762:                     *tfrontp++ = '\n';
 763:             }
 764:             continue;
 765: 
 766: 
 767:         case TS_IAC:
 768:             switch (c) {
 769: 
 770:             case WILL:
 771:                 state = TS_WILL;
 772:                 continue;
 773: 
 774:             case WONT:
 775:                 state = TS_WONT;
 776:                 continue;
 777: 
 778:             case DO:
 779:                 state = TS_DO;
 780:                 continue;
 781: 
 782:             case DONT:
 783:                 state = TS_DONT;
 784:                 continue;
 785: 
 786:             case DM:
 787:                 outputPurge();
 788:                 break;
 789: 
 790:             case NOP:
 791:             case GA:
 792:                 break;
 793: 
 794:             case SB:
 795:                 state = TS_SB;
 796:                 pSb = SbBuffer; /* where to collect */
 797:                 continue;
 798: 
 799:             case EOR:
 800:                 if (In3270) {
 801:                     Ibackp += DataFromNetwork(Ibackp,
 802:                         Ifrontp-Ibackp, 1);
 803:                     if (Ibackp == Ifrontp) {
 804:                     Ibackp = Ifrontp = Ibuf;
 805:                     ISend = 0;  /* should have been! */
 806:                     } else {
 807:                     ISend = 1;
 808:                     }
 809:                 }
 810:                 break;
 811: 
 812:             case IAC:
 813:                 if (In3270) {
 814:                     *Ifrontp++ = IAC;
 815:                 } else {
 816:                     *tfrontp++ = IAC;
 817:                 }
 818:                 break;
 819: 
 820:             default:
 821:                 break;
 822:             }
 823:             state = TS_DATA;
 824:             continue;
 825: 
 826:         case TS_WILL:
 827:             printoption("RCVD", will, c, !hisopts[c]);
 828:             if (!hisopts[c])
 829:                 willoption(c);
 830:             state = TS_DATA;
 831:             continue;
 832: 
 833:         case TS_WONT:
 834:             printoption("RCVD", wont, c, hisopts[c]);
 835:             if (hisopts[c])
 836:                 wontoption(c);
 837:             state = TS_DATA;
 838:             continue;
 839: 
 840:         case TS_DO:
 841:             printoption("RCVD", doopt, c, !myopts[c]);
 842:             if (!myopts[c])
 843:                 dooption(c);
 844:             state = TS_DATA;
 845:             continue;
 846: 
 847:         case TS_DONT:
 848:             printoption("RCVD", dont, c, myopts[c]);
 849:             if (myopts[c]) {
 850:                 myopts[c] = 0;
 851:                 if (c == TELOPT_BINARY) {
 852:                     SetIn3270();
 853:                 }
 854:                 sprintf(nfrontp, wont, c);
 855:                 nfrontp += sizeof (wont) - 2;
 856:                 printoption("SENT", wont, c);
 857:             }
 858:             state = TS_DATA;
 859:             continue;
 860:         case TS_SB:
 861:             if (c == IAC) {
 862:                 state = TS_SE;
 863:                 continue;
 864:             }
 865:             SB_ACCUM(c);
 866:             continue;
 867:         case TS_SE:
 868:             if (c != SE) {
 869:             if (c != IAC) {
 870:                 SB_ACCUM(IAC);
 871:             }
 872:             SB_ACCUM(c);
 873:             state = TS_SB;
 874:             } else {
 875:             /* this is the end of the sub negotiation */
 876:             /* we only allow a termtype, send, sub */
 877:             if ((Sb_Option != TELOPT_TTYPE) ||
 878:                 (Sb_Command != TELQUAL_SEND)) {
 879:                 /* what to do? XXX */
 880:             } else {
 881:                 /* send our type */
 882:                 SentTerminalType = 1;
 883:                 SetIn3270();
 884:                 bcopy(sb_terminal, nfrontp, sizeof sb_terminal);
 885:                 nfrontp += sizeof sb_terminal;
 886:                 printoption("SENT", sb_terminal,
 887:                     TELOPT_TTYPE);
 888:             }
 889:             state = TS_DATA;
 890:             }
 891:         }
 892:     }
 893: }
 894: 
 895: static
 896: willoption(option)
 897:     int option;
 898: {
 899:     char *fmt;
 900: 
 901:     switch (option) {
 902: 
 903:     case TELOPT_ECHO:
 904:         (void) mode(1);
 905: 
 906:     case TELOPT_BINARY:
 907:         hisopts[option] = 1;
 908:         SetIn3270();
 909:         fmt = doopt;
 910:         break;
 911: 
 912:     case TELOPT_EOR:
 913:     case TELOPT_SGA:
 914:         hisopts[option] = 1;
 915:         fmt = doopt;
 916:         break;
 917: 
 918:     case TELOPT_TM:
 919:         fmt = dont;
 920:         break;
 921: 
 922:     default:
 923:         fmt = dont;
 924:         break;
 925:     }
 926:     sprintf(nfrontp, fmt, option);
 927:     nfrontp += sizeof (dont) - 2;
 928:     printoption("SENT", fmt, option);
 929: }
 930: 
 931: static
 932: wontoption(option)
 933:     int option;
 934: {
 935:     char *fmt;
 936: 
 937:     switch (option) {
 938: 
 939:     case TELOPT_BINARY:
 940:         hisopts[option] = 0;
 941:         SetIn3270();
 942:         fmt = doopt;
 943:         break;
 944: 
 945:     case TELOPT_ECHO:
 946:         (void) mode(2);
 947: 
 948:     case TELOPT_SGA:
 949:         hisopts[option] = 0;
 950:         fmt = dont;
 951:         break;
 952: 
 953:     default:
 954:         fmt = dont;
 955:     }
 956:     sprintf(nfrontp, fmt, option);
 957:     nfrontp += sizeof (doopt) - 2;
 958:     printoption("SENT", fmt, option);
 959: }
 960: 
 961: static
 962: dooption(option)
 963:     int option;
 964: {
 965:     char *fmt;
 966: 
 967:     switch (option) {
 968: 
 969:     case TELOPT_TTYPE:
 970:     case TELOPT_BINARY:
 971:         myopts[option] = 1;
 972:         SetIn3270();
 973:         fmt = will;
 974:         break;
 975: 
 976:     case TELOPT_TM:
 977:         fmt = wont;
 978:         break;
 979: 
 980:     case TELOPT_ECHO:
 981:         (void) mode(2);
 982:         fmt = will;
 983:         hisopts[option] = 0;
 984:         break;
 985: 
 986:     case TELOPT_EOR:
 987:     case TELOPT_SGA:
 988:         fmt = will;
 989:         break;
 990: 
 991:     default:
 992:         fmt = wont;
 993:         break;
 994:     }
 995:     sprintf(nfrontp, fmt, option);
 996:     nfrontp += (sizeof dont)-2;
 997:     printoption("SENT", fmt, option);
 998: }
 999: 
1000: static
1001: SetIn3270()
1002: {
1003:     if (SentTerminalType && myopts[TELOPT_BINARY] && hisopts[TELOPT_BINARY]) {
1004:     if (!In3270) {
1005:         In3270 = 1;
1006:         OptInit();      /* initialize mappings */
1007:         /* initialize terminal key mapping */
1008:         (void) DataFromTerminal(ttyobuf, 0);
1009:         (void) mode(3);
1010:     }
1011:     } else {
1012:     if (In3270) {
1013:         Stop3270(1);
1014:         In3270 = 0;
1015:         (void) mode(2);
1016:     }
1017:     }
1018: }
1019: 
1020: /*
1021:  * Set the escape character.
1022:  */
1023: static
1024: setescape(argc, argv)
1025:     int argc;
1026:     char *argv[];
1027: {
1028:     register char *arg;
1029:     char buf[50];
1030: 
1031:     if (argc > 2)
1032:         arg = argv[1];
1033:     else {
1034:         printf("new escape character: ");
1035:         gets(buf);
1036:         arg = buf;
1037:     }
1038:     if (arg[0] != '\0')
1039:         escape = arg[0];
1040:     printf("Escape character is '%s'.\n", control(escape));
1041:     fflush(stdout);
1042: }
1043: 
1044: /*VARARGS*/
1045: static
1046: setoptions()
1047: {
1048: 
1049:     showoptions = !showoptions;
1050:     printf("%s show option processing.\n", showoptions ? "Will" : "Wont");
1051:     fflush(stdout);
1052: }
1053: 
1054: /*VARARGS*/
1055: static
1056: setcrmod()
1057: {
1058: 
1059:     crmod = !crmod;
1060:     printf("%s map carriage return on output.\n", crmod ? "Will" : "Wont");
1061:     fflush(stdout);
1062: }
1063: 
1064: /*VARARGS*/
1065: static
1066: setdebug()
1067: {
1068: 
1069:     debug = !debug;
1070:     printf("%s turn on socket level debugging.\n",
1071:         debug ? "Will" : "Wont");
1072:     fflush(stdout);
1073:     if (debug && net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
1074:         perror("setsockopt (SO_DEBUG)");
1075: }
1076: 
1077: /*VARARGS*/
1078: static
1079: SetPrintNet()
1080: {
1081: 
1082:     printnet = !printnet;
1083:     printf("%s turn on printing of raw network traffic.\n",
1084:         printnet ? "Will" : "Wont");
1085: }
1086: 
1087: /*
1088:  * Construct a control character sequence
1089:  * for a special character.
1090:  */
1091: static char *
1092: control(c)
1093:     register int c;
1094: {
1095:     static char buf[3];
1096: 
1097:     if (c == 0177)
1098:         return ("^?");
1099:     if (c >= 040) {
1100:         buf[0] = c;
1101:         buf[1] = 0;
1102:     } else {
1103:         buf[0] = '^';
1104:         buf[1] = '@'+c;
1105:         buf[2] = 0;
1106:     }
1107:     return (buf);
1108: }
1109: 
1110: static struct cmd *
1111: getcmd(name)
1112:     register char *name;
1113: {
1114:     register char *p, *q;
1115:     register struct cmd *c, *found;
1116:     register int nmatches, longest;
1117: 
1118:     longest = 0;
1119:     nmatches = 0;
1120:     found = 0;
1121:     for (c = cmdtab; p = c->name; c++) {
1122:         for (q = name; *q == *p++; q++)
1123:             if (*q == 0)        /* exact match? */
1124:                 return (c);
1125:         if (!*q) {          /* the name was a prefix */
1126:             if (q - name > longest) {
1127:                 longest = q - name;
1128:                 nmatches = 1;
1129:                 found = c;
1130:             } else if (q - name == longest)
1131:                 nmatches++;
1132:         }
1133:     }
1134:     if (nmatches > 1)
1135:         return ((struct cmd *)-1);
1136:     return (found);
1137: }
1138: 
1139: static
1140: deadpeer()
1141: {
1142:     (void) mode(0);
1143:     longjmp(peerdied, -1);
1144: }
1145: 
1146: static
1147: intr()
1148: {
1149:     (void) mode(0);
1150:     longjmp(toplevel, -1);
1151: }
1152: 
1153: static
1154: inputAvailable()
1155: {
1156:     HaveInput = 1;
1157: }
1158: 
1159: /* outputPurge() - get rid of all output destined for terminal. */
1160: outputPurge()
1161: {
1162:     ioctl(fileno(stdout), TIOCFLUSH, (char *)0);
1163:     tbackp = tfrontp = ttyobuf;
1164: }
1165: 
1166: ttyflush()
1167: {
1168:     int n;
1169: 
1170:     if ((n = tfrontp - tbackp) > 0)
1171:         n = write(tout, tbackp, n);
1172:     if (n < 0)
1173:         return;
1174:     tbackp += n;
1175:     if (tbackp == tfrontp)
1176:         tbackp = tfrontp = ttyobuf;
1177: }
1178: 
1179: /* TtyChars() - returns the number of characters in the TTY buffer */
1180: TtyChars()
1181: {
1182:     return(tfrontp-tbackp);
1183: }
1184: 
1185: netflush()
1186: {
1187:     int n;
1188: 
1189:     if ((n = nfrontp - nbackp) > 0)
1190:         n = write(net, nbackp, n);
1191:     if (n < 0) {
1192:         if (errno != ENOBUFS && errno != EWOULDBLOCK) {
1193:             (void) mode(0);
1194:             perror(hostname);
1195:             close(net);
1196:             longjmp(peerdied, -1);
1197:             /*NOTREACHED*/
1198:         }
1199:         n = 0;
1200:     }
1201:     if (printnet) {
1202:         Dump('>', nbackp, n);
1203:     }
1204:     nbackp += n;
1205:     if (nbackp == nfrontp)
1206:         nbackp = nfrontp = netobuf;
1207: }
1208: 
1209: /* DataToNetwork - queue up some data to go to network.  When last byte is
1210:     queued, we add on an IAC EOR sequence (so, don't call us until you
1211:     want that done...)
1212:  */
1213: 
1214: int
1215: DataToNetwork(buffer, count)
1216: register char   *buffer;        /* where the data is */
1217: register int    count;          /* how much to send */
1218: {
1219:     register int c;
1220:     int origCount;
1221: 
1222:     origCount = count;
1223: 
1224:     while (count) {
1225:     if ((&netobuf[sizeof netobuf] - nfrontp) < 6) {
1226:         netflush();
1227:         if ((&netobuf[sizeof netobuf] - nfrontp) < 6) {
1228:         break;
1229:         }
1230:     }
1231:     c = *buffer++;
1232:     count--;
1233:     if (c == IAC) {
1234:         *nfrontp++ = IAC;
1235:         *nfrontp++ = IAC;
1236:     } else {
1237:         *nfrontp++ = c;
1238:     }
1239:     }
1240: 
1241:     if (!count) {
1242:     *nfrontp++ = IAC;
1243:     *nfrontp++ = EOR;
1244:     netflush();     /* try to move along as quickly as ... */
1245:     }
1246:     return(origCount - count);
1247: }
1248: 
1249: /* DataToTerminal - queue up some data to go to terminal. */
1250: 
1251: int
1252: DataToTerminal(buffer, count)
1253: register char   *buffer;        /* where the data is */
1254: register int    count;          /* how much to send */
1255: {
1256:     int origCount;
1257:     int o;
1258: 
1259:     origCount = count;
1260: 
1261:     while (count) {
1262:     if (tfrontp >= &ttyobuf[sizeof ttyobuf]) {
1263:         ttyflush();
1264:         while (tfrontp >= &ttyobuf[sizeof ttyobuf]) {
1265:         o = 1<<tout;
1266:         (void) select(tout+1, (int *) 0, &o, (int *) 0,
1267:                         (struct timeval *) 0);
1268:         ttyflush();
1269:         }
1270:     }
1271:     *tfrontp++ = *buffer++;
1272:     count--;
1273:     }
1274:     return(origCount - count);
1275: }
1276: 
1277: /* EmptyTerminal - called to make sure that the terminal buffer is empty.
1278:  *			Note that we consider the buffer to run all the
1279:  *			way to the kernel (thus the select).
1280:  */
1281: 
1282: void
1283: EmptyTerminal()
1284: {
1285:     int o;
1286: 
1287:     o = 1<<tout;
1288: 
1289:     if (tfrontp == tbackp) {
1290:     (void) select(tout+1, (int *) 0, &o, (int *) 0,
1291:             (struct timeval *) 0);  /* wait for TTLOWAT */
1292:     } else {
1293:     while (tfrontp != tbackp) {
1294:         ttyflush();
1295:         (void) select(tout+1, (int *) 0, &o, (int *) 0,
1296:                 (struct timeval *) 0);  /* wait for TTLOWAT */
1297:     }
1298:     }
1299: }
1300: 
1301: 
1302: 
1303: /* StringToTerminal - output a null terminated string to the terminal */
1304: 
1305: int
1306: StringToTerminal(s)
1307: char *s;
1308: {
1309:     int count;
1310: 
1311:     count = strlen(s);
1312:     if (count) {
1313:     (void) DataToTerminal(s, count);    /* we know it always goes... */
1314:     }
1315: }
1316: 
1317: 
1318: /* _putchar - output a single character to the terminal.  This name is so that
1319:  *	curses(3x) can call us to send out data.
1320:  */
1321: 
1322: _putchar(c)
1323: char c;
1324: {
1325:     if (tfrontp >= &ttyobuf[sizeof ttyobuf]) {
1326:     (void) DataToTerminal(&c, 1);
1327:     } else {
1328:     *tfrontp++ = c;     /* optimize if possible. */
1329:     }
1330: }
1331: 
1332: static
1333: SetForExit()
1334: {
1335:     (void) mode(2);         /* switch modes to flush output */
1336:     (void) mode(0);
1337:     fflush(stdout);
1338:     fflush(stderr);
1339:     if (In3270) {
1340:     Stop3270(0);
1341:     }
1342:     (void) mode(2);         /* make sure we go back to mode 0 */
1343:     (void) mode(0);
1344: }
1345: 
1346: static
1347: Exit(returnCode)
1348: int returnCode;
1349: {
1350:     SetForExit();
1351:     exit(returnCode);
1352: }
1353: 
1354: ExitString(file, string, returnCode)
1355: FILE *file;
1356: char *string;
1357: int returnCode;
1358: {
1359:     SetForExit();
1360:     fwrite(string, 1, strlen(string), file);
1361:     exit(returnCode);
1362: }
1363: 
1364: ExitPerror(string, returnCode)
1365: char *string;
1366: int returnCode;
1367: {
1368:     SetForExit();
1369:     perror(string);
1370:     exit(returnCode);
1371: }
1372: 
1373: 
1374: static
1375: Dump(direction, buffer, length)
1376: char    direction;
1377: char    *buffer;
1378: int length;
1379: {
1380: #   define BYTES_PER_LINE   32
1381:     char *pThis;
1382:     int offset;
1383: 
1384:     offset = 0;
1385: 
1386:     while (length) {
1387:     /* print one line */
1388:     fprintf(NetTrace, "%c 0x%x\t", direction, offset);
1389:     pThis = buffer;
1390:     buffer = buffer+min(length, BYTES_PER_LINE);
1391:     while (pThis < buffer) {
1392:         fprintf(NetTrace, "%.2x", (*pThis)&0xff);
1393:         pThis++;
1394:     }
1395:     fprintf(NetTrace, "\n");
1396:     length -= BYTES_PER_LINE;
1397:     offset += BYTES_PER_LINE;
1398:     if (length < 0) {
1399:         return;
1400:     }
1401:     /* find next unique line */
1402:     }
1403: }
1404: 
1405: 
1406: 
1407: /*VARARGS*/
1408: static
1409: printoption(direction, fmt, option, what)
1410:     char *direction, *fmt;
1411:     int option, what;
1412: {
1413:     if (!showoptions)
1414:         return;
1415:     printf("%s ", direction);
1416:     if (fmt == doopt)
1417:         fmt = "do";
1418:     else if (fmt == dont)
1419:         fmt = "dont";
1420:     else if (fmt == will)
1421:         fmt = "will";
1422:     else if (fmt == wont)
1423:         fmt = "wont";
1424:     else if (fmt == sb_terminal)
1425:         fmt = "will (terminal)";
1426:     else
1427:         fmt = "???";
1428:     if (option < TELOPT_SUPDUP)
1429:         printf("%s %s", fmt, telopts[option]);
1430:     else
1431:         printf("%s %d", fmt, option);
1432:     if (*direction == '<') {
1433:         printf("\r\n");
1434:         return;
1435:     }
1436:     printf(" (%s)\r\n", what ? "reply" : "don't reply");
1437: }

Defined functions

DataToNetwork defined in line 1214; used 3 times
DataToTerminal defined in line 1251; used 3 times
Dump defined in line 1374; used 2 times
EmptyTerminal defined in line 1282; used 5 times
Exit defined in line 1346; used 2 times
ExitPerror defined in line 1364; never used
ExitString defined in line 1354; used 3 times
Scheduler defined in line 506; used 2 times
SetForExit defined in line 1332; used 3 times
SetIn3270 defined in line 1000; used 5 times
SetPrintNet defined in line 1078; used 2 times
StringToTerminal defined in line 1305; used 6 times
TtyChars defined in line 1180; never used
_putchar defined in line 1322; never used
bye defined in line 320; used 3 times
call defined in line 379; used 2 times
command defined in line 656; used 4 times
control defined in line 1091; used 2 times
deadpeer defined in line 1139; used 2 times
dooption defined in line 961; used 1 times
getcmd defined in line 1110; used 3 times
help defined in line 347; used 7 times
inputAvailable defined in line 1153; used 2 times
intr defined in line 1146; used 2 times
main defined in line 157; never used
makeargv defined in line 282; used 2 times
mode defined in line 395; used 24 times
netflush defined in line 1185; used 4 times
outputPurge defined in line 1160; used 2 times
printoption defined in line 1408; used 9 times
quit defined in line 338; used 3 times
setcrmod defined in line 1055; used 2 times
setdebug defined in line 1065; used 2 times
setescape defined in line 1023; used 2 times
setoptions defined in line 1045; used 2 times
status defined in line 271; used 3 times
suspend defined in line 306; used 4 times
telnet defined in line 465; used 1 times
telrcv defined in line 718; used 1 times
tn defined in line 198; used 3 times
ttyflush defined in line 1166; used 4 times
willoption defined in line 895; used 1 times
wontoption defined in line 931; used 1 times

Defined variables

ForceMode defined in line 84; used 3 times
HaveInput defined in line 77; used 3 times
ISend defined in line 83; used 6 times
Ibuf defined in line 50; used 12 times
Ifrontp defined in line 50; used 15 times
In3270 defined in line 82; used 13 times
SbBuffer defined in line 54; used 7 times
SentTerminalType defined in line 81; used 2 times
closehelp defined in line 118; used 1 times
cmdtab defined in line 129; used 2 times
connected defined in line 80; used 6 times
crmod defined in line 88; used 4 times
crmodhelp defined in line 126; used 1 times
debug defined in line 87; used 6 times
debughelp defined in line 121; used 1 times
dont defined in line 63; used 8 times
doopt defined in line 62; used 6 times
escape defined in line 92; used 3 times
escapehelp defined in line 122; used 1 times
helphelp defined in line 124; used 2 times
hisopts defined in line 59; used 12 times
hnamebuf defined in line 196; used 2 times
hostname defined in line 195; used 5 times
line defined in line 94; used 6 times
margc defined in line 95; used 4 times
margv defined in line 96; used 4 times
myopts defined in line 60; used 7 times
net defined in line 85; used 20 times
netobuf defined in line 52; used 8 times
nfrontp defined in line 52; used 27 times
noltc defined in line 393; used 1 times
notc defined in line 392; used 2 times
oltc defined in line 154; used 3 times
openhelp defined in line 117; used 1 times
optionshelp defined in line 125; used 1 times
otc defined in line 153; used 5 times
ottyb defined in line 155; used 3 times
pSb defined in line 54; used 5 times
peerdied defined in line 99; used 3 times
printnet defined in line 89; used 5 times
printnethelp defined in line 127; used 1 times
prompt defined in line 91; used 2 times
quithelp defined in line 119; used 1 times
sb_terminal defined in line 66; used 5 times
sbp defined in line 457; used 5 times
scc defined in line 459; used 13 times
sccsid defined in line 24; never used
showoptions defined in line 86; used 4 times
sibuf defined in line 457; used 3 times
sin defined in line 146; used 11 times
sp defined in line 151; used 3 times
statushelp defined in line 123; used 1 times
tbp defined in line 458; used 4 times
tcc defined in line 459; used 13 times
tfrontp defined in line 51; used 20 times
tibuf defined in line 458; used 3 times
tin defined in line 460; used 5 times
toplevel defined in line 98; used 4 times
tout defined in line 460; used 10 times
ttyobuf defined in line 51; used 11 times
will defined in line 64; used 5 times
wont defined in line 65; used 7 times
zhelp defined in line 120; used 2 times

Defined struct's

cmd defined in line 110; used 20 times

Defined macros

BYTES_PER_LINE defined in line 1380; used 3 times
HELPINDENT defined in line 108; used 1 times
SB_ACCUM defined in line 711; used 3 times
Sb_Command defined in line 56; used 1 times
Sb_Option defined in line 55; used 1 times
TELOPTS defined in line 37; never used
TS_DATA defined in line 702; used 7 times
TS_DO defined in line 706; used 1 times
TS_DONT defined in line 707; used 1 times
TS_IAC defined in line 703; used 2 times
TS_SB defined in line 708; used 2 times
TS_SE defined in line 709; used 1 times
TS_WILL defined in line 704; used 1 times
TS_WONT defined in line 705; used 1 times
min defined in line 48; used 1 times
strip defined in line 47; used 1 times
Last modified: 1986-05-13
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 6333
Valid CSS Valid XHTML 1.0 Strict