home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / contrib / usr.x25 / nimd / commands.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-04-13  |  14.8 KB  |  777 lines

  1. /*
  2.  * NIM daemon command parser
  3.  *
  4.  * Frank Pronk
  5.  * Copyright (c) 1984
  6.  */
  7.  
  8. #include <sys/types.h>
  9. #include <sys/socket.h>
  10. #include <sys/ioctl.h>
  11. #include <netccitt/x25.h>
  12. #include <netdb.h>
  13.  
  14. #include "../h/x29.h"
  15.  
  16. #include "nim.h"
  17.  
  18. #define    NEXTC    (*sym.s_next ? *sym.s_next++ : (char)0)
  19.  
  20. #define SSET    0200+0
  21. #define SPAR    0200+1
  22. #define SPROF    0200+2
  23. #define SSTATUS    0200+3
  24. #define SRESET    0200+4
  25. #define SINT    0200+5
  26. #define SINTD    0200+6
  27. #define SCLEAR    0200+7
  28. #define SHELP    0200+8
  29. #define SCALL    0200+9
  30. #define SPRI    0200+10
  31. #define SREV    0200+11
  32. #define SNUM    0200+12
  33. #define SSTR    0200+13
  34. #define SNUI    0200+14
  35.  
  36. struct    NIMCommand {
  37.     char    *c_name;
  38.     short    c_value;
  39. } Commands[] = {
  40.     "set",        SSET,
  41.     "par",        SPAR,
  42.     "prof",        SPROF,
  43.     "profile",    SPROF,
  44.     "stat",        SSTATUS,
  45.     "status",    SSTATUS,
  46.     "reset",    SRESET,
  47.     "int",        SINT,
  48.     "intd",        SINTD,
  49.     "clear",    SCLEAR,
  50.     "help",        SHELP,
  51.     "call",        SCALL,
  52.     "p",        SPRI,
  53.     "rev",        SREV,
  54.     "nui",        SNUI,
  55.     0,        0,
  56. };
  57.  
  58. struct    sockaddr_x25 RemoteHostAddr;
  59. char    *RemoteHostName;
  60. char    *Nui;        /* network user identification */
  61.  
  62. /*
  63.  * Structure used by insymbol() keep track of its
  64.  * current position in the command string being
  65.  * parsed and to hold the results of the last
  66.  * call to insymbol()
  67.  */
  68.  
  69. struct    symbol {
  70.     char    *s_start;    /* start of line to be parsed */
  71.     char    *s_next;    /* current position in line */
  72.     short    s_type;        /* see defines above */
  73.     int    s_num;        /* value if current symbol is a number */
  74.     char    *s_str;        /* address of symbol if a string */
  75. } sym;
  76.  
  77. /*
  78.  * List of symbolic names that can be
  79.  * used as replacements for numeric codes
  80.  * in specific "set parameter" requests
  81.  */
  82.  
  83. struct    NameList {
  84.     char    *n_name;
  85.     short    n_value;
  86. } ForwardList[]    = { "cr", 2, "control", 126, "off", 0, 0, 0 },
  87.   OnOffList[]    = { "on", 1, "off", 0, 0, 0},
  88.   LfList[]    = { "on", 4, "off", 0, "none", 0, "local", 4, "remote", 1, "both", 5, 0, 0};
  89.  
  90. /*
  91.  * list of x.29 symbolic keywords that can be substituted
  92.  * for numeric codes.  Note that this list contains only
  93.  * the x.29 parameters that an average user would ever
  94.  * want to change.
  95.  */
  96.  
  97. struct    X29Keyword {
  98.     char    *x_word;    /* parameter name */
  99.     short    x_param;    /* parameter code */
  100.     struct    NameList *x_list;
  101. } X29Keywords[]    = {
  102.     "escape",    X29_ESCAPE_TO_CMD_CODE,        OnOffList,
  103.     "echo",        X29_ECHO_CODE,            OnOffList,
  104.     "forward",    X29_FORWARDING_SIGNAL_CODE,    ForwardList,
  105.     "timer",    X29_IDLE_TIMER_CODE,        OnOffList,
  106.     "break",    X29_BREAK_PROCEDURE_CODE,    0,
  107.     "lf",        X29_LF_AFTER_CR,        LfList,
  108.     "lf-insertion",    X29_LF_AFTER_CR,        LfList,
  109.     "editing",    X29_EDITING,            OnOffList,
  110.     "erase",    X29_CHARACTER_DELETE,        0,
  111.     "kill",        X29_LINE_DELETE,        0,
  112.     "replay",    X29_LINE_DISPLAY,        0,
  113.     "display",    X29_LINE_DISPLAY,        0,
  114.     0,        0,                0
  115. };
  116.  
  117. /*
  118.  * Attempt to parse string pointed to by 'cp'.
  119.  */
  120.  
  121. NimCommand(cp)
  122. register char *cp;
  123. {
  124.  
  125.     sym.s_start = sym.s_next = cp;
  126.     /*
  127.      * strip parity bit from command
  128.      */
  129.     while ((*cp&0177) != '\0')
  130.         *cp++ &= 0177;
  131.     switch(insymbol(0)) {
  132.     case SCLEAR:
  133.         ClearCommand();
  134.         return;
  135.  
  136.     case SHELP:
  137.     case '?':
  138.         HelpCommand();
  139.         return;
  140.  
  141.     case SINT:
  142.         InterruptCommand(0);
  143.         break;
  144.  
  145.     case SINTD:
  146.         InterruptCommand(1);
  147.         return;
  148.  
  149.     case SPAR:
  150.         ParCommand();
  151.         return;
  152.  
  153.     case SPROF:
  154.         ProfileCommand();
  155.         return;
  156.  
  157.     case SRESET:
  158.         ResetCommand();
  159.         return;
  160.  
  161.     case SSET:
  162.         SetCommand();
  163.         return;
  164.  
  165.     case SSTATUS:
  166.         StatusCommand();
  167.         return;
  168.  
  169.     case SPRI:
  170.     case SREV:
  171.     case SNUM:
  172.         X121CallCommand();
  173.         return;
  174.  
  175.     case SCALL:
  176.         CallCommand();
  177.         return;
  178.  
  179.     case SNUI:
  180.         NuiCommand ();
  181.         return;
  182.  
  183.     case '\0':
  184.         NullCommand();
  185.         return;
  186.  
  187.     case '.':
  188.         DotCommand();
  189.         return;
  190.  
  191.     default:
  192.         message ("Unknown NIM command\r");
  193.         return;
  194.     }
  195. }
  196.  
  197. GetNumber(np)
  198. register struct NameList *np;
  199. {
  200.  
  201.     (void) insymbol(0);
  202.     if (sym.s_type == SNUM)
  203.         return (1);
  204.     if (sym.s_type == '(') {
  205.         register int value;
  206.  
  207.         switch (insymbol(1)) {
  208.         case SSTR:
  209.             if (strlen (sym.s_str) != 1)
  210.                 return (0);
  211.             value = *sym.s_str;
  212.             break;
  213.  
  214.         case SNUM:
  215.             value = sym.s_num;
  216.             break;
  217.  
  218.         default:
  219.             if (sym.s_type == '\r')
  220.                 return (0);
  221.             value = sym.s_type;
  222.         }
  223.         (void) insymbol(0);
  224.         if(sym.s_type == ')') {
  225.             sym.s_num = value;
  226.             return(1);
  227.         }
  228.         return(0);
  229.     }
  230.     if (sym.s_type == SSTR && np)
  231.         for (; np->n_name; np++)
  232.             if (strcmp(np->n_name, sym.s_str) == 0) {
  233.                 sym.s_num = np->n_value;
  234.                 return (1);
  235.             }
  236.     return (0);
  237. }
  238.  
  239. /*
  240.  * Save the string pointed to by 's'
  241.  */
  242.  
  243. char *
  244. saves(s)
  245. char *s;
  246. {
  247.     register char *p;
  248.     char *malloc();
  249.  
  250.     p = malloc(strlen(s) + 1);
  251.     strcpy(p, s);
  252.     return (p);
  253. }
  254.  
  255. char
  256. GetEscaped ()
  257. {
  258.     register char c, value;
  259.  
  260.     c = NEXTC;
  261.     if (c < '0' || c > '7')
  262.         return (c);
  263.     value = c - '0';
  264.     c = NEXTC;
  265.     if (c >= '0' && c <= '7') {
  266.         register int n;
  267.  
  268.         value = value*8 + c - '0';
  269.         c = NEXTC;
  270.         if (c >= '0' && c <= '7' && (n = value*8 + c-'0') < 256) {
  271.             value = n;
  272.             c = NEXTC;
  273.         }
  274.     }
  275.     return (value);
  276. }
  277.  
  278. insymbol(special)
  279. {
  280.     register char c, *cp;
  281.     char buf[128];
  282.  
  283.     if (sym.s_str) {
  284.         free(sym.s_str);
  285.         sym.s_str = 0;
  286.     }
  287.  
  288.     cp = buf;
  289.     while ((c = NEXTC) == ' ' || c == '\t');
  290.  
  291.     if (c == '\'' || c == '"') {
  292.         register char quote = c;
  293.  
  294.         while (1) {
  295.             c = NEXTC;
  296.             if (c == '\0')
  297.                 return (sym.s_type = 0);
  298.             if (c == quote)
  299.                 break;
  300.             if (c == '\\')
  301.                 c = GetEscaped ();
  302.             if(cp < buf + sizeof (buf))
  303.                 *cp++ = c;
  304.         }
  305.         *cp = '\0';
  306.         sym.s_type = SSTR;
  307.         sym.s_str = saves (buf);
  308.         return (sym.s_type);
  309.     }
  310.  
  311.     if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '\\') {
  312.         do {
  313.             if (c == '\\')
  314.                 c = GetEscaped ();
  315.             if(cp < buf + sizeof (buf)) {
  316.                 if (!special && c >= 'A' && c <= 'Z')
  317.                     c -= 'A' - 'a';
  318.                 *cp++ = c;
  319.             }
  320.             c = NEXTC;
  321.         } while (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' ||
  322.              c >= 'A' && c <= 'Z' || c == '_' || c == '-');
  323.         *cp = '\0';
  324.         if (c)
  325.             sym.s_next--;
  326.  
  327.         /*
  328.          * set symbol type to SSTR and
  329.          * then look for keywords
  330.          */
  331.  
  332.         sym.s_type = SSTR;
  333.         if (!special) {
  334.             register struct NIMCommand *np;
  335.  
  336.             cp = &buf[0];
  337.             for (np = Commands; np->c_name; np++)
  338.                 if(strcmp(cp, np->c_name) == 0) {
  339.                     sym.s_type = np->c_value;
  340.                     break;
  341.                 }
  342.         }
  343.         if (sym.s_type == SSTR)
  344.             sym.s_str = saves(buf);
  345.         return (sym.s_type);
  346.     }
  347.  
  348.     if (c >= '0' && c <= '9') {
  349.         do {
  350.             if(cp < buf + sizeof (buf))
  351.                 *cp++ = c;
  352.             c = NEXTC;
  353.         } while(c >= '0' && c <= '9');
  354.         *cp = '\0';
  355.         if (c)
  356.             sym.s_next--;
  357.         sym.s_num = atoi(buf);
  358.         sym.s_type = SNUM;
  359.         sym.s_str = saves(buf);
  360.         return (SNUM);
  361.     }
  362.     return (sym.s_type = c);
  363. }
  364.  
  365. /*
  366.  * Does nothing useful - retained for
  367.  * compatibility with Datapac.
  368.  */
  369.  
  370. DotCommand()
  371. {
  372.     message (Banner);
  373. }
  374.  
  375. X121CallCommand()
  376. {
  377.     register int len, havenet = 0;
  378.  
  379.     if (State & ST_DATA) {
  380.         message ("You are connected to %s, please clear this call before attempting another\r",
  381.             RemoteHostName?RemoteHostName:(char *)RemoteHostAddr.x25_addr);
  382.         return;
  383.     }
  384.     bzero ((char *)&RemoteHostAddr, sizeof (RemoteHostAddr));
  385.     if (RemoteHostName) {
  386.         free (RemoteHostName);
  387.         RemoteHostName = (char *)0;
  388.     }
  389.  
  390.     while (sym.s_type == SPRI || sym.s_type == SREV) {
  391.         if (sym.s_type == SPRI)        /* Datapac specific */
  392.             RemoteHostAddr.x25_opts.op_psize = X25_PS128;
  393.         else if (sym.s_type == SREV)
  394.             RemoteHostAddr.x25_opts.op_flags |= X25_REVERSE_CHARGE;
  395.         (void) insymbol(0);
  396.     }
  397. getaddr:
  398.     if (sym.s_type != SNUM) {
  399.         message("Non-numeric destination address\r");
  400.         return;
  401.     }
  402.     if (strlen(sym.s_str) > sizeof (RemoteHostAddr.x25_addr) - 1) {
  403.         message ("Destination address too long\r");
  404.         return;
  405.     }
  406.     strcpy(RemoteHostAddr.x25_addr, sym.s_str);
  407.  
  408.     (void) insymbol(1);
  409.     if (sym.s_type == ':' || sym.s_type == '.') {
  410.         if (havenet) {
  411.             message("Destination address error\r");
  412.             return;
  413.         }
  414.         havenet++;
  415.         RemoteHostAddr.x25_net = atoi(RemoteHostAddr.x25_addr);
  416.         (void) insymbol(1);
  417.         goto getaddr;
  418.     }
  419.  
  420.     if (sym.s_type == ',')        /* skip optional comma */
  421.         (void) insymbol(1);
  422.  
  423.     RemoteHostAddr.x25_udata[0] = ITI_CALL;
  424.     RemoteHostAddr.x25_udlen = 4;
  425.     if (sym.s_type == SSTR || sym.s_type == SNUM) {
  426.         if ((len = strlen(sym.s_str)) > sizeof (RemoteHostAddr.x25_udata) - 4) {
  427.             message ("Userdata field too long\r");
  428.             RemoteHostAddr.x25_addr[0] = '\0';
  429.             return;
  430.         }
  431.         strcpy(RemoteHostAddr.x25_udata + 4, sym.s_str);
  432.         RemoteHostAddr.x25_udlen += len;
  433.         (void) insymbol(1);
  434.     }
  435.  
  436.     if (sym.s_type == ',')        /* skip optional comma */
  437.         (void) insymbol(1);
  438.     if (sym.s_type == SSTR || sym.s_type == SNUM) {    /* protocol id */
  439.         if (strlen(sym.s_str) > 4) {
  440.             message ("Protocol field too long\r");
  441.             RemoteHostAddr.x25_addr[0] = '\0';
  442.             return;
  443.         }
  444.         strcpy(RemoteHostAddr.x25_udata, sym.s_str);
  445.         (void) insymbol(0);
  446.     }
  447.  
  448.     if (sym.s_type != '\0') {    /* still more to come? */
  449.         message ("usage: [options] address [userdata] [protocol id]\r");
  450.         RemoteHostAddr.x25_addr[0] = '\0';
  451.         return;
  452.     }
  453.  
  454.     InitiateSession ();
  455. }
  456.  
  457. CallCommand ()
  458. {
  459.     register struct hostent *hp;
  460.     struct hostent *getx25hostbyname ();
  461.  
  462.     if (insymbol(1) == '\0') {
  463.         if (RemoteHostAddr.x25_addr[0] == '\0') {
  464.             message ("Call who?\r");
  465.             return;
  466.         }
  467.     } else {
  468.         if (sym.s_type != SSTR) {
  469.             message("nimd: usage: call [hostname]\r");
  470.             return;
  471.         }
  472.         if ((hp = getx25hostbyname (sym.s_str)) == 0) {
  473.             message ("nimd: can't find \"%s\" in host table\r",
  474.                 sym.s_str);
  475.             return;
  476.         }
  477.         if (RemoteHostName)
  478.             free (RemoteHostName);
  479.         RemoteHostName = saves (sym.s_str);
  480.         bcopy (hp->h_addr, (char *)&RemoteHostAddr, sizeof (RemoteHostAddr));
  481.         RemoteHostAddr.x25_udata[0] = ITI_CALL;
  482.     }
  483.     InitiateSession ();
  484. }
  485.     
  486. InitiateSession ()
  487. {
  488.     register char *cp;
  489.     struct sockaddr_x25 peer;
  490.     int slen = sizeof (struct sockaddr_x25);
  491.     char buf[256];
  492.     int on = 1;
  493.  
  494. #ifdef waterloo
  495.     extern char user_name[];
  496.  
  497.     if ((RemoteHostAddr.x25_opts.op_flags & X25_REVERSE_CHARGE) == 0 &&
  498.          !(user_name[0] ? x25_can_callin(user_name) : 1)) {
  499.         message("You may not place locally charged calls\r");
  500.         return;
  501.     }
  502. #endif
  503.     sprint (buf, "calling %s", RemoteHostAddr.x25_addr);
  504.     cp = buf + strlen (buf);
  505.     if (RemoteHostName) {
  506.         sprint (cp, " (%s)", RemoteHostName);
  507.         cp = cp + strlen (cp);
  508.     }
  509.     if (RemoteHostAddr.x25_opts.op_flags & X25_REVERSE_CHARGE)
  510.         strcat (cp, " collect");
  511.     log (buf);
  512.     if ((NetFd = socket (AF_CCITT, SOCK_STREAM, 0)) < 0) {
  513.         error ();
  514.         return;
  515.     }
  516.     RemoteHostAddr.x25_family = AF_CCITT;
  517.     RemoteHostAddr.x25_opts.op_flags |= X25_MQBIT;
  518.     if (connect (NetFd, (char *)&RemoteHostAddr, sizeof (RemoteHostAddr)) < 0) {
  519.         error ();
  520.         close (NetFd);
  521.         NetFd = -1;
  522.         return;
  523.     }
  524.     if (getpeername (NetFd, (struct sockaddr *)&peer, &slen))
  525.         error ();
  526.     NetInfo.n_psize = (1 << peer.x25_opts.op_psize);
  527.     log ("call succeeded: packet size=%d", NetInfo.n_psize);
  528.     if (CurrentX29Parms[X29_RECEIVE_NET_MSGS_CODE]) {
  529.         message ("nimd:\tCall connected to %s", RemoteHostAddr.x25_addr);
  530.         if (RemoteHostName)
  531.             message (" (%s)", RemoteHostName);
  532.         message ("\r\t(%s charging, packet size: %d)\r\r",
  533.             RemoteHostAddr.x25_opts.op_flags & X25_REVERSE_CHARGE ?
  534.             "remote" : "local", NetInfo.n_psize);
  535.     }
  536.  
  537.     ioctl (NetFd, FIONBIO, (char *)&on);
  538.     State |= ST_DATA;
  539.     State &= ~ST_COMMAND;
  540. }
  541.  
  542. struct X29Keyword *
  543. LookupX29Keyword(word)
  544. char *word;
  545. {
  546.     register struct X29Keyword *xp;
  547.  
  548.     for (xp = X29Keywords; xp->x_word; xp++)
  549.         if (strcmp(word, xp->x_word) == 0)
  550.             return (xp);
  551.     return ((struct X29Keyword *)0);
  552. }
  553.  
  554. ParCommand()
  555. {
  556.     register int param;
  557.     register struct X29Keyword *xp;
  558.  
  559.     if (insymbol(0) == '\0') {
  560.         DisplayCurrentParms();
  561.         return;
  562.     }
  563.     while (sym.s_type != '\0') {
  564.         switch (sym.s_type) {
  565.         case SNUM:
  566.             param = sym.s_num;
  567.             xp = 0;
  568.             break;
  569.  
  570.         case SSTR:
  571.             if ((xp = LookupX29Keyword(sym.s_str)) == 0) {
  572.                 message("%s: unknown X.29 parameter\r", sym.s_str);
  573.                 return;
  574.             }
  575.             param = xp->x_param;
  576.             break;
  577.  
  578.         default:
  579.             message("number or X.29 parameter name expected\r");
  580.             return;
  581.         }
  582.         if (xp)
  583.             message ("%s:%d\r", xp->x_word, CurrentX29Parms[param]);
  584.         else
  585.             message ("%d:%d\r", param, CurrentX29Parms[param]);
  586.         if (insymbol(0) == ',')        /* skip optional comma */
  587.             (void) insymbol(0);
  588.     }
  589. }
  590.  
  591. DisplayCurrentParms()
  592. {
  593.     register int nparams, pnum, i;
  594.  
  595.     nparams = NetInfo.n_nparms;
  596.     for (i=0; i<nparams; i++) {
  597.         pnum = pnums[i];
  598.         message (i%6 ? ", %d:%d" : "\t%d:%d", pnum,
  599.             CurrentX29Parms[pnum]);
  600.         if (i % 6 == 5)
  601.             message ("\r");
  602.     }
  603.     if (nparams % 6)
  604.         message ("\r");
  605. }
  606.  
  607. SetCommand()
  608. {
  609.     register int setandread = 0, param;
  610.     register struct X29Keyword *xp;
  611.  
  612.     (void) insymbol(0);
  613.     if (sym.s_type == '?') {
  614.         setandread++;
  615.         insymbol(0);
  616.     }
  617.     while (sym.s_type != '\0') {
  618.         switch (sym.s_type) {
  619.         case SNUM:
  620.             param = sym.s_num;
  621.             xp = 0;
  622.             break;
  623.  
  624.         case SSTR:
  625.             if ((xp = LookupX29Keyword(sym.s_str)) == 0) {
  626.                 message("%s: unknown X.29 parameter\r", sym.s_str);
  627.                 return;
  628.             }
  629.             param = xp->x_param;
  630.             break;
  631.  
  632.         default:
  633.             message("number or X.29 parameter name expected\r");
  634.             return;
  635.         }
  636.         (void) insymbol(0);
  637.         if (sym.s_type != ':' && sym.s_type != '=') {
  638.             message("':' or '=' expected between parameter and value\r");
  639.             return;
  640.         }
  641.         if (GetNumber(xp ? xp->x_list : 0)) {
  642.             if (SetX29Parm(param, sym.s_num))
  643.                 if (xp)
  644.                     message ("par %s:invalid\r", xp->x_word);
  645.                 else
  646.                     message("par %d:invalid\r", param);
  647.             else
  648.                 if (setandread)
  649.                     if (xp)
  650.                         message ("par %s:%d\r",
  651.                             xp->x_word, sym.s_num);
  652.                     else
  653.                         message("PAR %d:%d\r", param, sym.s_num);
  654.         } else {
  655.             message("number expected\r");
  656.             return;
  657.         }
  658.         (void) insymbol(0);
  659.         if (sym.s_type == ',')        /* skip optional comma */
  660.             (void) insymbol(0);
  661.     }
  662. }
  663.  
  664. ProfileCommand()
  665. {
  666.     register int displayonly = 0, profile;
  667.  
  668.     if (insymbol(0) == '?') {
  669.         displayonly++;
  670.         (void) insymbol(0);
  671.     }
  672.     if (sym.s_type == SNUM) {
  673.         profile = sym.s_num;
  674.         if (profile >= 0 && profile <= 6)
  675.             if (insymbol(0) == '\0') {
  676.                 if (displayonly)
  677.                     DisplayProfile(profile);
  678.                 else
  679.                     InitProfile(profile);
  680.                 return;
  681.             }
  682.     }
  683.     message("usage: prof n or prof?n where n is a number between 1 and 6\r");
  684. }
  685.  
  686. DisplayProfile (profile)
  687. {
  688.     register int i, nparms, pnum, value;
  689.     extern char profiles[NPROFILES+1][NX29_PARMS];
  690.  
  691.     nparms = NetInfo.n_nparms;
  692.     for (i=1; i<=nparms; i++) {
  693.         pnum = pnums[i];
  694.         value = profiles[profile][pnum];
  695.         if (i % 6 == 0)
  696.             if (i == 0)
  697.                 message ("prof %d\t%d:%d", profile, pnum, value);
  698.             else
  699.                 message ("\t%d:%d", pnum, value);
  700.         else
  701.             message (", %d:%d", pnum, value);
  702.         if (i % 6 == 5)
  703.             message ("\r");
  704.     }
  705.     if (nparms % 6)
  706.         message ("\r");
  707. }
  708.  
  709. StatusCommand ()
  710. {
  711.     if (State & ST_DATA)
  712.         message ("Connected to %s\r", RemoteHostName);
  713.     else
  714.         message ("idle\r");
  715. }
  716.  
  717. InterruptCommand(discard)
  718. {
  719.     if (discard)
  720.         Break (21);
  721.     else
  722.         Break (1);
  723. }
  724.  
  725. ClearCommand ()
  726. {
  727.     if (State & ST_DATA)
  728.         ExitDataState ("local directive");
  729.     else
  730.         message ("you are not connected to anybody\r");
  731. }
  732.  
  733. NullCommand ()
  734. {
  735.     if (State & ST_ESCCOMM)
  736.         State &= ~(ST_COMMAND|ST_ESCCOMM);
  737.     else
  738.         message (Banner);
  739. }
  740.  
  741. ResetCommand()
  742. {
  743.     ResetBufs ();
  744. }
  745.  
  746. HelpCommand()
  747. {
  748.     GetHelp ("general-info");
  749. }
  750.  
  751. GetHelp (topic)
  752. char *topic;
  753. {
  754.     register int fd;
  755.     char HelpFile[128];
  756.  
  757.     strcpy (HelpFile, HELPFILE);
  758.     strcat (HelpFile, topic);
  759.     if ((fd = open (HelpFile, 0)) < 0) {
  760.         message ("No help with %s\r", topic);
  761.         return;
  762.     }
  763.     /* not yet implemented */
  764.     close (fd);
  765. }
  766.  
  767. NuiCommand ()        /* net completely implemented */
  768. {
  769.     if (insymbol (1) == '\0') {
  770.         if (Nui)
  771.             message ("NUI = %s\r", Nui);
  772.         else
  773.             message ("No valid NUI\r");
  774.         return;
  775.     }
  776. }
  777.