[net.sources] Repost CMD003 part 3

mem@sii.UUCP (Mark Mallett) (06/22/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