CMD003 Part 3 of 6
Mark Mallett
mem at sii.UUCP
Sun Jun 9 03:57:50 AEST 1985
<->
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
More information about the Comp.sources.unix
mailing list