4.4BSD/usr/src/contrib/usr.x25/nimd/commands.c
/*
* NIM daemon command parser
*
* Frank Pronk
* Copyright (c) 1984
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netccitt/x25.h>
#include <netdb.h>
#include "../h/x29.h"
#include "nim.h"
#define NEXTC (*sym.s_next ? *sym.s_next++ : (char)0)
#define SSET 0200+0
#define SPAR 0200+1
#define SPROF 0200+2
#define SSTATUS 0200+3
#define SRESET 0200+4
#define SINT 0200+5
#define SINTD 0200+6
#define SCLEAR 0200+7
#define SHELP 0200+8
#define SCALL 0200+9
#define SPRI 0200+10
#define SREV 0200+11
#define SNUM 0200+12
#define SSTR 0200+13
#define SNUI 0200+14
struct NIMCommand {
char *c_name;
short c_value;
} Commands[] = {
"set", SSET,
"par", SPAR,
"prof", SPROF,
"profile", SPROF,
"stat", SSTATUS,
"status", SSTATUS,
"reset", SRESET,
"int", SINT,
"intd", SINTD,
"clear", SCLEAR,
"help", SHELP,
"call", SCALL,
"p", SPRI,
"rev", SREV,
"nui", SNUI,
0, 0,
};
struct sockaddr_x25 RemoteHostAddr;
char *RemoteHostName;
char *Nui; /* network user identification */
/*
* Structure used by insymbol() keep track of its
* current position in the command string being
* parsed and to hold the results of the last
* call to insymbol()
*/
struct symbol {
char *s_start; /* start of line to be parsed */
char *s_next; /* current position in line */
short s_type; /* see defines above */
int s_num; /* value if current symbol is a number */
char *s_str; /* address of symbol if a string */
} sym;
/*
* List of symbolic names that can be
* used as replacements for numeric codes
* in specific "set parameter" requests
*/
struct NameList {
char *n_name;
short n_value;
} ForwardList[] = { "cr", 2, "control", 126, "off", 0, 0, 0 },
OnOffList[] = { "on", 1, "off", 0, 0, 0},
LfList[] = { "on", 4, "off", 0, "none", 0, "local", 4, "remote", 1, "both", 5, 0, 0};
/*
* list of x.29 symbolic keywords that can be substituted
* for numeric codes. Note that this list contains only
* the x.29 parameters that an average user would ever
* want to change.
*/
struct X29Keyword {
char *x_word; /* parameter name */
short x_param; /* parameter code */
struct NameList *x_list;
} X29Keywords[] = {
"escape", X29_ESCAPE_TO_CMD_CODE, OnOffList,
"echo", X29_ECHO_CODE, OnOffList,
"forward", X29_FORWARDING_SIGNAL_CODE, ForwardList,
"timer", X29_IDLE_TIMER_CODE, OnOffList,
"break", X29_BREAK_PROCEDURE_CODE, 0,
"lf", X29_LF_AFTER_CR, LfList,
"lf-insertion", X29_LF_AFTER_CR, LfList,
"editing", X29_EDITING, OnOffList,
"erase", X29_CHARACTER_DELETE, 0,
"kill", X29_LINE_DELETE, 0,
"replay", X29_LINE_DISPLAY, 0,
"display", X29_LINE_DISPLAY, 0,
0, 0, 0
};
/*
* Attempt to parse string pointed to by 'cp'.
*/
NimCommand(cp)
register char *cp;
{
sym.s_start = sym.s_next = cp;
/*
* strip parity bit from command
*/
while ((*cp&0177) != '\0')
*cp++ &= 0177;
switch(insymbol(0)) {
case SCLEAR:
ClearCommand();
return;
case SHELP:
case '?':
HelpCommand();
return;
case SINT:
InterruptCommand(0);
break;
case SINTD:
InterruptCommand(1);
return;
case SPAR:
ParCommand();
return;
case SPROF:
ProfileCommand();
return;
case SRESET:
ResetCommand();
return;
case SSET:
SetCommand();
return;
case SSTATUS:
StatusCommand();
return;
case SPRI:
case SREV:
case SNUM:
X121CallCommand();
return;
case SCALL:
CallCommand();
return;
case SNUI:
NuiCommand ();
return;
case '\0':
NullCommand();
return;
case '.':
DotCommand();
return;
default:
message ("Unknown NIM command\r");
return;
}
}
GetNumber(np)
register struct NameList *np;
{
(void) insymbol(0);
if (sym.s_type == SNUM)
return (1);
if (sym.s_type == '(') {
register int value;
switch (insymbol(1)) {
case SSTR:
if (strlen (sym.s_str) != 1)
return (0);
value = *sym.s_str;
break;
case SNUM:
value = sym.s_num;
break;
default:
if (sym.s_type == '\r')
return (0);
value = sym.s_type;
}
(void) insymbol(0);
if(sym.s_type == ')') {
sym.s_num = value;
return(1);
}
return(0);
}
if (sym.s_type == SSTR && np)
for (; np->n_name; np++)
if (strcmp(np->n_name, sym.s_str) == 0) {
sym.s_num = np->n_value;
return (1);
}
return (0);
}
/*
* Save the string pointed to by 's'
*/
char *
saves(s)
char *s;
{
register char *p;
char *malloc();
p = malloc(strlen(s) + 1);
strcpy(p, s);
return (p);
}
char
GetEscaped ()
{
register char c, value;
c = NEXTC;
if (c < '0' || c > '7')
return (c);
value = c - '0';
c = NEXTC;
if (c >= '0' && c <= '7') {
register int n;
value = value*8 + c - '0';
c = NEXTC;
if (c >= '0' && c <= '7' && (n = value*8 + c-'0') < 256) {
value = n;
c = NEXTC;
}
}
return (value);
}
insymbol(special)
{
register char c, *cp;
char buf[128];
if (sym.s_str) {
free(sym.s_str);
sym.s_str = 0;
}
cp = buf;
while ((c = NEXTC) == ' ' || c == '\t');
if (c == '\'' || c == '"') {
register char quote = c;
while (1) {
c = NEXTC;
if (c == '\0')
return (sym.s_type = 0);
if (c == quote)
break;
if (c == '\\')
c = GetEscaped ();
if(cp < buf + sizeof (buf))
*cp++ = c;
}
*cp = '\0';
sym.s_type = SSTR;
sym.s_str = saves (buf);
return (sym.s_type);
}
if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '\\') {
do {
if (c == '\\')
c = GetEscaped ();
if(cp < buf + sizeof (buf)) {
if (!special && c >= 'A' && c <= 'Z')
c -= 'A' - 'a';
*cp++ = c;
}
c = NEXTC;
} while (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z' || c == '_' || c == '-');
*cp = '\0';
if (c)
sym.s_next--;
/*
* set symbol type to SSTR and
* then look for keywords
*/
sym.s_type = SSTR;
if (!special) {
register struct NIMCommand *np;
cp = &buf[0];
for (np = Commands; np->c_name; np++)
if(strcmp(cp, np->c_name) == 0) {
sym.s_type = np->c_value;
break;
}
}
if (sym.s_type == SSTR)
sym.s_str = saves(buf);
return (sym.s_type);
}
if (c >= '0' && c <= '9') {
do {
if(cp < buf + sizeof (buf))
*cp++ = c;
c = NEXTC;
} while(c >= '0' && c <= '9');
*cp = '\0';
if (c)
sym.s_next--;
sym.s_num = atoi(buf);
sym.s_type = SNUM;
sym.s_str = saves(buf);
return (SNUM);
}
return (sym.s_type = c);
}
/*
* Does nothing useful - retained for
* compatibility with Datapac.
*/
DotCommand()
{
message (Banner);
}
X121CallCommand()
{
register int len, havenet = 0;
if (State & ST_DATA) {
message ("You are connected to %s, please clear this call before attempting another\r",
RemoteHostName?RemoteHostName:(char *)RemoteHostAddr.x25_addr);
return;
}
bzero ((char *)&RemoteHostAddr, sizeof (RemoteHostAddr));
if (RemoteHostName) {
free (RemoteHostName);
RemoteHostName = (char *)0;
}
while (sym.s_type == SPRI || sym.s_type == SREV) {
if (sym.s_type == SPRI) /* Datapac specific */
RemoteHostAddr.x25_opts.op_psize = X25_PS128;
else if (sym.s_type == SREV)
RemoteHostAddr.x25_opts.op_flags |= X25_REVERSE_CHARGE;
(void) insymbol(0);
}
getaddr:
if (sym.s_type != SNUM) {
message("Non-numeric destination address\r");
return;
}
if (strlen(sym.s_str) > sizeof (RemoteHostAddr.x25_addr) - 1) {
message ("Destination address too long\r");
return;
}
strcpy(RemoteHostAddr.x25_addr, sym.s_str);
(void) insymbol(1);
if (sym.s_type == ':' || sym.s_type == '.') {
if (havenet) {
message("Destination address error\r");
return;
}
havenet++;
RemoteHostAddr.x25_net = atoi(RemoteHostAddr.x25_addr);
(void) insymbol(1);
goto getaddr;
}
if (sym.s_type == ',') /* skip optional comma */
(void) insymbol(1);
RemoteHostAddr.x25_udata[0] = ITI_CALL;
RemoteHostAddr.x25_udlen = 4;
if (sym.s_type == SSTR || sym.s_type == SNUM) {
if ((len = strlen(sym.s_str)) > sizeof (RemoteHostAddr.x25_udata) - 4) {
message ("Userdata field too long\r");
RemoteHostAddr.x25_addr[0] = '\0';
return;
}
strcpy(RemoteHostAddr.x25_udata + 4, sym.s_str);
RemoteHostAddr.x25_udlen += len;
(void) insymbol(1);
}
if (sym.s_type == ',') /* skip optional comma */
(void) insymbol(1);
if (sym.s_type == SSTR || sym.s_type == SNUM) { /* protocol id */
if (strlen(sym.s_str) > 4) {
message ("Protocol field too long\r");
RemoteHostAddr.x25_addr[0] = '\0';
return;
}
strcpy(RemoteHostAddr.x25_udata, sym.s_str);
(void) insymbol(0);
}
if (sym.s_type != '\0') { /* still more to come? */
message ("usage: [options] address [userdata] [protocol id]\r");
RemoteHostAddr.x25_addr[0] = '\0';
return;
}
InitiateSession ();
}
CallCommand ()
{
register struct hostent *hp;
struct hostent *getx25hostbyname ();
if (insymbol(1) == '\0') {
if (RemoteHostAddr.x25_addr[0] == '\0') {
message ("Call who?\r");
return;
}
} else {
if (sym.s_type != SSTR) {
message("nimd: usage: call [hostname]\r");
return;
}
if ((hp = getx25hostbyname (sym.s_str)) == 0) {
message ("nimd: can't find \"%s\" in host table\r",
sym.s_str);
return;
}
if (RemoteHostName)
free (RemoteHostName);
RemoteHostName = saves (sym.s_str);
bcopy (hp->h_addr, (char *)&RemoteHostAddr, sizeof (RemoteHostAddr));
RemoteHostAddr.x25_udata[0] = ITI_CALL;
}
InitiateSession ();
}
InitiateSession ()
{
register char *cp;
struct sockaddr_x25 peer;
int slen = sizeof (struct sockaddr_x25);
char buf[256];
int on = 1;
#ifdef waterloo
extern char user_name[];
if ((RemoteHostAddr.x25_opts.op_flags & X25_REVERSE_CHARGE) == 0 &&
!(user_name[0] ? x25_can_callin(user_name) : 1)) {
message("You may not place locally charged calls\r");
return;
}
#endif
sprint (buf, "calling %s", RemoteHostAddr.x25_addr);
cp = buf + strlen (buf);
if (RemoteHostName) {
sprint (cp, " (%s)", RemoteHostName);
cp = cp + strlen (cp);
}
if (RemoteHostAddr.x25_opts.op_flags & X25_REVERSE_CHARGE)
strcat (cp, " collect");
log (buf);
if ((NetFd = socket (AF_CCITT, SOCK_STREAM, 0)) < 0) {
error ();
return;
}
RemoteHostAddr.x25_family = AF_CCITT;
RemoteHostAddr.x25_opts.op_flags |= X25_MQBIT;
if (connect (NetFd, (char *)&RemoteHostAddr, sizeof (RemoteHostAddr)) < 0) {
error ();
close (NetFd);
NetFd = -1;
return;
}
if (getpeername (NetFd, (struct sockaddr *)&peer, &slen))
error ();
NetInfo.n_psize = (1 << peer.x25_opts.op_psize);
log ("call succeeded: packet size=%d", NetInfo.n_psize);
if (CurrentX29Parms[X29_RECEIVE_NET_MSGS_CODE]) {
message ("nimd:\tCall connected to %s", RemoteHostAddr.x25_addr);
if (RemoteHostName)
message (" (%s)", RemoteHostName);
message ("\r\t(%s charging, packet size: %d)\r\r",
RemoteHostAddr.x25_opts.op_flags & X25_REVERSE_CHARGE ?
"remote" : "local", NetInfo.n_psize);
}
ioctl (NetFd, FIONBIO, (char *)&on);
State |= ST_DATA;
State &= ~ST_COMMAND;
}
struct X29Keyword *
LookupX29Keyword(word)
char *word;
{
register struct X29Keyword *xp;
for (xp = X29Keywords; xp->x_word; xp++)
if (strcmp(word, xp->x_word) == 0)
return (xp);
return ((struct X29Keyword *)0);
}
ParCommand()
{
register int param;
register struct X29Keyword *xp;
if (insymbol(0) == '\0') {
DisplayCurrentParms();
return;
}
while (sym.s_type != '\0') {
switch (sym.s_type) {
case SNUM:
param = sym.s_num;
xp = 0;
break;
case SSTR:
if ((xp = LookupX29Keyword(sym.s_str)) == 0) {
message("%s: unknown X.29 parameter\r", sym.s_str);
return;
}
param = xp->x_param;
break;
default:
message("number or X.29 parameter name expected\r");
return;
}
if (xp)
message ("%s:%d\r", xp->x_word, CurrentX29Parms[param]);
else
message ("%d:%d\r", param, CurrentX29Parms[param]);
if (insymbol(0) == ',') /* skip optional comma */
(void) insymbol(0);
}
}
DisplayCurrentParms()
{
register int nparams, pnum, i;
nparams = NetInfo.n_nparms;
for (i=0; i<nparams; i++) {
pnum = pnums[i];
message (i%6 ? ", %d:%d" : "\t%d:%d", pnum,
CurrentX29Parms[pnum]);
if (i % 6 == 5)
message ("\r");
}
if (nparams % 6)
message ("\r");
}
SetCommand()
{
register int setandread = 0, param;
register struct X29Keyword *xp;
(void) insymbol(0);
if (sym.s_type == '?') {
setandread++;
insymbol(0);
}
while (sym.s_type != '\0') {
switch (sym.s_type) {
case SNUM:
param = sym.s_num;
xp = 0;
break;
case SSTR:
if ((xp = LookupX29Keyword(sym.s_str)) == 0) {
message("%s: unknown X.29 parameter\r", sym.s_str);
return;
}
param = xp->x_param;
break;
default:
message("number or X.29 parameter name expected\r");
return;
}
(void) insymbol(0);
if (sym.s_type != ':' && sym.s_type != '=') {
message("':' or '=' expected between parameter and value\r");
return;
}
if (GetNumber(xp ? xp->x_list : 0)) {
if (SetX29Parm(param, sym.s_num))
if (xp)
message ("par %s:invalid\r", xp->x_word);
else
message("par %d:invalid\r", param);
else
if (setandread)
if (xp)
message ("par %s:%d\r",
xp->x_word, sym.s_num);
else
message("PAR %d:%d\r", param, sym.s_num);
} else {
message("number expected\r");
return;
}
(void) insymbol(0);
if (sym.s_type == ',') /* skip optional comma */
(void) insymbol(0);
}
}
ProfileCommand()
{
register int displayonly = 0, profile;
if (insymbol(0) == '?') {
displayonly++;
(void) insymbol(0);
}
if (sym.s_type == SNUM) {
profile = sym.s_num;
if (profile >= 0 && profile <= 6)
if (insymbol(0) == '\0') {
if (displayonly)
DisplayProfile(profile);
else
InitProfile(profile);
return;
}
}
message("usage: prof n or prof?n where n is a number between 1 and 6\r");
}
DisplayProfile (profile)
{
register int i, nparms, pnum, value;
extern char profiles[NPROFILES+1][NX29_PARMS];
nparms = NetInfo.n_nparms;
for (i=1; i<=nparms; i++) {
pnum = pnums[i];
value = profiles[profile][pnum];
if (i % 6 == 0)
if (i == 0)
message ("prof %d\t%d:%d", profile, pnum, value);
else
message ("\t%d:%d", pnum, value);
else
message (", %d:%d", pnum, value);
if (i % 6 == 5)
message ("\r");
}
if (nparms % 6)
message ("\r");
}
StatusCommand ()
{
if (State & ST_DATA)
message ("Connected to %s\r", RemoteHostName);
else
message ("idle\r");
}
InterruptCommand(discard)
{
if (discard)
Break (21);
else
Break (1);
}
ClearCommand ()
{
if (State & ST_DATA)
ExitDataState ("local directive");
else
message ("you are not connected to anybody\r");
}
NullCommand ()
{
if (State & ST_ESCCOMM)
State &= ~(ST_COMMAND|ST_ESCCOMM);
else
message (Banner);
}
ResetCommand()
{
ResetBufs ();
}
HelpCommand()
{
GetHelp ("general-info");
}
GetHelp (topic)
char *topic;
{
register int fd;
char HelpFile[128];
strcpy (HelpFile, HELPFILE);
strcat (HelpFile, topic);
if ((fd = open (HelpFile, 0)) < 0) {
message ("No help with %s\r", topic);
return;
}
/* not yet implemented */
close (fd);
}
NuiCommand () /* net completely implemented */
{
if (insymbol (1) == '\0') {
if (Nui)
message ("NUI = %s\r", Nui);
else
message ("No valid NUI\r");
return;
}
}