mem@sii.UUCP (Mark Mallett) (06/08/85)
<-> CMD003 TOPS20 style parsing Part 3 of 6 This is the 3rd of 6 pieces of COMND, a TOPS-20 style parsing library for CP/M (and other) systems, edit level 003. Its contents are suitable for unpacking by the standard "sh" shell. Remove this header, and any trailer added by the news/mail system, and run the rest through the shell like this: sh part3 and you will get the following files: test.c Test (sample) calling program. comnd.h Interface definitions. comndi.h Implementation definitions. mem.h Defines names that I use. cpm.h CP/M operating system interface definitions. If you are unwilling to unpack it on a unix system, it should be fairly simple to write a program to unpack it somewhere else. **** End of Header, remove to here+1 **** echo "unpacking file test.c" cat <<'EEOOFF' >test.c /* 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 */ } EEOOFF echo "unpacking file comnd.h" cat <<'EEOOFF' >comnd.h /* comnd.h Common (user and library) definitions for the COMND services. Copyright (C) 1984, 1985 Mark E. Mallett Permission is hereby granted to distribute this file indiscriminately. Edit history When Who What ------ --- -------------------------------- 84xxxx MEM Create file. */ #include "mem.h" /* Include my standard names */ /* Various constants, etc. */ /* CFB command function codes */ #define _CMINI 0x0000 /* Initialize the parse */ #define _CMKEY 0x0001 /* Keyword parse */ #define _CMNUM 0x0002 /* Number */ #define _CMNOI 0x0003 /* Noise words (guide string) */ #define _CMCFM 0x0004 /* Confirm */ #define _CMGSK 0x0005 /* General Storage Keyword */ #define _CMSWI 0x0006 /* Switch */ #define _CMTXT 0x0007 /* Text to end of line */ #define _CMTOK 0x0008 /* Token */ #define _CMUQS 0x0009 /* Unquoted string */ #define _CMDAT 0x000A /* Date and/or time */ #define _CMMAX 0x000A /* Maximum function code */ /* COMND result codes */ #define _CROK 0x0000 /* OK completion */ #define _CRNOP 0x0001 /* No-parse (no matching input) */ #define _CRRPT 0x0002 /* Reparse required */ #define _CRIFC 0x0003 /* Invalid function code */ #define _CRBOF 0x0004 /* Buffer overflow */ #define _CRBAS 0x0005 /* Invalid radix */ #define _CRAGN 0x0006 /* Try-again (for support mode only) */ /* Flags in the CSB_RFL element of the command state block */ #define _CFNOP 0x0001 /* No Parse */ #define _CFESC 0x0002 /* Terminated by escape */ #define _CFEOC 0x0004 /* Terminated by CR */ #define _CFRPT 0x0008 /* Reparse required */ #define _CFSWT 0x0010 /* Switch ended with colon */ #define _CFPFE 0x0020 /* Previous field term. with esc */ /* Flags in the CSB_PFL element of the command state block */ #define _CFRAI 0x0001 /* Raise lowercase to uppercase */ #define _CFNEC 0x0002 /* No echo if this is set */ /* Flags in the CFB_FLG element of the command function block */ #define _CFHPP 0x0001 /* User-supplied help string */ #define _CFDPP 0x0002 /* User-supplied default */ #define _CFSDH 0x0004 /* Suppress default help */ #define _CFCC 0x0008 /* CC table supplied */ #define _CFDTD 0x0040 /* Parse date (for _CMDAT) */ #define _CFDTT 0x0080 /* Parse time (for _CMDAT) */ /* *//* Structures */ /* CSB - The Command State Block */ typedef struct CSBs { BYTE CSB_PFL; /* Passed flags (from caller) */ BYTE CSB_RFL; /* Returned flags (to caller) */ struct setjmp *CSB_RSB; /* Reparse SETJMP buffer */ int (*CSB_INP)(); /* Addr of input-char routine */ int (*CSB_OUT)(); /* Addr of output-char routine */ BYTE *CSB_PMT; /* Prompt */ BYTE *CSB_BUF; /* Buffer address */ int CSB_BSZ; /* Buffer size */ BYTE *CSB_ABF; /* Atom buffer */ int CSB_ASZ; /* Atom buffer size */ int CSB_PRS; /* Parse index */ int CSB_FLN; /* Filled length (# chars filled) */ int CSB_RCD; /* Result code. */ union { /* Returned value */ int _INT; /* --for int value-- */ char *_ADR; /* --for address value-- */ } CSB_RVL; /* (returned value) */ struct CFBs *CSB_CFB; /* Addr of matching CFB, if any */ } CSB; /* CFB - the Command Function Block */ typedef struct CFBs { BYTE CFB_FNC; /* Function code */ BYTE CFB_FLG; /* Flags */ struct CFB *CFB_CFB; /* Addr of next CFB for parse resolution */ AITYPE CFB_DAT; /* Data, if any */ BYTE *CFB_HLP; /* Help string */ BYTE *CFB_DEF; /* Default string */ int *CFB_CC; /* Char characteristics table addr */ } CFB; /* CGK - Command General Keyword parsing block */ /* CGK is pointed to by CFB type _CMGSK. It specifies a routine to call to fetch each new candidate keyword. */ typedef struct CGKs { char *CGK_BAS; /* Base address to give to kfr */ char **(*CGK_KFR)(); /* Keyword fetch routine address */ } CGK; EEOOFF echo "unpacking file comndi.h" cat <<'EEOOFF' >comndi.h /* comndi.h Internal (for implementation only) definitions for the COMND services. Copyright (C) 1984, 1985 Mark E. Mallett Permission is hereby granted to distribute this file indiscriminately. Edit history When Who What ------ --- -------------------------------- 84xxxx MEM Create file. */ /* Various constants, etc. */ /* Parse result codes, to be returned by function code parsers */ #define _CPSUCC 0x0000 /* Success, causes immediate successful return to COMND caller. */ #define _CPABT 0x0001 /* Some severe error, causes immediate unsuccessful return to caller */ #define _CPNOP 0x0002 /* No parse... does not match. May cause unsuccessful return to COMND caller if there are no better results in the CFB chain. */ #define _CPGVH 0x0003 /* Gave help... input might have parsed if it was completed. */ #define _CPAGN 0x0004 /* Try again. Might match if it were complete. */ #define _CPCPE 0x0005 /* Completed with escape. */ /* Character codes passed around by COMND support routines */ #define _CCHLP 0xFFFF /* Help request */ #define _CCCMP 0xFFFE /* Command complete, please? */ #define _CCINC 0xFFFD /* Incomplete (get more) */ #define _CCEND 0xFFFC /* End of input (CR found) */ #define _CCINV 0xFFFB /* Invalid character in atom */ EEOOFF echo "unpacking file mem.h" cat <<'EEOOFF' >mem.h /* mem.h Names and typedefs that I use regularly. 1984 Mark E. Mallett */ #ifndef MEM_INC /* If not already included */ #define MEM_INC 0 /* Avoid duplicate includes */ #define AZTEC 1 /* For Aztec compiler specific stuff */ #define ISCPM 1 /* For CPM */ #define isalpha(c) (isupper(c) || islower(c)) typedef char BYTE; typedef int BOOL; typedef int WORD; typedef long LONG; typedef int AITYPE; /* type which can hold both an int and an address value. */ #define IND static /* For variables with no allocation dependancies (don't have to be on the stack) */ /**//* Various constants */ #define TRUE 1 #define FALSE 0 #define NUL '\000' #endif EEOOFF echo "unpacking file cpm.h" cat <<'EEOOFF' >cpm.h /* CP/M call codes 1983 Mark E. Mallett */ #define _MRICC 1 /* Input console character */ #define _MROCC 2 /* Output console character */ #define _MRPTR 3 /* Read paper tape */ #define _MRAUXI 3 /* Also known as auxiliary input */ #define _MRPTP 4 /* Write paper tape */ #define _MRAUXO 4 /* Also known as auxiliary output */ #define _MRLPT 5 /* Write LPT */ #define _MRDCIO 6 /* Direct console I/O */ #define _MRRIO 7 /* Read IO status */ #define _MRSIO 8 /* Write IO status */ #define _MRWCS 9 /* Write string to console */ #define _MRRBC 10 /* Read buffer from console */ #define _MRRCS 11 /* Read console status */ #define _MRCPV 12 /* CPM version number.. */ #define _MRLFH 12 /* Lift head */ #define _MRINI 13 /* Init BDOS */ #define _MRSEL 14 /* Select and login a disk */ #define _MROPN 15 /* Open a file */ #define _MRCLS 16 /* Close a file */ #define _MRSFL 17 /* Search for file */ #define _MRSNF 18 /* Search for next file */ #define _MRDEL 19 /* Delete a file */ #define _MRREA 20 /* Read next record */ #define _MRWRT 21 /* Write next record */ #define _MRCRF 22 /* Create file */ #define _MRREN 23 /* Rename file */ #define _MRILV 24 /* Interrogate login vector */ #define _MRIDN 25 /* Get drive number */ #define _MRDMA 26 /* Set DMA address */ #define _MRIAL 27 /* Get allocation vector */ #define _MRWPD 28 /* Write-protect disc */ #define _MRROV 29 /* Get R/O vector */ #define _MRSFA 30 /* Set file attributes */ #define _MRGDP 31 /* Get disc parms */ #define _MRGUC 32 /* Get/set user code */ #define _MRRRR 33 /* Read random record */ #define _MRWRR 34 /* Write random record */ #define _MRCFS 35 /* Compute file size */ #define _MRSRR 36 /* Set random record */ #define _MRWRZ 37 /* Write random record with zero fill */ #define _MRCTP 47 /* Chain to program */ #define _MRGDT 105 /* Get date and time. */ /* CP/M 3... */ #define _MRFDS 46 /* Get free disk space */ /* Bios calls */ #define _CBBOOT 0 /* Cold boot */ #define _CBWBOOT 1 /* Warm boot */ #define _CBCNST 2 /* Console status */ #define _CBCNIN 3 /* Console input */ #define _CBCNOUT 4 /* Console out */ #define _CBLIST 5 /* Write to listing */ #define _CBPUN 6 /* Write to punch */ #define _CBRDR 7 /* Read from reader */ #define _CBHOME 8 /* Home the disk */ #define _CBSEL 9 /* Select disc */ #define _CBSTRK 10 /* Set track */ #define _CBSSEC 11 /* Set sector */ #define _CBSDMA 12 /* Set DMA */ #define _CBREAD 13 /* Read sector */ #define _CBWRT 14 /* Write sector */ #define _CBLSST 15 /* List status */ #define _CBSTRN 16 /* Sector translate */ #define _CBXIST 18 /* AUX in status */ EEOOFF