home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / gopher / Unix / GopherTools / securegopher / telnet / telnet.c next >
Encoding:
C/C++ Source or Header  |  1992-04-24  |  47.4 KB  |  2,386 lines

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