# 2.11BSD#431 - /usr/src/ucb/telnet.c

##### Functions

    bye               X
    call              X
    command           X
    control           X
    deadpeer          X
    display           X
    docharmode        X
    doescape          X
    doflush           X
    dolinemode        X
    dooption          X
    dosynch           X
    genget            X
    getcmd            X
    getconnmode       X
    getmodecmd        X
    getnextcmd        X
    getnextmode       X
    getnextsend       X
    getnextset        X
    getnexttoggle     X
    getsend           X
    getset            X
    gettoggle         X
    help              X
    intp              X
    intr              X
    intr2             X
    lclchars          X
    main              X
    makeargv          X
    mode              X
    modecmd           X
    netclear          X
    netflush          X
    nextitem          X
    printoption       X
    quit              X
    sendbrk           X
    sendcmd           X
    setcmd            X
    setcommandmode    X
    setconnmode       X
    setescape         X
    special           X
    status            X
    stilloob          X
    suboption         X
    suspend           X
    telnet            X
    telrcv            X
    tn                X
    togcrmod          X
    togdebug          X
    toggle            X
    togglehelp        X
    ttyflush          X
    upcase            X
    willoption        X
    wontoption        X

##### Variables

    Modelist          X
    SYNCHing          X
    Sendlist          X
    Sendlist2         X
    Setlist           X
    Togglelist        X
    ambiguous         X
    autoflush         X
    autosynch         X
    closehelp         X
    cmdtab            X
    cmdtab2           X
    connected         X
    copyright         X
    crmod             X
    crmodhelp         X
    debug             X
    displayhelp       X
    donelclchars      X
    dont              X
    dontlecho         X
    doopt             X
    echoc             X
    escape            X
    escapehelp        X
    flushline         X
    flushout          X
    globalmode        X
    helphelp          X
    hisopts           X
    hnamebuf          X
    hostname          X
    line              X
    localchars        X
    margc             X
    margv             X
    modedescriptions  X
    modehelp          X
    myopts            X
    net               X
    netdata           X
    netobuf           X
    neturg            X
    nfrontp           X
    nltc              X
    ntc               X
    nttyb             X
    oltc              X
    openhelp          X
    otc               X
    ottyb             X
    peerdied          X
    prompt            X
    quithelp          X
    sbp               X
    scc               X
    sendhelp          X
    sethelp           X
    showoptions       X
    sibuf             X
    sin               X
    sp                X
    statushelp        X
    subbuffer         X
    subend            X
    subpointer        X
    tbp               X
    tcc               X
    telnetport        X
    tfrontp           X
    tibuf             X
    togglestring      X
    toplevel          X
    tout              X
    ttyobuf           X
    will              X
    wont              X
    zhelp             X

##### Macros

    Ambiguous         X
    BYTES_PER_LINE    X
    FD_CLR            X
    FD_ISSET          X
    FD_SET            X
    FD_ZERO           X
    HELPINDENT        X
    NET2ADD           X
    NETADD            X
    NETBYTES          X
    NETLOC            X
    NETMAX            X
    NETROOM           X
    SB_ACCUM          X
    SB_CLEAR          X
    SB_TERM           X
    SENDESCAPE        X
    SENDQUESTION      X
    TELOPTS           X
    TS_CR             X
    TS_DATA           X
    TS_DO             X
    TS_DONT           X
    TS_IAC            X
    TS_SB             X
    TS_SE             X
    TS_WILL           X
    TS_WONT           X
    TTYADD            X
    TTYBYTES          X
    TTYLOC            X
    TTYMAX            X
    TTYMIN            X
    TTYROOM           X
    doset             X
    dotog             X
    min               X
    settimer          X
    strip             X
    wewant            X

##### struct's

    cmd               X
    sendlist          X
    setlist           X
    togglelist        X

       1: /*
       2:  * Copyright (c) 1983 Regents of the University of California.
       3:  * All rights reserved.  The Berkeley software License Agreement
       4:  * specifies the terms and conditions for redistribution.
       5:  */
       6:
       7: #if defined(DOSCCS) && !defined(lint)
       8: char copyright[] =
       9: "@(#) Copyright (c) 1983 Regents of the University of California.\n\
      10:  All rights reserved.\n";
      11:
      12: static char sccsid[] = "@(#)telnet.c	5.16.1 (2.11BSD GTE) 1/1/94";
      13: #endif
      14:
      15: /*
      16:  * User telnet program.
      17:  *
      18:  * Many of the FUNCTIONAL changes in this newest version of telnet
      19:  * were suggested by Dave Borman of Cray Research, Inc.
      20:  */
      21:
      22: #include <sys/types.h>
      23: #include <sys/socket.h>
      24: #include <sys/ioctl.h>
      25: #include <sys/time.h>
      26:
      27: #include <netinet/in.h>
      28:
      29: #define TELOPTS
      30: #include <arpa/telnet.h>
      31: #include <arpa/inet.h>
      32:
      33: #include <stdio.h>
      34: #include <ctype.h>
      35: #include <errno.h>
      36: #include <signal.h>
      37: #include <setjmp.h>
      38: #include <netdb.h>
      39: #include <strings.h>
      40:
      41:
      42:
      43: #ifndef FD_SETSIZE
      44: /*
      45:  * The following is defined just in case someone should want to run
      46:  * this telnet on a 4.2 system.
      47:  *
      48:  */
      49:
      50: #define FD_SET(n, p)    ((p)->fds_bits[0] |= (1<<(n)))
      51: #define FD_CLR(n, p)    ((p)->fds_bits[0] &= ~(1<<(n)))
      52: #define FD_ISSET(n, p)  ((p)->fds_bits[0] & (1<<(n)))
      53: #define FD_ZERO(p)  ((p)->fds_bits[0] = 0)
      54:
      55: #endif
      56:
      57: #define strip(x)    ((x)&0x7f)
      58:
      59: char    ttyobuf[2*BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
      60: #define TTYADD(c)   { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
      61: #define TTYLOC()    (tfrontp)
      62: #define TTYMAX()    (ttyobuf+sizeof ttyobuf-1)
      63: #define TTYMIN()    (netobuf)
      64: #define TTYBYTES()  (tfrontp-tbackp)
      65: #define TTYROOM()   (TTYMAX()-TTYLOC()+1)
      66:
      67: char    netobuf[2*BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
      68: #define NETADD(c)   { *nfrontp++ = c; }
      69: #define NET2ADD(c1,c2)  { NETADD(c1); NETADD(c2); }
      70: #define NETLOC()    (nfrontp)
      71: #define NETMAX()    (netobuf+sizeof netobuf-1)
      72: #define NETBYTES()  (nfrontp-nbackp)
      73: #define NETROOM()   (NETMAX()-NETLOC()+1)
      74: char    *neturg = 0;        /* one past last byte of urgent data */
      75:
      76: char    subbuffer[100], *subpointer, *subend;   /* buffer for sub-options */
      77: #define SB_CLEAR()  subpointer = subbuffer;
      78: #define SB_TERM()   subend = subpointer;
      79: #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
      80:                 *subpointer++ = (c); \
      81:             }
      82:
      83: char    hisopts[256];
      84: char    myopts[256];
      85:
      86: char    doopt[] = { IAC, DO, '%', 'c', 0 };
      87: char    dont[] = { IAC, DONT, '%', 'c', 0 };
      88: char    will[] = { IAC, WILL, '%', 'c', 0 };
      89: char    wont[] = { IAC, WONT, '%', 'c', 0 };
      90:
      91: struct cmd {
      92:     char    *name;      /* command name */
      93:     char    *help;      /* help string */
      94:     int (*handler)();   /* routine which executes command */
      95:     int dohelp;     /* Should we give general help information? */
      96:     int needconnect;    /* Do we need to be connected to execute? */
      97: };
      98:
      99: int connected;
     100: int net;
     101: int tout;
     102: int showoptions = 0;
     103: int debug = 0;
     104: int crmod = 0;
     105: int netdata = 0;
     106: static FILE *NetTrace;
     107: int telnetport = 1;
     108:
     109:
     110: char    *prompt;
     111: char    escape = CTRL(]);
     112: char    echoc = CTRL(E);
     113:
     114: int SYNCHing = 0;       /* we are in TELNET SYNCH mode */
     115: int flushout = 0;       /* flush output */
     116: int autoflush = 0;      /* flush output when interrupting? */
     117: int autosynch = 0;      /* send interrupt characters with SYNCH? */
     118: int localchars = 0;     /* we recognize interrupt/quit */
     119: int donelclchars = 0;   /* the user has set "localchars" */
     120: int dontlecho = 0;      /* do we suppress local echoing right now? */
     121:
     122: char    line[200];
     123: int margc;
     124: char    *margv[20];
     125:
     126: jmp_buf toplevel;
     127: jmp_buf peerdied;
     128:
     129: extern  int errno;
     130:
     131:
     132: struct sockaddr_in sin;
     133:
     134: struct  cmd *getcmd();
     135: struct  servent *sp;
     136:
     137: struct  tchars otc, ntc;
     138: struct  ltchars oltc, nltc;
     139: struct  sgttyb ottyb, nttyb;
     140: int globalmode = 0;
     141: int flushline = 1;
     142:
     143: char    *hostname;
     144: char    hnamebuf[32];
     145:
     146: /*
     147:  * The following are some clocks used to decide how to interpret
     148:  * the relationship between various variables.
     149:  */
     150:
     151: struct {
     152:     int
     153:     system,         /* what the current time is */
     154:     echotoggle,     /* last time user entered echo character */
     155:     modenegotiated,     /* last time operating mode negotiated */
     156:     didnetreceive,      /* last time we read data from network */
     157:     gotDM;          /* when did we last see a data mark */
     158: } clocks;
     159:
     160: #define settimer(x) clocks.x = clocks.system++
     161:
     162: /*
     163:  * Various utility routines.
     164:  */
     165:
     166: char *ambiguous;        /* special return value */
     167: #define Ambiguous(t)    ((t)&ambiguous)
     168:
     169:
     170: char **
     171: genget(name, table, next)
     172: char    *name;      /* name to match */
     173: char    **table;        /* name entry in table */
     174: char    **(*next)();    /* routine to return next entry in table */
     175: {
     176:     register char *p, *q;
     177:     register char **c, **found;
     178:     register int nmatches, longest;
     179:
     180:     longest = 0;
     181:     nmatches = 0;
     182:     found = 0;
     183:     for (c = table; p = *c; c = (*next)(c)) {
     184:         for (q = name; *q == *p++; q++)
     185:             if (*q == 0)        /* exact match? */
     186:                 return (c);
     187:         if (!*q) {          /* the name was a prefix */
     188:             if (q - name > longest) {
     189:                 longest = q - name;
     190:                 nmatches = 1;
     191:                 found = c;
     192:             } else if (q - name == longest)
     193:                 nmatches++;
     194:         }
     195:     }
     196:     if (nmatches > 1)
     197:         return Ambiguous(char **);
     198:     return (found);
     199: }
     200:
     201: /*
     202:  * Make a character string into a number.
     203:  *
     204:  * Todo:  1.  Could take random integers (12, 0x12, 012, 0b1).
     205:  */
     206:
     207: special(s)
     208: register char *s;
     209: {
     210:     register char c;
     211:     char b;
     212:
     213:     switch (*s) {
     214:     case '^':
     215:         b = *++s;
     216:         if (b == '?') {
     217:             c = b | 0x40;       /* DEL */
     218:         } else {
     219:             c = b & 0x1f;
     220:         }
     221:         break;
     222:     default:
     223:         c = *s;
     224:         break;
     225:     }
     226:     return c;
     227: }
     228:
     229: /*
     230:  * Construct a control character sequence
     231:  * for a special character.
     232:  */
     233: char *
     234: control(c)
     235:     register int c;
     236: {
     237:     static char buf[3];
     238:
     239:     if (c == 0x7f)
     240:         return ("^?");
     241:     if (c == '\377') {
     242:         return "off";
     243:     }
     244:     if (c >= 0x20) {
     245:         buf[0] = c;
     246:         buf[1] = 0;
     247:     } else {
     248:         buf[0] = '^';
     249:         buf[1] = '@'+c;
     250:         buf[2] = 0;
     251:     }
     252:     return (buf);
     253: }
     254:
     255:
     256: /*
     257:  * upcase()
     258:  *
     259:  *	Upcase (in place) the argument.
     260:  */
     261:
     262: void
     263: upcase(argument)
     264: register char *argument;
     265: {
     266:     register int c;
     267:
     268:     while (c = *argument) {
     269:     if (islower(c)) {
     270:         *argument = toupper(c);
     271:     }
     272:     argument++;
     273:     }
     274: }
     275:
     276: /*
     277:  * Check to see if any out-of-band data exists on a socket (for
     278:  * Telnet "synch" processing).
     279:  */
     280:
     281: int
     282: stilloob(s)
     283: int s;      /* socket number */
     284: {
     285:     static struct timeval timeout = { 0 };
     286:     fd_set  excepts;
     287:     int value;
     288:
     289:     do {
     290:     FD_ZERO(&excepts);
     291:     FD_SET(s, &excepts);
     292:     value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);
     293:     } while ((value == -1) && (errno = EINTR));
     294:
     295:     if (value < 0) {
     296:     perror("select");
     297:     quit();
     298:     }
     299:     if (FD_ISSET(s, &excepts)) {
     300:     return 1;
     301:     } else {
     302:     return 0;
     303:     }
     304: }
     305:
     306:
     307: /*
     308:  *  netflush
     309:  *		Send as much data as possible to the network,
     310:  *	handling requests for urgent data.
     311:  */
     312:
     313:
     314: netflush(fd)
     315: {
     316:     int n;
     317:
     318:     if ((n = nfrontp - nbackp) > 0) {
     319:     if (!neturg) {
     320:         n = write(fd, nbackp, n);   /* normal write */
     321:     } else {
     322:         n = neturg - nbackp;
     323:         /*
     324: 	     * In 4.2 (and 4.3) systems, there is some question about
     325: 	     * what byte in a sendOOB operation is the "OOB" data.
     326: 	     * To make ourselves compatible, we only send ONE byte
     327: 	     * out of band, the one WE THINK should be OOB (though
     328: 	     * we really have more the TCP philosophy of urgent data
     329: 	     * rather than the Unix philosophy of OOB data).
     330: 	     */
     331:         if (n > 1) {
     332:         n = send(fd, nbackp, n-1, 0);   /* send URGENT all by itself */
     333:         } else {
     334:         n = send(fd, nbackp, n, MSG_OOB);   /* URGENT data */
     335:         }
     336:     }
     337:     }
     338:     if (n < 0) {
     339:     if (errno != ENOBUFS && errno != EWOULDBLOCK) {
     340:         setcommandmode();
     341:         perror(hostname);
     342:         close(fd);
     343:         neturg = 0;
     344:         longjmp(peerdied, -1);
     345:         /*NOTREACHED*/
     346:     }
     347:     n = 0;
     348:     }
     349:     if (netdata && n) {
     350:     Dump('>', nbackp, n);
     351:     }
     352:     nbackp += n;
     353:     if (nbackp >= neturg) {
     354:     neturg = 0;
     355:     }
     356:     if (nbackp == nfrontp) {
     357:     nbackp = nfrontp = netobuf;
     358:     }
     359: }
     360:
     361: /*
     362:  * nextitem()
     363:  *
     364:  *	Return the address of the next "item" in the TELNET data
     365:  * stream.  This will be the address of the next character if
     366:  * the current address is a user data character, or it will
     367:  * be the address of the character following the TELNET command
     368:  * if the current address is a TELNET IAC ("I Am a Command")
     369:  * character.
     370:  */
     371:
     372: char *
     373: nextitem(current)
     374: char    *current;
     375: {
     376:     if ((*current&0xff) != IAC) {
     377:     return current+1;
     378:     }
     379:     switch (*(current+1)&0xff) {
     380:     case DO:
     381:     case DONT:
     382:     case WILL:
     383:     case WONT:
     384:     return current+3;
     385:     case SB:        /* loop forever looking for the SE */
     386:     {
     387:         register char *look = current+2;
     388:
     389:         for (;;) {
     390:         if ((*look++&0xff) == IAC) {
     391:             if ((*look++&0xff) == SE) {
     392:             return look;
     393:             }
     394:         }
     395:         }
     396:     }
     397:     default:
     398:     return current+2;
     399:     }
     400: }
     401: /*
     402:  * netclear()
     403:  *
     404:  *	We are about to do a TELNET SYNCH operation.  Clear
     405:  * the path to the network.
     406:  *
     407:  *	Things are a bit tricky since we may have sent the first
     408:  * byte or so of a previous TELNET command into the network.
     409:  * So, we have to scan the network buffer from the beginning
     410:  * until we are up to where we want to be.
     411:  *
     412:  *	A side effect of what we do, just to keep things
     413:  * simple, is to clear the urgent data pointer.  The principal
     414:  * caller should be setting the urgent data pointer AFTER calling
     415:  * us in any case.
     416:  */
     417:
     418: netclear()
     419: {
     420:     register char *thisitem, *next;
     421:     char *good;
     422: #define wewant(p)   ((nfrontp > p) && ((*p&0xff) == IAC) && \
     423:                 ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL))
     424:
     425:     thisitem = netobuf;
     426:
     427:     while ((next = nextitem(thisitem)) <= nbackp) {
     428:     thisitem = next;
     429:     }
     430:
     431:     /* Now, thisitem is first before/at boundary. */
     432:
     433:     good = netobuf; /* where the good bytes go */
     434:
     435:     while (nfrontp > thisitem) {
     436:     if (wewant(thisitem)) {
     437:         int length;
     438:
     439:         next = thisitem;
     440:         do {
     441:         next = nextitem(next);
     442:         } while (wewant(next) && (nfrontp > next));
     443:         length = next-thisitem;
     444:         bcopy(thisitem, good, length);
     445:         good += length;
     446:         thisitem = next;
     447:     } else {
     448:         thisitem = nextitem(thisitem);
     449:     }
     450:     }
     451:
     452:     nbackp = netobuf;
     453:     nfrontp = good;     /* next byte to be sent */
     454:     neturg = 0;
     455: }
     456:
     457: /*
     458:  * Send as much data as possible to the terminal.
     459:  */
     460:
     461:
     462: ttyflush()
     463: {
     464:     int n;
     465:
     466:     if ((n = tfrontp - tbackp) > 0) {
     467:     if (!(SYNCHing||flushout)) {
     468:         n = write(tout, tbackp, n);
     469:     } else {
     470:         ioctl(fileno(stdout), TIOCFLUSH, (char *) 0);
     471:         /* we leave 'n' alone! */
     472:     }
     473:     }
     474:     if (n < 0) {
     475:     return;
     476:     }
     477:     tbackp += n;
     478:     if (tbackp == tfrontp) {
     479:     tbackp = tfrontp = ttyobuf;
     480:     }
     481: }
     482:
     483: /*
     484:  * Various signal handling routines.
     485:  */
     486:
     487: deadpeer()
     488: {
     489:     setcommandmode();
     490:     longjmp(peerdied, -1);
     491: }
     492:
     493: intr()
     494: {
     495:     if (localchars) {
     496:     intp();
     497:     return;
     498:     }
     499:     setcommandmode();
     500:     longjmp(toplevel, -1);
     501: }
     502:
     503: intr2()
     504: {
     505:     if (localchars) {
     506:     sendbrk();
     507:     return;
     508:     }
     509: }
     510:
     511: doescape()
     512: {
     513:     command(0);
     514: }
     515:
     516: /*
     517:  * The following are routines used to print out debugging information.
     518:  */
     519:
     520:
     521: static
     522: Dump(direction, buffer, length)
     523: char    direction;
     524: char    *buffer;
     525: int length;
     526: {
     527: #   define BYTES_PER_LINE   32
     528: #   define min(x,y) ((x<y)? x:y)
     529:     char *pThis;
     530:     int offset;
     531:
     532:     offset = 0;
     533:
     534:     while (length) {
     535:     /* print one line */
     536:     fprintf(NetTrace, "%c 0x%x\t", direction, offset);
     537:     pThis = buffer;
     538:     buffer = buffer+min(length, BYTES_PER_LINE);
     539:     while (pThis < buffer) {
     540:         fprintf(NetTrace, "%02x", (*pThis)&0xff);
     541:         pThis++;
     542:     }
     543:     fprintf(NetTrace, "\n");
     544:     length -= BYTES_PER_LINE;
     545:     offset += BYTES_PER_LINE;
     546:     if (length < 0) {
     547:         return;
     548:     }
     549:     /* find next unique line */
     550:     }
     551: }
     552:
     553:
     554: /*VARARGS*/
     555: printoption(direction, fmt, option, what)
     556:     char *direction, *fmt;
     557:     int option, what;
     558: {
     559:     if (!showoptions)
     560:         return;
     561:     printf("%s ", direction+1);
     562:     if (fmt == doopt)
     563:         fmt = "do";
     564:     else if (fmt == dont)
     565:         fmt = "dont";
     566:     else if (fmt == will)
     567:         fmt = "will";
     568:     else if (fmt == wont)
     569:         fmt = "wont";
     570:     else
     571:         fmt = "???";
     572:     if (option < (sizeof telopts/sizeof telopts[0]))
     573:         printf("%s %s", fmt, telopts[option]);
     574:     else
     575:         printf("%s %d", fmt, option);
     576:     if (*direction == '<') {
     577:         printf("\r\n");
     578:         return;
     579:     }
     580:     printf(" (%s)\r\n", what ? "reply" : "don't reply");
     581: }
     582:
     583: /*
     584:  * Mode - set up terminal to a specific mode.
     585:  */
     586:
     587:
     588: mode(f)
     589:     register int f;
     590: {
     591:     static int prevmode = 0;
     592:     struct tchars *tc;
     593:     struct ltchars *ltc;
     594:     struct sgttyb sb;
     595:     int onoff, old;
     596:     struct  tchars notc2;
     597:     struct  ltchars noltc2;
     598:     static struct   tchars notc =   { -1, -1, -1, -1, -1, -1 };
     599:     static struct   ltchars noltc = { -1, -1, -1, -1, -1, -1 };
     600:
     601:     globalmode = f;
     602:     if (prevmode == f)
     603:         return;
     604:     old = prevmode;
     605:     prevmode = f;
     606:     sb = nttyb;
     607:     switch (f) {
     608:
     609:     case 0:
     610:         onoff = 0;
     611:         tc = &otc;
     612:         ltc = &oltc;
     613:         break;
     614:
     615:     case 1:     /* remote character processing, remote echo */
     616:     case 2:     /* remote character processing, local echo */
     617:         sb.sg_flags |= CBREAK;
     618:         if (f == 1)
     619:             sb.sg_flags &= ~(ECHO|CRMOD);
     620:         else
     621:             sb.sg_flags |= ECHO|CRMOD;
     622:         sb.sg_erase = sb.sg_kill = -1;
     623:         tc = &notc;
     624:         /*
     625: 		 * If user hasn't specified one way or the other,
     626: 		 * then default to not trapping signals.
     627: 		 */
     628:         if (!donelclchars) {
     629:             localchars = 0;
     630:         }
     631:         if (localchars) {
     632:             notc2 = notc;
     633:             notc2.t_intrc = ntc.t_intrc;
     634:             notc2.t_quitc = ntc.t_quitc;
     635:             tc = &notc2;
     636:         } else
     637:             tc = &notc;
     638:         ltc = &noltc;
     639:         onoff = 1;
     640:         break;
     641:     case 3:     /* local character processing, remote echo */
     642:     case 4:     /* local character processing, local echo */
     643:     case 5:     /* local character processing, no echo */
     644:         sb.sg_flags &= ~CBREAK;
     645:         sb.sg_flags |= CRMOD;
     646:         if (f == 4)
     647:             sb.sg_flags |= ECHO;
     648:         else
     649:             sb.sg_flags &= ~ECHO;
     650:         notc2 = ntc;
     651:         tc = &notc2;
     652:         noltc2 = oltc;
     653:         ltc = &noltc2;
     654:         /*
     655: 		 * If user hasn't specified one way or the other,
     656: 		 * then default to trapping signals.
     657: 		 */
     658:         if (!donelclchars) {
     659:             localchars = 1;
     660:         }
     661:         if (localchars) {
     662:             notc2.t_brkc = nltc.t_flushc;
     663:             noltc2.t_flushc = -1;
     664:         } else {
     665:             notc2.t_intrc = notc2.t_quitc = -1;
     666:         }
     667:         noltc2.t_suspc = escape;
     668:         noltc2.t_dsuspc = -1;
     669:         onoff = 1;
     670:         break;
     671:
     672:     default:
     673:         return;
     674:     }
     675:     ioctl(fileno(stdin), TIOCSLTC, (char *)ltc);
     676:     ioctl(fileno(stdin), TIOCSETC, (char *)tc);
     677:     ioctl(fileno(stdin), TIOCSETP, (char *)&sb);
     678:     ioctl(fileno(stdin), FIONBIO, (char *)&onoff);
     679:     ioctl(fileno(stdout), FIONBIO, (char *)&onoff);
     680:     if (f >= 3)
     681:         signal(SIGTSTP, doescape);
     682:     else if (old >= 3) {
     683:         signal(SIGTSTP, SIG_DFL);
     684:         sigsetmask(sigblock(0L) & ~(1L<<(SIGTSTP-1)));
     685:     }
     686: }
     687:
     688: /*
     689:  * These routines decides on what the mode should be (based on the values
     690:  * of various global variables).
     691:  */
     692:
     693: char *modedescriptions[] = {
     694:     "telnet command mode",                  /* 0 */
     695:     "character-at-a-time mode",             /* 1 */
     696:     "character-at-a-time mode (local echo)",        /* 2 */
     697:     "line-by-line mode (remote echo)",          /* 3 */
     698:     "line-by-line mode",                    /* 4 */
     699:     "line-by-line mode (local echoing suppressed)",     /* 5 */
     700: };
     701:
     702: getconnmode()
     703: {
     704:     static char newmode[8] = { 4, 5, 3, 3, 2, 2, 1, 1 };
     705:     int modeindex = 0;
     706:
     707:     if (hisopts[TELOPT_ECHO]) {
     708:     modeindex += 2;
     709:     }
     710:     if (hisopts[TELOPT_SGA]) {
     711:     modeindex += 4;
     712:     }
     713:     if (dontlecho && (clocks.echotoggle > clocks.modenegotiated)) {
     714:     modeindex += 1;
     715:     }
     716:     return newmode[modeindex];
     717: }
     718:
     719: setconnmode()
     720: {
     721:     mode(getconnmode());
     722: }
     723:
     724:
     725: setcommandmode()
     726: {
     727:     mode(0);
     728: }
     729:
     730: char    sibuf[BUFSIZ], *sbp;
     731: char    tibuf[BUFSIZ], *tbp;
     732: int scc, tcc;
     733:
     734:
     735: /*
     736:  * Select from tty and network...
     737:  */
     738: telnet()
     739: {
     740:     register int c;
     741:     int tin = fileno(stdin);
     742:     int on = 1;
     743:     fd_set ibits, obits, xbits;
     744:
     745:     tout = fileno(stdout);
     746:     setconnmode();
     747:     scc = 0;
     748:     tcc = 0;
     749:     FD_ZERO(&ibits);
     750:     FD_ZERO(&obits);
     751:     FD_ZERO(&xbits);
     752:
     753:     ioctl(net, FIONBIO, (char *)&on);
     754: #if defined(SO_OOBINLINE)
     755:     setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);
     756: #endif	/* defined(SO_OOBINLINE) */
     757:     if (telnetport) {
     758:         if (!hisopts[TELOPT_SGA]) {
     759:         willoption(TELOPT_SGA, 0);
     760:         }
     761:         if (!myopts[TELOPT_TTYPE]) {
     762:         dooption(TELOPT_TTYPE, 0);
     763:         }
     764:     }
     765:     for (;;) {
     766:         if (scc < 0 && tcc < 0) {
     767:             break;
     768:         }
     769:
     770:         if (((globalmode < 4) || flushline) && NETBYTES()) {
     771:             FD_SET(net, &obits);
     772:         } else {
     773:             FD_SET(tin, &ibits);
     774:         }
     775:         if (TTYBYTES()) {
     776:             FD_SET(tout, &obits);
     777:         } else {
     778:             FD_SET(net, &ibits);
     779:         }
     780:         if (!SYNCHing) {
     781:             FD_SET(net, &xbits);
     782:         }
     783:         if ((c = select(16, &ibits, &obits, &xbits,
     784:                         (struct timeval *)0)) < 1) {
     785:             if (c == -1) {
     786:                 /*
     787: 				 * we can get EINTR if we are in line mode,
     788: 				 * and the user does an escape (TSTP), or
     789: 				 * some other signal generator.
     790: 				 */
     791:                 if (errno == EINTR) {
     792:                     continue;
     793:                 }
     794:             }
     795:             sleep(5);
     796:             continue;
     797:         }
     798:
     799:         /*
     800: 		 * Any urgent data?
     801: 		 */
     802:         if (FD_ISSET(net, &xbits)) {
     803:             FD_CLR(net, &xbits);
     804:             SYNCHing = 1;
     805:             ttyflush(); /* flush already enqueued data */
     806:         }
     807:
     808:         /*
     809: 		 * Something to read from the network...
     810: 		 */
     811:         if (FD_ISSET(net, &ibits)) {
     812:             int canread;
     813:
     814:             FD_CLR(net, &ibits);
     815:             if (scc == 0) {
     816:                 sbp = sibuf;
     817:             }
     818:             canread = sibuf + sizeof sibuf - sbp;
     819: #if !defined(SO_OOBINLINE)
     820:             /*
     821: 			 * In 4.2 (and some early 4.3) systems, the
     822: 			 * OOB indication and data handling in the kernel
     823: 			 * is such that if two separate TCP Urgent requests
     824: 			 * come in, one byte of TCP data will be overlaid.
     825: 			 * This is fatal for Telnet, but we try to live
     826: 			 * with it.
     827: 			 *
     828: 			 * In addition, in 4.2 (and...), a special protocol
     829: 			 * is needed to pick up the TCP Urgent data in
     830: 			 * the correct sequence.
     831: 			 *
     832: 			 * What we do is:  if we think we are in urgent
     833: 			 * mode, we look to see if we are "at the mark".
     834: 			 * If we are, we do an OOB receive.  If we run
     835: 			 * this twice, we will do the OOB receive twice,
     836: 			 * but the second will fail, since the second
     837: 			 * time we were "at the mark", but there wasn't
     838: 			 * any data there (the kernel doesn't reset
     839: 			 * "at the mark" until we do a normal read).
     840: 			 * Once we've read the OOB data, we go ahead
     841: 			 * and do normal reads.
     842: 			 *
     843: 			 * There is also another problem, which is that
     844: 			 * since the OOB byte we read doesn't put us
     845: 			 * out of OOB state, and since that byte is most
     846: 			 * likely the TELNET DM (data mark), we would
     847: 			 * stay in the TELNET SYNCH (SYNCHing) state.
     848: 			 * So, clocks to the rescue.  If we've "just"
     849: 			 * received a DM, then we test for the
     850: 			 * presence of OOB data when the receive OOB
     851: 			 * fails (and AFTER we did the normal mode read
     852: 			 * to clear "at the mark").
     853: 			 */
     854:             if (SYNCHing) {
     855:             int atmark;
     856:
     857:             ioctl(net, SIOCATMARK, (char *)&atmark);
     858:             if (atmark) {
     859:                 c = recv(net, sibuf, canread, MSG_OOB);
     860:                 if ((c == -1) && (errno == EINVAL)) {
     861:                 c = read(net, sibuf, canread);
     862:                 if (clocks.didnetreceive < clocks.gotDM) {
     863:                     SYNCHing = stilloob(net);
     864:                 }
     865:                 }
     866:             } else {
     867:                 c = read(net, sibuf, canread);
     868:             }
     869:             } else {
     870:             c = read(net, sibuf, canread);
     871:             }
     872:             settimer(didnetreceive);
     873: #else   /* !defined(SO_OOBINLINE) */
     874:             c = read(net, sbp, canread);
     875: #endif	/* !defined(SO_OOBINLINE) */
     876:             if (c < 0 && errno == EWOULDBLOCK) {
     877:             c = 0;
     878:             } else if (c <= 0) {
     879:             break;
     880:             }
     881:             if (netdata) {
     882:             Dump('<', sbp, c);
     883:             }
     884:             scc += c;
     885:         }
     886:
     887:         /*
     888: 		 * Something to read from the tty...
     889: 		 */
     890:         if (FD_ISSET(tin, &ibits)) {
     891:             FD_CLR(tin, &ibits);
     892:             if (tcc == 0) {
     893:                 tbp = tibuf;    /* nothing left, reset */
     894:             }
     895:             c = read(tin, tbp, tibuf+sizeof tibuf - tbp);
     896:             if (c < 0 && errno == EWOULDBLOCK) {
     897:                 c = 0;
     898:             } else {
     899:                 /* EOF detection for line mode!!!! */
     900:                 if (c == 0 && globalmode >= 3) {
     901:                     /* must be an EOF... */
     902:                     *tbp = ntc.t_eofc;
     903:                     c = 1;
     904:                 }
     905:                 if (c <= 0) {
     906:                     tcc = c;
     907:                     break;
     908:                 }
     909:             }
     910:             tcc += c;
     911:         }
     912:
     913:         while (tcc > 0) {
     914:             register int sc;
     915:
     916:             if (NETROOM() < 2) {
     917:                 flushline = 1;
     918:                 break;
     919:             }
     920:             c = *tbp++ & 0xff, sc = strip(c), tcc--;
     921:             if (sc == escape) {
     922:                 command(0);
     923:                 tcc = 0;
     924:                 flushline = 1;
     925:                 break;
     926:             } else if ((globalmode >= 4) && (sc == echoc)) {
     927:                 if (tcc > 0 && strip(*tbp) == echoc) {
     928:                     tbp++;
     929:                     tcc--;
     930:                 } else {
     931:                     dontlecho = !dontlecho;
     932:                     settimer(echotoggle);
     933:                     setconnmode();
     934:                     tcc = 0;
     935:                     flushline = 1;
     936:                     break;
     937:                 }
     938:             }
     939:             if (localchars) {
     940:                 if (sc == ntc.t_intrc) {
     941:                     intp();
     942:                     break;
     943:                 } else if (sc == ntc.t_quitc) {
     944:                     sendbrk();
     945:                     break;
     946:                 } else if (sc == nltc.t_flushc) {
     947:                     NET2ADD(IAC, AO);
     948:                     if (autoflush) {
     949:                         doflush();
     950:                     }
     951:                     break;
     952:                 } else if (globalmode > 2) {
     953:                     ;
     954:                 } else if (sc == nttyb.sg_kill) {
     955:                     NET2ADD(IAC, EL);
     956:                     break;
     957:                 } else if (sc == nttyb.sg_erase) {
     958:                     NET2ADD(IAC, EC);
     959:                     break;
     960:                 }
     961:             }
     962:             switch (c) {
     963:             case '\n':
     964:                 /*
     965: 				 * If we are in CRMOD mode (\r ==> \n)
     966: 				 * on our local machine, then probably
     967: 				 * a newline (unix) is CRLF (TELNET).
     968: 				 */
     969:                 if (globalmode >= 3) {
     970:                     NETADD('\r');
     971:                 }
     972:                 NETADD('\n');
     973:                 flushline = 1;
     974:                 break;
     975:             case '\r':
     976:                 NET2ADD('\r', '\0');
     977:                 flushline = 1;
     978:                 break;
     979:             case IAC:
     980:                 NET2ADD(IAC, IAC);
     981:                 break;
     982:             default:
     983:                 NETADD(c);
     984:                 break;
     985:             }
     986:         }
     987:         if (((globalmode < 4) || flushline) &&
     988:             FD_ISSET(net, &obits) && (NETBYTES() > 0)) {
     989:             FD_CLR(net, &obits);
     990:             netflush(net);
     991:         }
     992:         if (scc > 0)
     993:             telrcv();
     994:         if (FD_ISSET(tout, &obits) && (TTYBYTES() > 0)) {
     995:             FD_CLR(tout, &obits);
     996:             ttyflush();
     997:         }
     998:     }
     999:     setcommandmode();
    1000: }
    1001:
    1002: /*
    1003:  * Telnet receiver states for fsm
    1004:  */
    1005: #define TS_DATA     0
    1006: #define TS_IAC      1
    1007: #define TS_WILL     2
    1008: #define TS_WONT     3
    1009: #define TS_DO       4
    1010: #define TS_DONT     5
    1011: #define TS_CR       6
    1012: #define TS_SB       7       /* sub-option collection */
    1013: #define TS_SE       8       /* looking for sub-option end */
    1014:
    1015: telrcv()
    1016: {
    1017:     register int c;
    1018:     static int state = TS_DATA;
    1019:
    1020:     while ((scc > 0) && (TTYROOM() > 2)) {
    1021:         c = *sbp++ & 0xff, scc--;
    1022:         switch (state) {
    1023:
    1024:         case TS_CR:
    1025:             state = TS_DATA;
    1026:             if (c == '\0') {
    1027:                 break;  /* Ignore \0 after CR */
    1028:             } else if (c == '\n') {
    1029:                 if (hisopts[TELOPT_ECHO] && !crmod) {
    1030:                 TTYADD(c);
    1031:                 }
    1032:                 break;
    1033:             }
    1034:             /* Else, fall through */
    1035:
    1036:         case TS_DATA:
    1037:             if (c == IAC) {
    1038:                 state = TS_IAC;
    1039:                 continue;
    1040:             }
    1041:                 /*
    1042: 			     * The 'crmod' hack (see following) is needed
    1043: 			     * since we can't * set CRMOD on output only.
    1044: 			     * Machines like MULTICS like to send \r without
    1045: 			     * \n; since we must turn off CRMOD to get proper
    1046: 			     * input, the mapping is done here (sigh).
    1047: 			     */
    1048:             if (c == '\r') {
    1049:                 if (scc > 0) {
    1050:                     c = *sbp&0xff;
    1051:                     if (c == 0) {
    1052:                         sbp++, scc--;
    1053:                         /* a "true" CR */
    1054:                         TTYADD('\r');
    1055:                     } else if (!hisopts[TELOPT_ECHO] &&
    1056:                                 (c == '\n')) {
    1057:                         sbp++, scc--;
    1058:                         TTYADD('\n');
    1059:                     } else {
    1060:                         TTYADD('\r');
    1061:                         if (crmod) {
    1062:                             TTYADD('\n');
    1063:                         }
    1064:                     }
    1065:                 } else {
    1066:                     state = TS_CR;
    1067:                     TTYADD('\r');
    1068:                     if (crmod) {
    1069:                         TTYADD('\n');
    1070:                     }
    1071:                 }
    1072:             } else {
    1073:                 TTYADD(c);
    1074:             }
    1075:             continue;
    1076:
    1077:         case TS_IAC:
    1078:             switch (c) {
    1079:
    1080:             case WILL:
    1081:                 state = TS_WILL;
    1082:                 continue;
    1083:
    1084:             case WONT:
    1085:                 state = TS_WONT;
    1086:                 continue;
    1087:
    1088:             case DO:
    1089:                 state = TS_DO;
    1090:                 continue;
    1091:
    1092:             case DONT:
    1093:                 state = TS_DONT;
    1094:                 continue;
    1095:
    1096:             case DM:
    1097:                 /*
    1098: 				 * We may have missed an urgent notification,
    1099: 				 * so make sure we flush whatever is in the
    1100: 				 * buffer currently.
    1101: 				 */
    1102:                 SYNCHing = 1;
    1103:                 ttyflush();
    1104:                 SYNCHing = stilloob(net);
    1105:                 settimer(gotDM);
    1106:                 break;
    1107:
    1108:             case NOP:
    1109:             case GA:
    1110:                 break;
    1111:
    1112:             case SB:
    1113:                 SB_CLEAR();
    1114:                 state = TS_SB;
    1115:                 continue;
    1116:
    1117:             default:
    1118:                 break;
    1119:             }
    1120:             state = TS_DATA;
    1121:             continue;
    1122:
    1123:         case TS_WILL:
    1124:             printoption(">RCVD", will, c, !hisopts[c]);
    1125:             if (c == TELOPT_TM) {
    1126:                 if (flushout) {
    1127:                     flushout = 0;
    1128:                 }
    1129:             } else if (!hisopts[c]) {
    1130:                 willoption(c, 1);
    1131:             }
    1132:             state = TS_DATA;
    1133:             continue;
    1134:
    1135:         case TS_WONT:
    1136:             printoption(">RCVD", wont, c, hisopts[c]);
    1137:             if (c == TELOPT_TM) {
    1138:                 if (flushout) {
    1139:                     flushout = 0;
    1140:                 }
    1141:             } else if (hisopts[c]) {
    1142:                 wontoption(c, 1);
    1143:             }
    1144:             state = TS_DATA;
    1145:             continue;
    1146:
    1147:         case TS_DO:
    1148:             printoption(">RCVD", doopt, c, !myopts[c]);
    1149:             if (!myopts[c])
    1150:                 dooption(c);
    1151:             state = TS_DATA;
    1152:             continue;
    1153:
    1154:         case TS_DONT:
    1155:             printoption(">RCVD", dont, c, myopts[c]);
    1156:             if (myopts[c]) {
    1157:                 myopts[c] = 0;
    1158:                 sprintf(nfrontp, wont, c);
    1159:                 nfrontp += sizeof (wont) - 2;
    1160:                 flushline = 1;
    1161:                 setconnmode();  /* set new tty mode (maybe) */
    1162:                 printoption(">SENT", wont, c);
    1163:             }
    1164:             state = TS_DATA;
    1165:             continue;
    1166:         case TS_SB:
    1167:             if (c == IAC) {
    1168:                 state = TS_SE;
    1169:             } else {
    1170:                 SB_ACCUM(c);
    1171:             }
    1172:             continue;
    1173:
    1174:         case TS_SE:
    1175:             if (c != SE) {
    1176:                 if (c != IAC) {
    1177:                     SB_ACCUM(IAC);
    1178:                 }
    1179:                 SB_ACCUM(c);
    1180:                 state = TS_SB;
    1181:             } else {
    1182:                 SB_TERM();
    1183:                 suboption();    /* handle sub-option */
    1184:                 state = TS_DATA;
    1185:             }
    1186:         }
    1187:     }
    1188: }
    1189:
    1190: willoption(option, reply)
    1191:     int option, reply;
    1192: {
    1193:     char *fmt;
    1194:
    1195:     switch (option) {
    1196:
    1197:     case TELOPT_ECHO:
    1198:     case TELOPT_SGA:
    1199:         settimer(modenegotiated);
    1200:         hisopts[option] = 1;
    1201:         fmt = doopt;
    1202:         setconnmode();      /* possibly set new tty mode */
    1203:         break;
    1204:
    1205:     case TELOPT_TM:
    1206:         return;         /* Never reply to TM will's/wont's */
    1207:
    1208:     default:
    1209:         fmt = dont;
    1210:         break;
    1211:     }
    1212:     sprintf(nfrontp, fmt, option);
    1213:     nfrontp += sizeof (dont) - 2;
    1214:     if (reply)
    1215:         printoption(">SENT", fmt, option);
    1216:     else
    1217:         printoption("<SENT", fmt, option);
    1218: }
    1219:
    1220: wontoption(option, reply)
    1221:     int option, reply;
    1222: {
    1223:     char *fmt;
    1224:
    1225:     switch (option) {
    1226:
    1227:     case TELOPT_ECHO:
    1228:     case TELOPT_SGA:
    1229:         settimer(modenegotiated);
    1230:         hisopts[option] = 0;
    1231:         fmt = dont;
    1232:         setconnmode();          /* Set new tty mode */
    1233:         break;
    1234:
    1235:     case TELOPT_TM:
    1236:         return;     /* Never reply to TM will's/wont's */
    1237:
    1238:     default:
    1239:         fmt = dont;
    1240:     }
    1241:     sprintf(nfrontp, fmt, option);
    1242:     nfrontp += sizeof (doopt) - 2;
    1243:     if (reply)
    1244:         printoption(">SENT", fmt, option);
    1245:     else
    1246:         printoption("<SENT", fmt, option);
    1247: }
    1248:
    1249: dooption(option)
    1250:     int option;
    1251: {
    1252:     char *fmt;
    1253:
    1254:     switch (option) {
    1255:
    1256:     case TELOPT_TM:
    1257:         fmt = will;
    1258:         break;
    1259:
    1260:     case TELOPT_TTYPE:      /* terminal type option */
    1261:     case TELOPT_SGA:        /* no big deal */
    1262:         fmt = will;
    1263:         myopts[option] = 1;
    1264:         break;
    1265:
    1266:     case TELOPT_ECHO:       /* We're never going to echo... */
    1267:     default:
    1268:         fmt = wont;
    1269:         break;
    1270:     }
    1271:     sprintf(nfrontp, fmt, option);
    1272:     nfrontp += sizeof (doopt) - 2;
    1273:     printoption(">SENT", fmt, option);
    1274: }
    1275:
    1276: /*
    1277:  * suboption()
    1278:  *
    1279:  *	Look at the sub-option buffer, and try to be helpful to the other
    1280:  * side.
    1281:  *
    1282:  *	Currently we recognize:
    1283:  *
    1284:  *		Terminal type, send request.
    1285:  */
    1286:
    1287: suboption()
    1288: {
    1289:     switch (subbuffer[0]&0xff) {
    1290:     case TELOPT_TTYPE:
    1291:     if ((subbuffer[1]&0xff) != TELQUAL_SEND) {
    1292:         ;
    1293:     } else {
    1294:         char *name;
    1295:         char namebuf[41];
    1296:         char *getenv();
    1297:         int len;
    1298:
    1299:         name = getenv("TERM");
    1300:         if ((name == 0) || ((len = strlen(name)) > 40)) {
    1301:         name = "UNKNOWN";
    1302:         }
    1303:         if ((len + 4+2) < NETROOM()) {
    1304:         strcpy(namebuf, name);
    1305:         upcase(namebuf);
    1306:         sprintf(nfrontp, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
    1307:                     TELQUAL_IS, namebuf, IAC, SE);
    1308:         nfrontp += 4+strlen(namebuf)+2;
    1309:         }
    1310:     }
    1311:
    1312:     default:
    1313:     break;
    1314:     }
    1315: }
    1316:
    1317: /*
    1318:  *	The following are data structures and routines for
    1319:  *	the "send" command.
    1320:  *
    1321:  */
    1322:
    1323: struct sendlist {
    1324:     char    *name;      /* How user refers to it (case independent) */
    1325:     int     what;       /* Character to be sent (<0 ==> special) */
    1326:     char    *help;      /* Help information (0 ==> no help) */
    1327:     int     (*routine)();   /* Routine to perform (for special ops) */
    1328: };
    1329:
    1330: /*ARGSUSED*/
    1331: dosynch(s)
    1332: struct sendlist *s;
    1333: {
    1334:     netclear();         /* clear the path to the network */
    1335:     NET2ADD(IAC, DM);
    1336:     neturg = NETLOC()-1;    /* Some systems are off by one XXX */
    1337: }
    1338:
    1339: doflush()
    1340: {
    1341:     NET2ADD(IAC, DO);
    1342:     NETADD(TELOPT_TM);
    1343:     flushline = 1;
    1344:     flushout = 1;
    1345:     ttyflush();
    1346:     /* do printoption AFTER flush, otherwise the output gets tossed... */
    1347:     printoption("<SENT", doopt, TELOPT_TM);
    1348: }
    1349:
    1350: intp()
    1351: {
    1352:     NET2ADD(IAC, IP);
    1353:     if (autoflush) {
    1354:     doflush();
    1355:     }
    1356:     if (autosynch) {
    1357:     dosynch();
    1358:     }
    1359: }
    1360:
    1361: sendbrk()
    1362: {
    1363:     NET2ADD(IAC, BREAK);
    1364:     if (autoflush) {
    1365:     doflush();
    1366:     }
    1367:     if (autosynch) {
    1368:     dosynch();
    1369:     }
    1370: }
    1371:
    1372:
    1373: #define SENDQUESTION    -1
    1374: #define SENDESCAPE  -3
    1375:
    1376: struct sendlist Sendlist[] = {
    1377:     { "ao", AO, "Send Telnet Abort output" },
    1378:     { "ayt", AYT, "Send Telnet 'Are You There'" },
    1379:     { "brk", BREAK, "Send Telnet Break" },
    1380:     { "ec", EC, "Send Telnet Erase Character" },
    1381:     { "el", EL, "Send Telnet Erase Line" },
    1382:     { "escape", SENDESCAPE, "Send current escape character" },
    1383:     { "ga", GA, "Send Telnet 'Go Ahead' sequence" },
    1384:     { "ip", IP, "Send Telnet Interrupt Process" },
    1385:     { "nop", NOP, "Send Telnet 'No operation'" },
    1386:     { "synch", SYNCH, "Perform Telnet 'Synch operation'", dosynch },
    1387:     { "?", SENDQUESTION, "Display send options" },
    1388:     { 0 }
    1389: };
    1390:
    1391: struct sendlist Sendlist2[] = {     /* some synonyms */
    1392:     { "break", BREAK, 0 },
    1393:
    1394:     { "intp", IP, 0 },
    1395:     { "interrupt", IP, 0 },
    1396:     { "intr", IP, 0 },
    1397:
    1398:     { "help", SENDQUESTION, 0 },
    1399:
    1400:     { 0 }
    1401: };
    1402:
    1403: char **
    1404: getnextsend(name)
    1405: char *name;
    1406: {
    1407:     struct sendlist *c = (struct sendlist *) name;
    1408:
    1409:     return (char **) (c+1);
    1410: }
    1411:
    1412: struct sendlist *
    1413: getsend(name)
    1414: char *name;
    1415: {
    1416:     struct sendlist *sl;
    1417:
    1418:     if (sl = (struct sendlist *)
    1419:                 genget(name, (char **) Sendlist, getnextsend)) {
    1420:     return sl;
    1421:     } else {
    1422:     return (struct sendlist *)
    1423:                 genget(name, (char **) Sendlist2, getnextsend);
    1424:     }
    1425: }
    1426:
    1427: sendcmd(argc, argv)
    1428: int argc;
    1429: char    **argv;
    1430: {
    1431:     int what;       /* what we are sending this time */
    1432:     int count;      /* how many bytes we are going to need to send */
    1433:     int hadsynch;   /* are we going to process a "synch"? */
    1434:     int i;
    1435:     int question = 0;   /* was at least one argument a question */
    1436:     struct sendlist *s; /* pointer to current command */
    1437:
    1438:     if (argc < 2) {
    1439:     printf("need at least one argument for 'send' command\n");
    1440:     printf("'send ?' for help\n");
    1441:     return 0;
    1442:     }
    1443:     /*
    1444:      * First, validate all the send arguments.
    1445:      * In addition, we see how much space we are going to need, and
    1446:      * whether or not we will be doing a "SYNCH" operation (which
    1447:      * flushes the network queue).
    1448:      */
    1449:     count = 0;
    1450:     hadsynch = 0;
    1451:     for (i = 1; i < argc; i++) {
    1452:     s = getsend(argv[i]);
    1453:     if (s == 0) {
    1454:         printf("Unknown send argument '%s'\n'send ?' for help.\n",
    1455:             argv[i]);
    1456:         return 0;
    1457:     } else if (s == Ambiguous(struct sendlist *)) {
    1458:         printf("Ambiguous send argument '%s'\n'send ?' for help.\n",
    1459:             argv[i]);
    1460:         return 0;
    1461:     }
    1462:     switch (s->what) {
    1463:     case SENDQUESTION:
    1464:         break;
    1465:     case SENDESCAPE:
    1466:         count += 1;
    1467:         break;
    1468:     case SYNCH:
    1469:         hadsynch = 1;
    1470:         count += 2;
    1471:         break;
    1472:     default:
    1473:         count += 2;
    1474:         break;
    1475:     }
    1476:     }
    1477:     /* Now, do we have enough room? */
    1478:     if (NETROOM() < count) {
    1479:     printf("There is not enough room in the buffer TO the network\n");
    1480:     printf("to process your request.  Nothing will be done.\n");
    1481:     printf("('send synch' will throw away most data in the network\n");
    1482:     printf("buffer, if this might help.)\n");
    1483:     return 0;
    1484:     }
    1485:     /* OK, they are all OK, now go through again and actually send */
    1486:     for (i = 1; i < argc; i++) {
    1487:     if (!(s = getsend(argv[i]))) {
    1488:         fprintf(stderr, "Telnet 'send' error - argument disappeared!\n");
    1489:         quit();
    1490:         /*NOTREACHED*/
    1491:     }
    1492:     if (s->routine) {
    1493:         (*s->routine)(s);
    1494:     } else {
    1495:         switch (what = s->what) {
    1496:         case SYNCH:
    1497:         dosynch();
    1498:         break;
    1499:         case SENDQUESTION:
    1500:         for (s = Sendlist; s->name; s++) {
    1501:             if (s->help) {
    1502:             printf(s->name);
    1503:             if (s->help) {
    1504:                 printf("\t%s", s->help);
    1505:             }
    1506:             printf("\n");
    1507:             }
    1508:         }
    1509:         question = 1;
    1510:         break;
    1511:         case SENDESCAPE:
    1512:         NETADD(escape);
    1513:         break;
    1514:         default:
    1515:         NET2ADD(IAC, what);
    1516:         break;
    1517:         }
    1518:     }
    1519:     }
    1520:     return !question;
    1521: }
    1522:
    1523: /*
    1524:  * The following are the routines and data structures referred
    1525:  * to by the arguments to the "toggle" command.
    1526:  */
    1527:
    1528: lclchars()
    1529: {
    1530:     donelclchars = 1;
    1531:     return 1;
    1532: }
    1533:
    1534: togdebug()
    1535: {
    1536: #ifndef NOT43
    1537:     if (net > 0 &&
    1538:     setsockopt(net, SOL_SOCKET, SO_DEBUG, (char *)&debug, sizeof(debug))
    1539:                                     < 0) {
    1540:         perror("setsockopt (SO_DEBUG)");
    1541:     }
    1542: #else   NOT43
    1543:     if (debug) {
    1544:     if (net > 0 && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
    1545:         perror("setsockopt (SO_DEBUG)");
    1546:     } else
    1547:     printf("Cannot turn off socket debugging\n");
    1548: #endif	NOT43
    1549:     return 1;
    1550: }
    1551:
    1552:
    1553:
    1554: int togglehelp();
    1555:
    1556: struct togglelist {
    1557:     char    *name;      /* name of toggle */
    1558:     char    *help;      /* help message */
    1559:     int     (*handler)();   /* routine to do actual setting */
    1560:     int     dohelp;     /* should we display help information */
    1561:     int     *variable;
    1562:     char    *actionexplanation;
    1563: };
    1564:
    1565: struct togglelist Togglelist[] = {
    1566:     { "autoflush",
    1567:     "toggle flushing of output when sending interrupt characters",
    1568:         0,
    1569:         1,
    1570:             &autoflush,
    1571:             "flush output when sending interrupt characters" },
    1572:     { "autosynch",
    1573:     "toggle automatic sending of interrupt characters in urgent mode",
    1574:         0,
    1575:         1,
    1576:             &autosynch,
    1577:             "send interrupt characters in urgent mode" },
    1578:     { "crmod",
    1579:     "toggle mapping of received carriage returns",
    1580:         0,
    1581:         1,
    1582:             &crmod,
    1583:             "map carriage return on output" },
    1584:     { "localchars",
    1585:     "toggle local recognition of certain control characters",
    1586:         lclchars,
    1587:         1,
    1588:             &localchars,
    1589:             "recognize certain control characters" },
    1590:     { " ", "", 0, 1 },      /* empty line */
    1591:     { "debug",
    1592:     "(debugging) toggle debugging",
    1593:         togdebug,
    1594:         1,
    1595:             &debug,
    1596:             "turn on socket level debugging" },
    1597:     { "netdata",
    1598:     "(debugging) toggle printing of hexadecimal network data",
    1599:         0,
    1600:         1,
    1601:             &netdata,
    1602:             "print hexadecimal representation of network traffic" },
    1603:     { "options",
    1604:     "(debugging) toggle viewing of options processing",
    1605:         0,
    1606:         1,
    1607:             &showoptions,
    1608:             "show option processing" },
    1609:     { " ", "", 0, 1 },      /* empty line */
    1610:     { "?",
    1611:     "display help information",
    1612:         togglehelp,
    1613:         1 },
    1614:     { "help",
    1615:     "display help information",
    1616:         togglehelp,
    1617:         0 },
    1618:     { 0 }
    1619: };
    1620:
    1621: togglehelp()
    1622: {
    1623:     struct togglelist *c;
    1624:
    1625:     for (c = Togglelist; c->name; c++) {
    1626:     if (c->dohelp) {
    1627:         printf("%s\t%s\n", c->name, c->help);
    1628:     }
    1629:     }
    1630:     return 0;
    1631: }
    1632:
    1633: char **
    1634: getnexttoggle(name)
    1635: char *name;
    1636: {
    1637:     struct togglelist *c = (struct togglelist *) name;
    1638:
    1639:     return (char **) (c+1);
    1640: }
    1641:
    1642: struct togglelist *
    1643: gettoggle(name)
    1644: char *name;
    1645: {
    1646:     return (struct togglelist *)
    1647:             genget(name, (char **) Togglelist, getnexttoggle);
    1648: }
    1649:
    1650: toggle(argc, argv)
    1651: int argc;
    1652: char    *argv[];
    1653: {
    1654:     int retval = 1;
    1655:     char *name;
    1656:     struct togglelist *c;
    1657:
    1658:     if (argc < 2) {
    1659:     fprintf(stderr,
    1660:         "Need an argument to 'toggle' command.  'toggle ?' for help.\n");
    1661:     return 0;
    1662:     }
    1663:     argc--;
    1664:     argv++;
    1665:     while (argc--) {
    1666:     name = *argv++;
    1667:     c = gettoggle(name);
    1668:     if (c == Ambiguous(struct togglelist *)) {
    1669:         fprintf(stderr, "'%s': ambiguous argument ('toggle ?' for help).\n",
    1670:                     name);
    1671:         return 0;
    1672:     } else if (c == 0) {
    1673:         fprintf(stderr, "'%s': unknown argument ('toggle ?' for help).\n",
    1674:                     name);
    1675:         return 0;
    1676:     } else {
    1677:         if (c->variable) {
    1678:         *c->variable = !*c->variable;       /* invert it */
    1679:         printf("%s %s.\n", *c->variable? "Will" : "Won't",
    1680:                             c->actionexplanation);
    1681:         }
    1682:         if (c->handler) {
    1683:         retval &= (*c->handler)(c);
    1684:         }
    1685:     }
    1686:     }
    1687:     return retval;
    1688: }
    1689:
    1690: /*
    1691:  * The following perform the "set" command.
    1692:  */
    1693:
    1694: struct setlist {
    1695:     char *name;             /* name */
    1696:     char *help;             /* help information */
    1697:     char *charp;            /* where it is located at */
    1698: };
    1699:
    1700: struct setlist Setlist[] = {
    1701:     { "echo",   "character to toggle local echoing on/off", &echoc },
    1702:     { "escape", "character to escape back to telnet command mode", &escape },
    1703:     { " ", "" },
    1704:     { " ", "The following need 'localchars' to be toggled true", 0 },
    1705:     { "erase",  "character to cause an Erase Character", &nttyb.sg_erase },
    1706:     { "flushoutput", "character to cause an Abort Oubput", &nltc.t_flushc },
    1707:     { "interrupt", "character to cause an Interrupt Process", &ntc.t_intrc },
    1708:     { "kill",   "character to cause an Erase Line", &nttyb.sg_kill },
    1709:     { "quit",   "character to cause a Break", &ntc.t_quitc },
    1710:     { "eof",    "character to cause an EOF ", &ntc.t_eofc },
    1711:     { 0 }
    1712: };
    1713:
    1714: char **
    1715: getnextset(name)
    1716: char *name;
    1717: {
    1718:     struct setlist *c = (struct setlist *)name;
    1719:
    1720:     return (char **) (c+1);
    1721: }
    1722:
    1723: struct setlist *
    1724: getset(name)
    1725: char *name;
    1726: {
    1727:     return (struct setlist *) genget(name, (char **) Setlist, getnextset);
    1728: }
    1729:
    1730: setcmd(argc, argv)
    1731: int argc;
    1732: char    *argv[];
    1733: {
    1734:     int value;
    1735:     struct setlist *ct;
    1736:
    1737:     /* XXX back we go... sigh */
    1738:     if (argc != 3) {
    1739:     if ((argc == 2) &&
    1740:             ((!strcmp(argv[1], "?")) || (!strcmp(argv[1], "help")))) {
    1741:         for (ct = Setlist; ct->name; ct++) {
    1742:         printf("%s\t%s\n", ct->name, ct->help);
    1743:         }
    1744:         printf("?\tdisplay help information\n");
    1745:     } else {
    1746:         printf("Format is 'set Name Value'\n'set ?' for help.\n");
    1747:     }
    1748:     return 0;
    1749:     }
    1750:
    1751:     ct = getset(argv[1]);
    1752:     if (ct == 0) {
    1753:     fprintf(stderr, "'%s': unknown argument ('set ?' for help).\n",
    1754:             argv[1]);
    1755:     return 0;
    1756:     } else if (ct == Ambiguous(struct setlist *)) {
    1757:     fprintf(stderr, "'%s': ambiguous argument ('set ?' for help).\n",
    1758:             argv[1]);
    1759:     return 0;
    1760:     } else {
    1761:     if (strcmp("off", argv[2])) {
    1762:         value = special(argv[2]);
    1763:     } else {
    1764:         value = -1;
    1765:     }
    1766:     *(ct->charp) = value;
    1767:     printf("%s character is '%s'.\n", ct->name, control(*(ct->charp)));
    1768:     }
    1769:     return 1;
    1770: }
    1771:
    1772: /*
    1773:  * The following are the data structures and routines for the
    1774:  * 'mode' command.
    1775:  */
    1776:
    1777: dolinemode()
    1778: {
    1779:     if (hisopts[TELOPT_SGA]) {
    1780:     wontoption(TELOPT_SGA, 0);
    1781:     }
    1782:     if (hisopts[TELOPT_ECHO]) {
    1783:     wontoption(TELOPT_ECHO, 0);
    1784:     }
    1785: }
    1786:
    1787: docharmode()
    1788: {
    1789:     if (!hisopts[TELOPT_SGA]) {
    1790:     willoption(TELOPT_SGA, 0);
    1791:     }
    1792:     if (!hisopts[TELOPT_ECHO]) {
    1793:     willoption(TELOPT_ECHO, 0);
    1794:     }
    1795: }
    1796:
    1797: struct cmd Modelist[] = {
    1798:     { "character",  "character-at-a-time mode", docharmode, 1, 1 },
    1799:     { "line",       "line-by-line mode",        dolinemode, 1, 1 },
    1800:     { 0 },
    1801: };
    1802:
    1803: char **
    1804: getnextmode(name)
    1805: char *name;
    1806: {
    1807:     struct cmd *c = (struct cmd *) name;
    1808:
    1809:     return (char **) (c+1);
    1810: }
    1811:
    1812: struct cmd *
    1813: getmodecmd(name)
    1814: char *name;
    1815: {
    1816:     return (struct cmd *) genget(name, (char **) Modelist, getnextmode);
    1817: }
    1818:
    1819: modecmd(argc, argv)
    1820: int argc;
    1821: char    *argv[];
    1822: {
    1823:     struct cmd *mt;
    1824:
    1825:     if ((argc != 2) || !strcmp(argv[1], "?") || !strcmp(argv[1], "help")) {
    1826:     printf("format is:  'mode Mode', where 'Mode' is one of:\n\n");
    1827:     for (mt = Modelist; mt->name; mt++) {
    1828:         printf("%s\t%s\n", mt->name, mt->help);
    1829:     }
    1830:     return 0;
    1831:     }
    1832:     mt = getmodecmd(argv[1]);
    1833:     if (mt == 0) {
    1834:     fprintf(stderr, "Unknown mode '%s' ('mode ?' for help).\n", argv[1]);
    1835:     return 0;
    1836:     } else if (mt == Ambiguous(struct cmd *)) {
    1837:     fprintf(stderr, "Ambiguous mode '%s' ('mode ?' for help).\n", argv[1]);
    1838:     return 0;
    1839:     } else {
    1840:     (*mt->handler)();
    1841:     }
    1842:     return 1;
    1843: }
    1844:
    1845: /*
    1846:  * The following data structures and routines implement the
    1847:  * "display" command.
    1848:  */
    1849:
    1850: display(argc, argv)
    1851: int argc;
    1852: char    *argv[];
    1853: {
    1854: #define dotog(tl)   if (tl->variable && tl->actionexplanation) { \
    1855:                 if (*tl->variable) { \
    1856:                 printf("will"); \
    1857:                 } else { \
    1858:                 printf("won't"); \
    1859:                 } \
    1860:                 printf(" %s.\n", tl->actionexplanation); \
    1861:             }
    1862:
    1863: #define doset(sl)   if (sl->name && *sl->name != ' ') { \
    1864:             printf("[%s]\t%s.\n", control(*sl->charp), sl->name); \
    1865:             }
    1866:
    1867:     struct togglelist *tl;
    1868:     struct setlist *sl;
    1869:
    1870:     if (argc == 1) {
    1871:     for (tl = Togglelist; tl->name; tl++) {
    1872:         dotog(tl);
    1873:     }
    1874:     printf("\n");
    1875:     for (sl = Setlist; sl->name; sl++) {
    1876:         doset(sl);
    1877:     }
    1878:     } else {
    1879:     int i;
    1880:
    1881:     for (i = 1; i < argc; i++) {
    1882:         sl = getset(argv[i]);
    1883:         tl = gettoggle(argv[i]);
    1884:         if ((sl == Ambiguous(struct setlist *)) ||
    1885:                 (tl == Ambiguous(struct togglelist *))) {
    1886:         printf("?Ambiguous argument '%s'.\n", argv[i]);
    1887:         return 0;
    1888:         } else if (!sl && !tl) {
    1889:         printf("?Unknown argument '%s'.\n", argv[i]);
    1890:         return 0;
    1891:         } else {
    1892:         if (tl) {
    1893:             dotog(tl);
    1894:         }
    1895:         if (sl) {
    1896:             doset(sl);
    1897:         }
    1898:         }
    1899:     }
    1900:     }
    1901:     return 1;
    1902: #undef  doset(sl)
    1903: #undef  dotog(tl)
    1904: }
    1905:
    1906: /*
    1907:  * The following are the data structures, and many of the routines,
    1908:  * relating to command processing.
    1909:  */
    1910:
    1911: /*
    1912:  * Set the escape character.
    1913:  */
    1914: setescape(argc, argv)
    1915:     int argc;
    1916:     char *argv[];
    1917: {
    1918:     register char *arg;
    1919:     char buf[50];
    1920:
    1921:     printf(
    1922:         "Deprecated usage - please use 'set escape%s%s' in the future.\n",
    1923:                 (argc > 2)? " ":"", (argc > 2)? argv[1]: "");
    1924:     if (argc > 2)
    1925:         arg = argv[1];
    1926:     else {
    1927:         printf("new escape character: ");
    1928:         gets(buf);
    1929:         arg = buf;
    1930:     }
    1931:     if (arg[0] != '\0')
    1932:         escape = arg[0];
    1933:     printf("Escape character is '%s'.\n", control(escape));
    1934:     fflush(stdout);
    1935:     return 1;
    1936: }
    1937:
    1938: /*VARARGS*/
    1939: togcrmod()
    1940: {
    1941:     crmod = !crmod;
    1942:     printf("Deprecated usage - please use 'toggle crmod' in the future.\n");
    1943:     printf("%s map carriage return on output.\n", crmod ? "Will" : "Won't");
    1944:     fflush(stdout);
    1945:     return 1;
    1946: }
    1947:
    1948: /*VARARGS*/
    1949: suspend()
    1950: {
    1951:     setcommandmode();
    1952:     kill(0, SIGTSTP);
    1953:     /* reget parameters in case they were changed */
    1954:     ioctl(0, TIOCGETP, (char *)&ottyb);
    1955:     ioctl(0, TIOCGETC, (char *)&otc);
    1956:     ioctl(0, TIOCGLTC, (char *)&oltc);
    1957:     return 1;
    1958: }
    1959:
    1960: /*VARARGS*/
    1961: bye()
    1962: {
    1963:     register char *op;
    1964:
    1965:     if (connected) {
    1966:         shutdown(net, 2);
    1967:         printf("Connection closed.\n");
    1968:         close(net);
    1969:         connected = 0;
    1970:         /* reset his options */
    1971:         for (op = hisopts; op < &hisopts[256]; op++)
    1972:             *op = 0;
    1973:         /* reset our options */
    1974:         bzero(myopts, 256);
    1975:     }
    1976:     return 0;
    1977: }
    1978:
    1979: /*VARARGS*/
    1980: quit()
    1981: {
    1982:     (void) call(bye, "bye", 0);
    1983:     exit(0);
    1984:     /*NOTREACHED*/
    1985: }
    1986:
    1987: /*
    1988:  * Print status about the connection.
    1989:  */
    1990: /*ARGSUSED*/
    1991: status(argc, argv)
    1992: int argc;
    1993: char    *argv[];
    1994: {
    1995:     if (connected) {
    1996:     printf("Connected to %s.\n", hostname);
    1997:     if (argc < 2) {
    1998:         printf("Operating in %s.\n", modedescriptions[getconnmode()]);
    1999:         if (localchars) {
    2000:         printf("Catching signals locally.\n");
    2001:         }
    2002:     }
    2003:     } else {
    2004:     printf("No connection.\n");
    2005:     }
    2006:     printf("Escape character is '%s'.\n", control(escape));
    2007:     fflush(stdout);
    2008:     return 1;
    2009: }
    2010:
    2011: tn(argc, argv)
    2012:     int argc;
    2013:     char *argv[];
    2014: {
    2015:     register struct hostent *host = 0;
    2016:
    2017:     if (connected) {
    2018:         printf("?Already connected to %s\n", hostname);
    2019:         return 0;
    2020:     }
    2021:     if (argc < 2) {
    2022:         (void) strcpy(line, "Connect ");
    2023:         printf("(to) ");
    2024:         gets(&line[strlen(line)]);
    2025:         makeargv();
    2026:         argc = margc;
    2027:         argv = margv;
    2028:     }
    2029:     if (argc > 3) {
    2030:         printf("usage: %s host-name [port]\n", argv[0]);
    2031:         return 0;
    2032:     }
    2033:     sin.sin_addr.s_addr = inet_addr(argv[1]);
    2034:     if (sin.sin_addr.s_addr != -1) {
    2035:         sin.sin_family = AF_INET;
    2036:         (void) strcpy(hnamebuf, argv[1]);
    2037:         hostname = hnamebuf;
    2038:     } else {
    2039:         host = gethostbyname(argv[1]);
    2040:         if (host) {
    2041:             sin.sin_family = host->h_addrtype;
    2042: #ifndef NOT43
    2043:             bcopy(host->h_addr_list[0], (caddr_t)&sin.sin_addr,
    2044:                 host->h_length);
    2045: #else   NOT43
    2046:             bcopy(host->h_addr, (caddr_t)&sin.sin_addr,
    2047:                 host->h_length);
    2048: #endif	NOT43
    2049:             hostname = host->h_name;
    2050:         } else {
    2051:             printf("%s: unknown host\n", argv[1]);
    2052:             return 0;
    2053:         }
    2054:     }
    2055:     sin.sin_port = sp->s_port;
    2056:     if (argc == 3) {
    2057:         sin.sin_port = atoi(argv[2]);
    2058:         if (sin.sin_port == 0) {
    2059:             sp = getservbyname(argv[2], "tcp");
    2060:             if (sp)
    2061:                 sin.sin_port = sp->s_port;
    2062:             else {
    2063:                 printf("%s: bad port number\n", argv[2]);
    2064:                 return 0;
    2065:             }
    2066:         } else {
    2067:             sin.sin_port = atoi(argv[2]);
    2068:             sin.sin_port = htons(sin.sin_port);
    2069:         }
    2070:         telnetport = 0;
    2071:     } else {
    2072:         telnetport = 1;
    2073:     }
    2074:     signal(SIGINT, intr);
    2075:     signal(SIGQUIT, intr2);
    2076:     signal(SIGPIPE, deadpeer);
    2077:     printf("Trying...\n");
    2078:     do {
    2079:         net = socket(AF_INET, SOCK_STREAM, 0);
    2080:         if (net < 0) {
    2081:             perror("telnet: socket");
    2082:             return 0;
    2083:         }
    2084: #ifndef NOT43
    2085:         if (debug &&
    2086:                 setsockopt(net, SOL_SOCKET, SO_DEBUG,
    2087:                     (char *)&debug, sizeof(debug)) < 0)
    2088: #else   NOT43
    2089:         if (debug && setsockopt(net, SOL_SOCKET, SO_DEBUG, 0, 0) < 0)
    2090: #endif	NOT43
    2091:             perror("setsockopt (SO_DEBUG)");
    2092:
    2093:         if (connect(net, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
    2094: #ifndef NOT43
    2095:             if (host && host->h_addr_list[1]) {
    2096:                 int oerrno = errno;
    2097:
    2098:                 fprintf(stderr,
    2099:                     "telnet: connect to address %s: ",
    2100:                     inet_ntoa(sin.sin_addr));
    2101:                 errno = oerrno;
    2102:                 perror((char *)0);
    2103:                 host->h_addr_list++;
    2104:                 bcopy(host->h_addr_list[0],
    2105:                     (caddr_t)&sin.sin_addr, host->h_length);
    2106:                 fprintf(stderr, "Trying %s...\n",
    2107:                     inet_ntoa(sin.sin_addr));
    2108:                 (void) close(net);
    2109:                 continue;
    2110:             }
    2111: #endif	NOT43
    2112:             perror("telnet: connect");
    2113:             signal(SIGINT, SIG_DFL);
    2114:             signal(SIGQUIT, SIG_DFL);
    2115:             return 0;
    2116:         }
    2117:         connected++;
    2118:     } while (connected == 0);
    2119:     call(status, "status", "notmuch", 0);
    2120:     if (setjmp(peerdied) == 0)
    2121:         telnet();
    2122:     fprintf(stderr, "Connection closed by foreign host.\n");
    2123:     exit(1);
    2124:     /*NOTREACHED*/
    2125: }
    2126:
    2127:
    2128: #define HELPINDENT (sizeof ("connect"))
    2129:
    2130: char    openhelp[] =    "connect to a site";
    2131: char    closehelp[] =   "close current connection";
    2132: char    quithelp[] =    "exit telnet";
    2133: char    zhelp[] =   "suspend telnet";
    2134: char    statushelp[] =  "print status information";
    2135: char    helphelp[] =    "print help information";
    2136: char    sendhelp[] =    "transmit special characters ('send ?' for more)";
    2137: char    sethelp[] =     "set operating parameters ('set ?' for more)";
    2138: char    togglestring[] ="toggle operating parameters ('toggle ?' for more)";
    2139: char    displayhelp[] = "display operating parameters";
    2140: char    modehelp[] =
    2141:         "try to enter line-by-line or character-at-a-time mode";
    2142:
    2143: int help();
    2144:
    2145: struct cmd cmdtab[] = {
    2146:     { "close",  closehelp,  bye,        1, 1 },
    2147:     { "display",    displayhelp,    display,    1, 0 },
    2148:     { "mode",   modehelp,   modecmd,    1, 1 },
    2149:     { "open",   openhelp,   tn,     1, 0 },
    2150:     { "quit",   quithelp,   quit,       1, 0 },
    2151:     { "send",   sendhelp,   sendcmd,    1, 1 },
    2152:     { "set",    sethelp,    setcmd,     1, 0 },
    2153:     { "status", statushelp, status,     1, 0 },
    2154:     { "toggle", togglestring,   toggle,     1, 0 },
    2155:     { "z",      zhelp,      suspend,    1, 0 },
    2156:     { "?",      helphelp,   help,       1, 0 },
    2157:     0
    2158: };
    2159:
    2160: char    crmodhelp[] =   "deprecated command -- use 'toggle crmod' instead";
    2161: char    escapehelp[] =  "deprecated command -- use 'set escape' instead";
    2162:
    2163: struct cmd cmdtab2[] = {
    2164:     { "help",   helphelp,   help,       0, 0 },
    2165:     { "escape", escapehelp, setescape,  1, 0 },
    2166:     { "crmod",  crmodhelp,  togcrmod,   1, 0 },
    2167:     0
    2168: };
    2169:
    2170: /*
    2171:  * Help command.
    2172:  */
    2173: help(argc, argv)
    2174:     int argc;
    2175:     char *argv[];
    2176: {
    2177:     register struct cmd *c;
    2178:
    2179:     if (argc == 1) {
    2180:         printf("Commands may be abbreviated.  Commands are:\n\n");
    2181:         for (c = cmdtab; c->name; c++)
    2182:             if (c->dohelp) {
    2183:                 printf("%-*s\t%s\n", HELPINDENT, c->name,
    2184:                                     c->help);
    2185:             }
    2186:         return 0;
    2187:     }
    2188:     while (--argc > 0) {
    2189:         register char *arg;
    2190:         arg = *++argv;
    2191:         c = getcmd(arg);
    2192:         if (c == Ambiguous(struct cmd *))
    2193:             printf("?Ambiguous help command %s\n", arg);
    2194:         else if (c == (struct cmd *)0)
    2195:             printf("?Invalid help command %s\n", arg);
    2196:         else
    2197:             printf("%s\n", c->help);
    2198:     }
    2199:     return 0;
    2200: }
    2201: /*
    2202:  * Call routine with argc, argv set from args (terminated by 0).
    2203:  * VARARGS2
    2204:  */
    2205: call(routine, args)
    2206:     int (*routine)();
    2207:     char *args;
    2208: {
    2209:     register char **argp;
    2210:     register int argc;
    2211:
    2212:     for (argc = 0, argp = &args; *argp++ != 0; argc++)
    2213:         ;
    2214:     return (*routine)(argc, &args);
    2215: }
    2216:
    2217: makeargv()
    2218: {
    2219:     register char *cp;
    2220:     register char **argp = margv;
    2221:
    2222:     margc = 0;
    2223:     for (cp = line; *cp;) {
    2224:         while (isspace(*cp))
    2225:             cp++;
    2226:         if (*cp == '\0')
    2227:             break;
    2228:         *argp++ = cp;
    2229:         margc += 1;
    2230:         while (*cp != '\0' && !isspace(*cp))
    2231:             cp++;
    2232:         if (*cp == '\0')
    2233:             break;
    2234:         *cp++ = '\0';
    2235:     }
    2236:     *argp++ = 0;
    2237: }
    2238:
    2239: char **
    2240: getnextcmd(name)
    2241: char *name;
    2242: {
    2243:     struct cmd *c = (struct cmd *) name;
    2244:
    2245:     return (char **) (c+1);
    2246: }
    2247:
    2248: struct cmd *
    2249: getcmd(name)
    2250: char *name;
    2251: {
    2252:     struct cmd *cm;
    2253:
    2254:     if (cm = (struct cmd *) genget(name, (char **) cmdtab, getnextcmd)) {
    2255:     return cm;
    2256:     } else {
    2257:     return (struct cmd *) genget(name, (char **) cmdtab2, getnextcmd);
    2258:     }
    2259: }
    2260:
    2261: command(top)
    2262:     int top;
    2263: {
    2264:     register struct cmd *c;
    2265:
    2266:     setcommandmode();
    2267:     if (!top) {
    2268:         putchar('\n');
    2269:     } else {
    2270:         signal(SIGINT, SIG_DFL);
    2271:         signal(SIGQUIT, SIG_DFL);
    2272:     }
    2273:     for (;;) {
    2274:         printf("%s> ", prompt);
    2275:         if (gets(line) == 0) {
    2276:             if (feof(stdin))
    2277:                 quit();
    2278:             break;
    2279:         }
    2280:         if (line[0] == 0)
    2281:             break;
    2282:         makeargv();
    2283:         c = getcmd(margv[0]);
    2284:         if (c == Ambiguous(struct cmd *)) {
    2285:             printf("?Ambiguous command\n");
    2286:             continue;
    2287:         }
    2288:         if (c == 0) {
    2289:             printf("?Invalid command\n");
    2290:             continue;
    2291:         }
    2292:         if (c->needconnect && !connected) {
    2293:             printf("?Need to be connected first.\n");
    2294:             continue;
    2295:         }
    2296:         if ((*c->handler)(margc, margv)) {
    2297:             break;
    2298:         }
    2299:     }
    2300:     if (!top) {
    2301:         if (!connected) {
    2302:             longjmp(toplevel, 1);
    2303:             /*NOTREACHED*/
    2304:         }
    2305:         setconnmode();
    2306:     }
    2307: }
    2308:
    2309: /*
    2310:  * main.  Parse arguments, invoke the protocol or command parser.
    2311:  */
    2312:
    2313:
    2314: main(argc, argv)
    2315:     int argc;
    2316:     char *argv[];
    2317: {
    2318:     sp = getservbyname("telnet", "tcp");
    2319:     if (sp == 0) {
    2320:         fprintf(stderr, "telnet: tcp/telnet: unknown service\n");
    2321:         exit(1);
    2322:     }
    2323:     NetTrace = stdout;
    2324:     ioctl(0, TIOCGETP, (char *)&ottyb);
    2325:     ioctl(0, TIOCGETC, (char *)&otc);
    2326:     ioctl(0, TIOCGLTC, (char *)&oltc);
    2327: #if defined(LNOFLSH)
    2328:     ioctl(0, TIOCLGET, (char *)&autoflush);
    2329:     autoflush = !(autoflush&LNOFLSH);   /* if LNOFLSH, no autoflush */
    2330: #else   /* LNOFLSH */
    2331:     autoflush = 1;
    2332: #endif	/* LNOFLSH */
    2333:     ntc = otc;
    2334:     nltc = oltc;
    2335:     nttyb = ottyb;
    2336:     setbuf(stdin, (char *)0);
    2337:     setbuf(stdout, (char *)0);
    2338:     prompt = argv[0];
    2339:     if (argc > 1 && !strcmp(argv[1], "-d")) {
    2340:         debug = 1;
    2341:         argv++;
    2342:         argc--;
    2343:     }
    2344:     if (argc > 1 && !strcmp(argv[1], "-n")) {
    2345:         argv++;
    2346:         argc--;
    2347:         if (argc > 1) {     /* get file name */
    2348:         NetTrace = fopen(argv[1], "w");
    2349:         argv++;
    2350:         argc--;
    2351:         if (NetTrace == NULL) {
    2352:             NetTrace = stdout;
    2353:         }
    2354:         }
    2355:     }
    2356:     if (argc != 1) {
    2357:         if (setjmp(toplevel) != 0)
    2358:             exit(0);
    2359:         tn(argc, argv);
    2360:     }
    2361:     setjmp(toplevel);
    2362:     for (;;)
    2363:         command(1);
    2364: }

#### Defined functions

Dump defined in line 521; used 2 times

- in line 350, 882

bye defined in line 1961; used 2 times

- in line 1982, 2146

call defined in line 2205; used 2 times

- in line 1982, 2119

command defined in line 2261; used 3 times

- in line 513, 922, 2363

control defined in line 233; used 4 times

- in line 1767, 1864, 1933, 2006

deadpeer defined in line 487; used 1 times

- in line 2076

display defined in line 1850; used 1 times

- in line 2147

docharmode defined in line 1787; used 1 times

- in line 1798

doescape defined in line 511; used 1 times

- in line 681

doflush defined in line 1339; used 3 times

- in line 949, 1354, 1365

dolinemode defined in line 1777; used 1 times

- in line 1799

dooption defined in line 1249; used 2 times

- in line 762, 1150

dosynch defined in line 1331; used 4 times

- in line 1357, 1368, 1386, 1497

genget defined in line 170; used 7 times

- in line 1419-1423(2), 1647, 1727, 1816, 2254-2257(2)

getcmd defined in line 2248; used 3 times

- in line 134, 2191, 2283

getconnmode defined in line 702; used 2 times

- in line 721, 1998

getmodecmd defined in line 1812; used 1 times

- in line 1832

getnextcmd defined in line 2239; used 2 times

- in line 2254-2257(2)

getnextmode defined in line 1803; used 1 times

- in line 1816

getnextsend defined in line 1403; used 2 times

- in line 1419-1423(2)

getnextset defined in line 1714; used 1 times

- in line 1727

getnexttoggle defined in line 1633; used 1 times

- in line 1647

getsend defined in line 1412; used 2 times

- in line 1452, 1487

getset defined in line 1723; used 2 times

- in line 1751, 1882

gettoggle defined in line 1642; used 2 times

- in line 1667, 1883

help defined in line 2173; used 15 times

- in line 93, 1326, 1501-1504(3), 1558, 1627, 1696, 1742, 1828, 2143, 2156, 2164, 2184, 2197

intp defined in line 1350; used 2 times

- in line 496, 941

intr defined in line 493; used 1 times

- in line 2074

intr2 defined in line 503; used 1 times

- in line 2075

lclchars defined in line 1528; used 1 times

- in line 1586

main defined in line 2314; never used
makeargv defined in line 2217; used 2 times

- in line 2025, 2282

mode defined in line 588; used 2 times

- in line 721, 727

modecmd defined in line 1819; used 1 times

- in line 2148

netclear defined in line 418; used 1 times

- in line 1334

netflush defined in line 314; used 1 times

- in line 990

nextitem defined in line 372; used 3 times

- in line 427, 441, 448

printoption defined in line 555; used 11 times

- in line 1124, 1136, 1148, 1155, 1162, 1215-1217(2), 1244-1246(2), 1273, 1347

quit defined in line 1980; used 4 times

- in line 297, 1489, 2150, 2277

sendbrk defined in line 1361; used 2 times

- in line 506, 944

sendcmd defined in line 1427; used 1 times

- in line 2151

setcmd defined in line 1730; used 1 times

- in line 2152

setcommandmode defined in line 725; used 6 times

- in line 340, 489, 499, 999, 1951, 2266

setconnmode defined in line 719; used 6 times

- in line 746, 933, 1161, 1202, 1232, 2305

setescape defined in line 1914; used 1 times

- in line 2165

special defined in line 207; used 1 times

- in line 1762

status defined in line 1991; used 2 times

- in line 2119, 2153

stilloob defined in line 281; used 2 times

- in line 863, 1104

suboption defined in line 1287; used 1 times

- in line 1183

suspend defined in line 1949; used 1 times

- in line 2155

telnet defined in line 738; used 1 times

- in line 2121

telrcv defined in line 1015; used 1 times

- in line 993

tn defined in line 2011; used 2 times

- in line 2149, 2359

togcrmod defined in line 1939; used 1 times

- in line 2166

togdebug defined in line 1534; used 1 times

- in line 1593

toggle defined in line 1650; used 1 times

- in line 2154

togglehelp defined in line 1621; used 3 times

- in line 1554, 1612-1616(2)

ttyflush defined in line 462; used 4 times

- in line 805, 996, 1103, 1345

upcase defined in line 262; used 1 times

- in line 1305

willoption defined in line 1190; used 4 times

- in line 759, 1130, 1790-1793(2)

wontoption defined in line 1220; used 3 times

- in line 1142, 1780-1783(2)

#### Defined variables

Modelist defined in line 1797; used 2 times

- in line 1816, 1827

SYNCHing defined in line 114; used 8 times

- in line 60, 467, 780, 804, 854, 863, 1102-1104(2)

Sendlist defined in line 1376; used 2 times

- in line 1419, 1500

Sendlist2 defined in line 1391; used 1 times

- in line 1423

Setlist defined in line 1700; used 3 times

- in line 1727, 1741, 1875

Togglelist defined in line 1565; used 3 times

- in line 1625, 1647, 1871

ambiguous defined in line 166; used 1 times

- in line 167

autoflush defined in line 116; used 8 times

- in line 948, 1353, 1364, 1570, 2328-2331(4)

autosynch defined in line 117; used 3 times

- in line 1356, 1367, 1576

closehelp defined in line 2131; used 1 times

- in line 2146

cmdtab defined in line 2145; used 2 times

- in line 2181, 2254

cmdtab2 defined in line 2163; used 1 times

- in line 2257

connected defined in line 99; used 8 times

- in line 1965-1969(2), 1995, 2017, 2117-2118(2), 2292, 2301

copyright defined in line 8; never used
crmod defined in line 104; used 7 times

- in line 1029, 1061, 1068, 1582, 1941-1943(3)

crmodhelp defined in line 2160; used 1 times

- in line 2166

debug defined in line 103; used 9 times

- in line 1538-1543(3), 1595, 2085-2089(4), 2340

displayhelp defined in line 2139; used 1 times

- in line 2147

donelclchars defined in line 119; used 3 times

- in line 628, 658, 1530

dont defined in line 87; used 6 times

- in line 564, 1155, 1209-1213(2), 1231, 1239

dontlecho defined in line 120; used 3 times

- in line 713, 931(2)

doopt defined in line 86; used 6 times

- in line 562, 1148, 1201, 1242, 1272, 1347

echoc defined in line 112; used 3 times

- in line 926-927(2), 1701

escape defined in line 111; used 7 times

- in line 667, 921, 1512, 1702, 1932-1933(2), 2006

escapehelp defined in line 2161; used 1 times

- in line 2165

flushline defined in line 141; used 9 times

- in line 770, 917, 924, 935, 973-977(2), 987, 1160, 1343

flushout defined in line 115; used 7 times

- in line 60, 467, 1126-1127(2), 1138-1139(2), 1344

globalmode defined in line 140; used 7 times

- in line 601, 770, 900, 926, 952, 969, 987

helphelp defined in line 2135; used 2 times

- in line 2156, 2164

hisopts defined in line 83; used 17 times

- in line 707-710(2), 758, 1029, 1055, 1124-1129(2), 1136-1141(2), 1200, 1230, 1779-1782(2), 1789-1792(2), 1971(2)

hnamebuf defined in line 144; used 2 times

- in line 2036-2037(2)

hostname defined in line 143; used 5 times

- in line 341, 1996, 2018, 2037, 2049

line defined in line 122; used 6 times

- in line 2022-2024(3), 2223, 2275-2280(2)

localchars defined in line 118; used 9 times

- in line 495, 505, 629-631(2), 659-661(2), 939, 1588, 1999

margc defined in line 123; used 4 times

- in line 2026, 2222, 2229, 2296

margv defined in line 124; used 4 times

- in line 2027, 2220, 2283, 2296

modedescriptions defined in line 693; used 1 times

- in line 1998

modehelp defined in line 2140; used 1 times

- in line 2148

myopts defined in line 84; used 8 times

- in line 761, 1148-1149(2), 1155-1157(3), 1263, 1974

net defined in line 100; used 32 times

- in line 753-755(2), 771, 778-781(2), 802-803(2), 811-814(2), 857-874(7), 988-990(3), 1104, 1537-1538(2), 1544(2), 1966-1968(2), 2079-2080(2), 2086-2093(3), 2108

netdata defined in line 105; used 3 times

- in line 349, 881, 1601

netobuf defined in line 67; used 9 times

- in line 63-71(5), 357, 425, 433, 452

neturg defined in line 74; used 7 times

- in line 319-322(2), 343, 353-354(2), 454, 1336

nfrontp defined in line 67; used 20 times

- in line 68-72(3), 318, 356-357(2), 422, 435, 442, 453, 1158-1159(2), 1212-1213(2), 1241-1242(2), 1271-1272(2), 1306-1308(2)

nltc defined in line 138; used 4 times

- in line 662, 946, 1706, 2334

ntc defined in line 137; used 10 times

- in line 633-634(2), 650, 902, 940-943(2), 1707-1710(3), 2333

nttyb defined in line 139; used 6 times

- in line 606, 954-957(2), 1705-1708(2), 2335

oltc defined in line 138; used 5 times

- in line 612, 652, 1956, 2326, 2334

openhelp defined in line 2130; used 1 times

- in line 2149

otc defined in line 137; used 4 times

- in line 611, 1955, 2325, 2333

ottyb defined in line 139; used 3 times

- in line 1954, 2324, 2335

peerdied defined in line 127; used 3 times

- in line 344, 490, 2120

prompt defined in line 110; used 2 times

- in line 2274, 2338

quithelp defined in line 2132; used 1 times

- in line 2150

sbp defined in line 730; used 8 times

- in line 816-818(2), 874, 882, 1021, 1050-1057(3)

scc defined in line 732; used 10 times

- in line 747, 766, 815, 884, 992, 1020-1021(2), 1049-1057(3)

sccsid defined in line 12; never used
sendhelp defined in line 2136; used 1 times

- in line 2151

sethelp defined in line 2137; used 1 times

- in line 2152

showoptions defined in line 102; used 2 times

- in line 559, 1607

sibuf defined in line 730; used 7 times

- in line 816-818(3), 859-861(2), 867-870(2)

sin defined in line 132; used 18 times

- in line 2033-2035(3), 2041-2046(3), 2055-2061(4), 2067-2068(3), 2093(2), 2100-2107(3)

sp defined in line 135; used 6 times

- in line 2055-2061(4), 2318-2319(2)

statushelp defined in line 2134; used 1 times

- in line 2153

subbuffer defined in line 76; used 5 times

- in line 77-79(3), 1289-1291(2)

subend defined in line 76; used 1 times

- in line 78

subpointer defined in line 76; used 4 times

- in line 77-80(4)

tbp defined in line 731; used 7 times

- in line 893-895(3), 902, 920, 927-928(2)

tcc defined in line 732; used 11 times

- in line 748, 766, 892, 906-913(3), 920-934(5)

telnetport defined in line 107; used 3 times

- in line 757, 2070-2072(2)

tfrontp defined in line 59; used 6 times

- in line 60-64(3), 466, 478-479(2)

tibuf defined in line 731; used 3 times

- in line 893-895(3)

togglestring defined in line 2138; used 1 times

- in line 2154

toplevel defined in line 126; used 4 times

- in line 500, 2302, 2357-2361(2)

tout defined in line 101; used 5 times

- in line 468, 745, 776, 994-995(2)

ttyobuf defined in line 59; used 5 times

- in line 59-62(4), 479

will defined in line 88; used 4 times

- in line 566, 1124, 1257-1262(2)

wont defined in line 89; used 6 times

- in line 568, 1136, 1158-1162(3), 1268

zhelp defined in line 2133; used 1 times

- in line 2155

#### Defined struct's

cmd defined in line 91; used 42 times

- in line 134(2), 1797(2), 1807-1816(8), 1823(2), 1836(2), 2145(2), 2163(2), 2177(2), 2192-2194(4), 2243-2257(12), 2264(2), 2284(2)

sendlist defined in line 1323; used 22 times

- in line 1332(2), 1376(2), 1391(2), 1407-1422(12), 1436(2), 1457(2)

setlist defined in line 1694; used 18 times

- in line 1700(2), 1718-1727(8), 1735(2), 1756(2), 1868(2), 1884(2)

togglelist defined in line 1556; used 20 times

- in line 1565(2), 1623(2), 1637-1646(8), 1656(2), 1668(2), 1867(2), 1885(2)

#### Defined macros

Ambiguous defined in line 167; used 9 times

- in line 197, 1457, 1668, 1756, 1836, 1884-1885(2), 2192, 2284

BYTES_PER_LINE defined in line 527; used 3 times

- in line 538, 544-545(2)

FD_CLR defined in line 51; used 5 times

- in line 803, 814, 891, 989, 995

FD_ISSET defined in line 52; used 6 times

- in line 299, 802, 811, 890, 988, 994

FD_SET defined in line 50; used 6 times

- in line 291, 771-781(5)

FD_ZERO defined in line 53; used 4 times

- in line 290, 749-751(3)

HELPINDENT defined in line 2128; used 1 times

- in line 2183

NET2ADD defined in line 69; used 10 times

- in line 947, 955-958(2), 976-980(2), 1335, 1341, 1352, 1363, 1515

NETADD defined in line 68; used 7 times

- in line 69(2), 970-972(2), 983, 1342, 1512

NETBYTES defined in line 72; used 2 times

- in line 770, 988

NETLOC defined in line 70; used 2 times

- in line 73, 1336

NETMAX defined in line 71; used 1 times

- in line 73

NETROOM defined in line 73; used 3 times

- in line 916, 1303, 1478

SB_ACCUM defined in line 79; used 3 times

- in line 1170, 1177-1179(2)

SB_CLEAR defined in line 77; used 1 times

- in line 1113

SB_TERM defined in line 78; used 1 times

- in line 1182

SENDESCAPE defined in line 1374; used 1 times

- in line 1382

SENDQUESTION defined in line 1373; used 2 times

- in line 1387, 1398

TELOPTS defined in line 29; never used
TS_CR defined in line 1011; used 1 times

- in line 1066

TS_DATA defined in line 1005; used 8 times

- in line 1018, 1025, 1120, 1132, 1144, 1151, 1164, 1184

TS_DO defined in line 1009; used 1 times

- in line 1089

TS_DONT defined in line 1010; used 1 times

- in line 1093

TS_IAC defined in line 1006; used 1 times

- in line 1038

TS_SB defined in line 1012; used 2 times

- in line 1114, 1180

TS_SE defined in line 1013; used 1 times

- in line 1168

TS_WILL defined in line 1007; used 1 times

- in line 1081

TS_WONT defined in line 1008; used 1 times

- in line 1085

TTYADD defined in line 60; used 8 times

- in line 1030, 1054-1073(7)

TTYBYTES defined in line 64; used 2 times

- in line 775, 994

TTYLOC defined in line 61; used 1 times

- in line 65

TTYMAX defined in line 62; used 1 times

- in line 65

TTYMIN defined in line 63; never used
TTYROOM defined in line 65; used 1 times

- in line 1020

doset defined in line 1863; used 3 times

- in line 1876, 1896, 1902

dotog defined in line 1854; used 3 times

- in line 1872, 1893, 1903

min defined in line 528; used 1 times

- in line 538

settimer defined in line 160; used 5 times

- in line 872, 932, 1105, 1199, 1229

strip defined in line 57; used 2 times

- in line 920, 927

wewant defined in line 422; used 2 times

- in line 436, 442

Last modified: 1994-01-11 Generated: 2016-12-26 | Generated by src2html V0.67 page hit count: 5980 |