/* The following is a test program for a subroutine set which provides a simple emulation of the TOPS-20 COMND JSYS for C programs. This program tests some of the basic features, and illustrates how the subroutine package is used. */ #include "comnd.h" /* Include interface definitions */ /* for the COMND package */ #include "stdio.h" /* Include standard system defn's */ #include "setjmp.h" /* Include defn for the setjmp/longjump */ /* facility of the C language */ /* Declare forward routines that our program uses; mainly those routines which are included in the command dispatch table. Since they will be in the table, they must be defined. */ int datcmd(); /* The DATE command */ int exicmd(); /* The EXIT command */ int hlpcmd(); /* The HELP command */ int numcmd(); /* The NUMBER command */ int uicmd(); /* Unimplemented command */ /* Declare various simple variables our program uses. */ char Atmbuf[100] = {0}; /* Atom buffer used by CSB */ jmp_buf Topenv = {0}; /* Setjump buffer used in command parsing. */ char Txtbuf[100] = {0}; /* Text buffer used by CSB (later) */ /* Define our command table and dispatch table. The command table is a table of pointers to strings; these strings do not have to be in any order; but remember they are printed (for help) in the order given here. */ /* Note that in the command table, only a few of the entries are reasonable. The rest of the keywords are in the table to illustrate the way that keywords are matched by the COMND subroutines. */ char *Cmdtbl[] = { /* Command keyword table */ "ABLE", "BAKER", "BARKER", "BASKET", "CHANCELLOR", "CHESTER", "DATE", "DOG", "EXIT", "HELP", "NUMBER", NULL /* The table ends with a null */ }; int (*Cmddsp[])() = { /* Dispatch table for commands */ uicmd, /* ABLE */ uicmd, /* BAKER */ uicmd, /* BARKER */ uicmd, /* BASKET */ uicmd, /* CHANCELLOR */ uicmd, /* CHESTER */ datcmd, /* DATE */ uicmd, /* DOG */ exicmd, /* EXIT */ hlpcmd, /* HELP */ numcmd /* NUMBER */ }; /* Define our Command State Block. A program may have any number of these, and typically have a couple (one for main level commands and another for subcommands, etc.). This program only uses one, because it is not very complex. */ CSB Topcsb = {0, /* Flags to pass to COMND subroutine */ 0, /* Flags returned by COMND */ &Topenv, /* Address of setjmp buffer; used in transfering control if a reparse is necessary */ 0, /* Input designator (ignored) */ 0, /* Output designator (ignored) */ "TEST> ", /* Prompt string */ &Txtbuf, /* Address of text buffer */ 100, /* Size of text buffer (bytes) */ &Atmbuf, /* Address of atom buffer */ 100, /* Size of atom buffer (bytes) */ 0}; /* The rest of the block is used for returned values and does not have to be initialized here */ /* Define the various Command Function Blocks that we will use. Each function block defines something to be parsed. The first definition is expanded with comments; the rest are simply defined. */ CFB Cmdcfb = {_CMKEY, /* Function code (_CMKEY=keyword) */ _CFDPP|_CFHPP, /* Flags; _CFDPP indicates that we've supplied a default string; _CFHPP indicates that we've supplied our own help text to be used in addition to the standard help. _CFSDH would suppress the standard help as well. */ 0, /* This would be an address of another CFB to be used in satisfying the parse. No alternatives here */ &Cmdtbl, /* Data for the function; addr of keyword table, here. */ "Command, ", /* Help text that we supply */ "BASKET" /* Default string. */ }; /* CFB for HELP... this illustrates how CFBs can be chained to give alternative parse paths. */ CFB Hlpcfb = {_CMTOK, _CFHPP|_CFSDH, &Cmdcfb, "*", "\"*\" for help on all topics", 0}; /* Initialization CFB */ CFB Inicfb = {_CMINI, 0, 0, 0, 0, 0}; /* CFB for guide words */ CFB Noicfb = {_CMNOI, 0, 0, 0, 0, "guide words"}; /* CFB for confirmation */ CFB Cfmcfb = {_CMCFM, 0, 0, 0, 0, 0}; /* CFB for date parse */ CFB Datcfb = {_CMDAT, _CFDTD|_CFDTT, 0, 0, 0, 0}; /* CFB for decimal number parse */ CFB Numcfb = {_CMNUM, 0, 0, 10, 0, 0}; /* */ /* The main routine. */ main() { IND int i; /* Scratch */ IND char **kptr; /* Keyword ptr ptr */ /* Enter command loop. */ while (TRUE) { /* The first part of COMND parsing is to initialize the parse. This is done with a CFB with function code of _CFINI */ COMND (&Topcsb, &Inicfb); /* Init the parse */ /* Call setjmp to mark the point where a reparse of the command string would take place. Since we've supplied this setjmp buffer address to COMND (by putting its address in our CSB), COMND will transfer control here whenever a reparse should take place. If the setjmp mechanism is not used, the program must always check for a return code of _CRRPT, indicating that a reparse is necessary. The setjmp mechanism is the far simpler method. */ setjmp (Topenv); /* Trap reparse */ /* Now parse a command keyword. This is done by calling COMND with the appropriate command function block. */ if (!COMNDi (&Topcsb, &Cmdcfb)) /* Parse a command */ continue; /* continue if failed. (see the */ /* routine COMNDI() below) */ /* Now determine what keyword was parsed. The return value (in CSB_RVL of the command state block) is the address of the keyword table entry which was parsed. Thus it is a pointer to a pointer to the keyword. */ kptr = (char **) (Topcsb.CSB_RVL._ADR); /* Get the table entry address */ i = kptr - &Cmdtbl[0]; /* Get the command table index */ /* i now has the command index; simply dispatch via the command dispatch table to the appropriate processing routine. */ (*Cmddsp[i])(); /* Call the routine */ /* End of command loop. */ } } /* */ /* datcmd - the DATE command */ datcmd () { IND int id,it,m,d,y,mm,hh; /* Date/time values */ IND int *rslptr; /* Result pointer */ /* Issue a call to our "noise" subroutine (below) to parse guide words. */ if (!noise("is")) /* Do guide word parse */ return; /* And return if it failed */ /* Parse the command argument */ if (!COMNDi(&Topcsb, &Datcfb)) /* Do COMND call and check failure */ return; /* Issue call to our "confrm" routine to confirm the command. */ if (!confrm()) /* Call our general confirm routine */ return; /* If not confirmed, just return. */ rslptr = &Atmbuf[0]; id = rslptr[0]; /* Get results */ it = rslptr[1]; cvided (id, it, &m, &d, &y, &hh, &mm); printf ("Returned %02d/%02d/%04d %02d:%02d\n", m, d, y, hh, mm); } /* */ /* exicmd - the EXIT command */ exicmd () { /* Issue a call to our "noise" subroutine (below) to parse guide words. */ if (!noise("program")) /* Do guide word parse */ return; /* And return if it failed */ /* Issue call to our "confrm" routine to confirm the command. */ if (!confrm()) /* Call our general confirm routine */ return; /* If not confirmed, just return. */ exit(); /* Exit the program. */ } /* */ /* hlpcmd - the HELP command */ /* This command illustrates how COMND is used to parse one of multiple choices; here we either parse the token "*" or a command keyword. */ hlpcmd () { char **kptr; /* Points to keyword */ char *aptr; /* Points to argument */ /* Collect the help argument, after giving appropriate guide string */ if (!noise ("on subject")) /* Give guide string */ return; /* return if it failed */ /* Parse the command argument. */ if (!COMNDi(&Topcsb, &Hlpcfb)) /* Do COMND call and check failure */ return; /* Since we supplied alternate CFBs in our COMND call, we have to see which one matched the input, and process accordingly. Here "process" is simply to set a pointer to the text we are going to say we can't give help for */ if (Topcsb.CSB_CFB == &Hlpcfb) /* If matched our token */ aptr = "*"; /* Set pointer to token string. */ else /* Otherwise */ { kptr = (char **) Topcsb.CSB_RVL._ADR; /* Get ptr to keyword pointer */ aptr = *kptr; /* Get addr of string */ } if (!confrm()) /* Call our general confirm routine */ return; /* If not confirmed, just return. */ /* Now we've got the keyword; this is only a test routine, show the thing parsed and say we can't give help for it. */ printf ("Sorry, can not give help for "); printf (aptr); printf ("\n"); } /* */ /* numcmd - the NUMBER command */ numcmd () { IND int num; /* Number */ if (!noise ("to print")) /* Get/give guide string */ return; /* Return if invalid */ if (!COMNDi (&Topcsb, &Numcfb)) /* If not ok */ return; /* Extract the number from the returned value field in the CSB; then go on to confirm the command. */ num = Topcsb.CSB_RVL._INT; /* Get the number */ if (!confrm()) /* Call our general confirm routine */ return; /* If not confirmed, just return. */ printf ("Number is %d\n", num); /* Print the number, for show */ } /* */ /* uicmd - unimplemented or silly commands */ uicmd () { if (!noise ("nothing")) /* Give random guide string */ return; /* Return if bad parse of it */ if (!confrm()) /* Call our general confirm routine */ return; /* If not confirmed, just return. */ printf ("This command is not implemented.\n"); } /* */ /* noise - parses a guide string. Called with the address of the expected guide string; without the parentheses, of course. */ noise (str) char *str; /* Address of string */ { Noicfb.CFB_DEF = str; /* Store the string pointer in the guide word CFB */ return (COMNDi(&Topcsb, &Noicfb)); /* Do parse and return the result */ } /* */ /* confrm - get confirmation (call COMND for confirm) */ /* Returns TRUE if OK confirmation; FALSE if not. */ confrm () { if (COMNDi (&Topcsb, &Cfmcfb)) /* Get confirmation */ return (TRUE); /* Give OK return if success */ printf (" ?Not confirmed\n"); /* Give another error msg */ return (FALSE); /* Return bad status. */ } /* */ /* COMNDi - interface to the COMND() library routine, giving a message if a parse error occurs. Returns TRUE or FALSE depending on success */ int COMNDi (CSBptr, CFBptr) CSB *CSBptr; /* Address of command state block */ CFB *CFBptr; /* Address of command function block */ { IND int i; /* A counter */ IND char *sptr; /* A string pointer */ if (COMND(CSBptr, CFBptr) == _CROK) /* If successful parse */ return (TRUE); /* then give good return */ sptr = &CSBptr->CSB_BUF[CSBptr->CSB_PRS]; /* Get addr of unrecognized input */ i = CSBptr->CSB_FLN - CSBptr->CSB_PRS; /* Get # of chars unrecognized */ printf (" ??Invalid- Can not recognize \""); while (i--) putchar (*sptr++); /* Print the bad string */ printf ("\"... use ? here.\n"); /* Tell him how to proceed. */ return (FALSE); /* Give bad return */ }