mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-25 17:34:06 +00:00
418 lines
13 KiB
C
418 lines
13 KiB
C
/**************************************************************************/
|
||
/**************************************************************************/
|
||
/* genc5.c: M E N U handling routines
|
||
/*
|
||
/* Written by Bill Fitler
|
||
/**************************************************************************/
|
||
/**************************************************************************/
|
||
#ifndef MAINMODULE
|
||
#include <genccpm.h>
|
||
EXTERN BOOLEAN verbose; /* flag: be wordy */
|
||
#endif
|
||
|
||
MLOCAL BOOLEAN eflag = FALSE; /* user error */
|
||
|
||
/**************************************************************************/
|
||
/* bldmenu: builds a menu list from successive calls
|
||
/**************************************************************************/
|
||
MENU *
|
||
bldmenu(muroot,typ,ptr,name,description)
|
||
MENU *muroot; /* root of menu list (or NULLPTR) */
|
||
WORD typ; /* type of menu item */
|
||
BYTE *ptr; /* pointer to something... */
|
||
BYTE *name; /* command name for this menu item */
|
||
BYTE *description; /* long label for this item */
|
||
{
|
||
REG MENU *mu; /* ptr to created item */
|
||
REG MENU *tmu; /* temp ptr for linked list scan */
|
||
|
||
mu = (MENU *)malloc(sizeof(*mu)); /* allocate memory for item */
|
||
mu->mutype = typ;
|
||
mu->muiptr = ptr;
|
||
mu->muname = name;
|
||
mu->mudesc = description;
|
||
mu->munext = NULLPTR;
|
||
if( muroot == NULLPTR ) /* if NULL list */
|
||
return mu; /* return something to start with */
|
||
for( tmu=muroot; tmu->munext != NULLPTR; tmu = tmu->munext )
|
||
; /* append to end of list */
|
||
tmu->munext = mu;
|
||
return muroot; /* keep list in order */
|
||
}
|
||
|
||
/**************************************************************************/
|
||
/* domenu: executes the specified commands (in cbuf) from the menu (mnu)
|
||
/**************************************************************************/
|
||
#define CMDLEN 80
|
||
#define USERR ++eflag;printf
|
||
#define ECHK(fn) _errchk(fn,cmd,cmdval)
|
||
|
||
VOID
|
||
domenu(cbuf,mnu)
|
||
BYTE *cbuf; /* command buffer */
|
||
MENU *mnu; /* table of menu specs */
|
||
{
|
||
REG MENU *mi; /* ptr to menu item */
|
||
LOCAL BYTE cmd[CMDLEN]; /* place for command & value */
|
||
LOCAL BYTE *cmdval; /* ptr within cmd */
|
||
LOCAL BYTE *cp; /* temp char ptr */
|
||
BYTE *msetbool(); /* set a boolean value */
|
||
BYTE *msetbyte(); /* set a numeric byte value */
|
||
BYTE *msetword(); /* set an integer pointer */
|
||
BYTE *msettxt(); /* set a text value */
|
||
BYTE *(*proc)(); /* procedure pointer */
|
||
BYTE *msetdrv(); /* set a drive value */
|
||
BYTE *_nxtcmd(); /* command parser */
|
||
MENU *_fndcmd(); /* command lookup */
|
||
|
||
eflag = FALSE; /* no errors yet */
|
||
while((cbuf=_nxtcmd(cbuf,cmd))){ /* while there are commands in cbuf */
|
||
cmdval = NULLPTR; /* init value ptr for command */
|
||
for( cp=cmd; *cp; cp++ ) { /* scan command for assign op */
|
||
if( isupper(*cp) ) /* convert all commands to LOWER CASE */
|
||
*cp = tolower(*cp);
|
||
if( *cp == '=' ) { /* found? */
|
||
*cp++ = NULL; /* terminate command */
|
||
cmdval = cp; /* point value to after assign op */
|
||
break; /* and terminate loop */
|
||
}
|
||
}
|
||
if( (mi=_fndcmd(cmd,mnu)) == NULLPTR ){ /* scan menu commands */
|
||
USERR("'%s' is not a command for this menu\n",cmd);
|
||
} else { /******** cmd loop ********/
|
||
switch(mi->mutype) { /* handle cmd type */
|
||
case MBOOL: /** BOOLEAN command **/
|
||
ECHK(msetbool(mi->muiptr,cmdval));
|
||
break;
|
||
case MBYTE: /** BYTE command **/
|
||
ECHK(msetbyte(mi->muiptr,cmdval));
|
||
break;
|
||
case MWORD: /** INTEGER command **/
|
||
ECHK(msetword(mi->muiptr,cmdval));
|
||
break;
|
||
case MTEXT: /** TEXT command **/
|
||
ECHK(msettxt(mi->muiptr,cmdval));
|
||
break;
|
||
case MPROC: /** PROCEDURAL command **/
|
||
proc = mi->muiptr; /* assign to function ptr */
|
||
ECHK(((*proc)(cmdval,mi->mudesc)));
|
||
break;
|
||
case MDRIV: /** DRIVE letter command **/
|
||
ECHK(msetdrv(mi->muiptr,cmdval));
|
||
break;
|
||
} /* end switch */
|
||
} /********* end cmd loop *********/
|
||
if(eflag) { /* user error? */
|
||
press_return();
|
||
*cbuf = NULL;
|
||
} else if(verbose) /* are we being wordy? */
|
||
prtmival(mi); /* let the user know what happened */
|
||
} /**** end while loop ****/
|
||
}
|
||
|
||
|
||
|
||
/* _nxtcmd: parses cin into cout */
|
||
BYTE *
|
||
_nxtcmd(cin,cout) /* returns ptr to after next cmd */
|
||
BYTE *cin; /* command in buf */
|
||
BYTE *cout; /* command out buf */
|
||
{
|
||
/***** handle quoted strings??? *****/
|
||
while( isspace(*cin) ) /* skip leading spaces */
|
||
++cin;
|
||
if( *cin == NULL ) /* check for EOS */
|
||
return NULLPTR;
|
||
while( *cin && !isspace(*cin) ) /* scan to eos or whitespace */
|
||
*cout++ = *cin++; /* xfer to command out buf */
|
||
*cout = NULL; /* null terminate command out buf */
|
||
return cin; /* return ptr to after command */
|
||
}
|
||
|
||
|
||
|
||
/* _fndcmd: scans thru menu structure 'mu' looking for command name 'cn' */
|
||
/* returns ptr to menu item or NULLPTR */
|
||
MENU *
|
||
_fndcmd(cn,mus)
|
||
BYTE *cn; /* command name */
|
||
MENU *mus; /* ptr to menu list */
|
||
{
|
||
REG WORD cnl; /* cmd name len */
|
||
MENU *musave; /* extra ptr to menu item */
|
||
|
||
cnl = strlen(cn);
|
||
for( ; ; mus = mus->munext) { /* scan menu list */
|
||
if( mus==NULLPTR ) /* if we hit the end then fail */
|
||
return NULLPTR;
|
||
if( strncmp(cn,mus->muname,cnl) == 0 ) /* cn prefix of name?*/
|
||
break; /* yes, we have a candidate */
|
||
}
|
||
musave = mus;
|
||
for( mus=mus->munext; ; mus=mus->munext ) { /* finish scanning list */
|
||
if( mus==NULLPTR ) /* if we hit the end then succeed */
|
||
return musave; /* we reached end of list: found it! */
|
||
if( strncmp(cn,mus->muname,cnl) == 0 )
|
||
return NULLPTR; /* another match: ambiguous command */
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/* _errchk: checks for error returns on functions */
|
||
VOID
|
||
_errchk(emsg,cm,cmv)
|
||
BYTE *emsg; /* error message to check */
|
||
BYTE *cm; /* command generating the error */
|
||
BYTE *cmv; /* value generating the error */
|
||
{
|
||
if( emsg ) { /* non NULL error message return? */
|
||
USERR("Error on command '%s",cm);
|
||
if(cmv)
|
||
USERR("=%s",cmv);
|
||
USERR("': %s\n",emsg);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/* msetbool: sets the BOOLEAN pointed to by 'bp' according to value in 'cvp' */
|
||
BYTE * /* return NULLPTR, or ptr to err msg */
|
||
msetbool(bp,cvp)
|
||
BYTE *bp; /* pointer to boolean to set */
|
||
BYTE *cvp; /* pointer to value to set to */
|
||
{
|
||
REG WORD bv;
|
||
REG BYTE *tp;
|
||
|
||
if( cvp==NULLPTR ) /* no val: toggle boolean */
|
||
bv = (*bp ? 4 : 1); /* toggle by setting bv index */
|
||
else {
|
||
for( tp=cvp; *tp; ++tp )
|
||
if( isupper(*tp) ) *tp=tolower(*tp);
|
||
if( (bv=whichof(cvp,",on,yes,true,off,no,false"))==0 )
|
||
return "value must be 'yes' or 'no'";
|
||
}
|
||
if( bv < 4 )
|
||
*bp = -1; /* zap to 0xFFFF */
|
||
else *bp = 0; /* zap to zero */
|
||
return NULLPTR;
|
||
}
|
||
|
||
|
||
BYTE *
|
||
msetbyte(bp,cvp) /* set numeric BYTE value */
|
||
BYTE *bp; /* ptr to BYTE to set */
|
||
BYTE *cvp; /* value to set BYTE to */
|
||
{
|
||
REG WORD val; /* temp holding place */
|
||
|
||
val=0;
|
||
if(cvp != NULLPTR) val=atoih(cvp); /* assume numbers are in HEX!! */
|
||
if( cvp==NULLPTR || (val==0 && !isdigit(*cvp)) )
|
||
return "value must be a number";
|
||
if( val > 255 )
|
||
return "value must be less than FF hex (255 decimal)";
|
||
*bp = val;
|
||
return NULLPTR;
|
||
}
|
||
|
||
|
||
BYTE *
|
||
msetword(ip,cvp)
|
||
WORD *ip; /* ptr to WORD to set */
|
||
BYTE *cvp; /* value to set WORD to */
|
||
{
|
||
REG WORD val; /* temp holding place */
|
||
|
||
val=0;
|
||
if(cvp != NULLPTR) val=atoih(cvp); /* assume numbers are in HEX!! */
|
||
if( cvp==NULLPTR || (val==0 && !isdigit(*cvp)) )
|
||
return "value must be an unsigned hex number between 0 and FFFFh";
|
||
*ip = val;
|
||
return NULLPTR;
|
||
}
|
||
|
||
|
||
WORD
|
||
atoih(cp) /* conver ascii hex to word */
|
||
BYTE *cp; /* buffer number */
|
||
{
|
||
REG WORD v; /* resulting value */
|
||
REG BYTE cv; /* character value */
|
||
BYTE *savcp; /* save orig ptr */
|
||
|
||
v = 0; savcp = cp; /* init value */
|
||
for( cv = *cp; ; cv = *++cp ){ /* for each char in cp */
|
||
if( isdigit(cv) )
|
||
v = (v<<4) + (cv-'0'); /* convert digit and add */
|
||
else { /* maybe it's a letter */
|
||
if( isupper(cv) ) /* upper case letter? */
|
||
cv = tolower(cv); /* convert it */
|
||
if( cv<'a' || cv>'f') /* range check */
|
||
break; /* terminate loop if failed */
|
||
v = (v<<4)+(10+cv-'a'); /* convert letter and add */
|
||
}
|
||
}
|
||
if( cp-savcp <= 4 )
|
||
return v; /* return the value */
|
||
else {
|
||
*savcp |= 0x80;
|
||
return 0; /* turn on hi bit, make isdigit test fail */
|
||
}
|
||
}
|
||
|
||
BYTE *
|
||
msettxt(tp,cvp)
|
||
BYTE **tp; /* place to store ptr to value */
|
||
BYTE *cvp; /* value to set BYTEs to */
|
||
{
|
||
REG BYTE *tmp; /* temp ptr for allocated storage */
|
||
|
||
if( cvp==NULLPTR )
|
||
cvp=""; /* point to null string, instead */
|
||
tmp = malloc(1+strlen(cvp)); /* get space for string */
|
||
strcpy(tmp,cvp); /* copy to allocated space */
|
||
*tp = tmp; /* assign ptr */
|
||
return NULLPTR;
|
||
}
|
||
|
||
BYTE *
|
||
msetdrv(dp,cvp)
|
||
BYTE *dp; /* place to store ptr to value */
|
||
BYTE *cvp; /* value to set DRIVE to */
|
||
{
|
||
REG WORD v; /* work place */
|
||
|
||
if( cvp!=NULLPTR ) { /* check for drive letter */
|
||
if( isupper(*cvp) )
|
||
*cvp = tolower(*cvp);
|
||
v = *cvp - 'a'; /* convert drive spec to nibble */
|
||
}
|
||
if( cvp==NULLPTR || v<0 || v>15 || cvp[1] != ':' )
|
||
return "you must specify a drive 'A:' thru 'P:'";
|
||
*dp = v; /* looks okay, set it */
|
||
return NULLPTR; /* no errors */
|
||
}
|
||
|
||
|
||
/**************************************************************************/
|
||
/* prtmenu: displays the menu labels, values and descriptors
|
||
/**************************************************************************/
|
||
VOID
|
||
prtmenu(mtitle,mnu)
|
||
BYTE *mtitle; /* title of menu */
|
||
MENU *mnu; /* table specifying menu */
|
||
{
|
||
printf("\n\n%s\n",mtitle); /* give the menu a title */
|
||
for( ; mnu!=NULLPTR; mnu = mnu->munext ) /* travel down list */
|
||
prtmival(mnu);
|
||
}
|
||
|
||
|
||
prtmival(mnu) /* return ptr to value of item */
|
||
MENU *mnu; /* item to get value of */
|
||
{
|
||
REG BYTE *ival;
|
||
BYTE ivbuf[10]; /* local buf for display */
|
||
BYTE *vbp; /* value byte ptr */
|
||
WORD *vwp; /* value word ptr */
|
||
WORD v; /* place to put value */
|
||
|
||
printf("%12.12s ",mnu->muname); /* first the name */
|
||
ival = ivbuf; *ival = NULL; /* init value */
|
||
switch(mnu->mutype) { /* now the value */
|
||
case MBOOL:
|
||
if( *(mnu->muiptr) )
|
||
ival="[Y]";
|
||
else ival="[N]";
|
||
break;
|
||
case MBYTE:
|
||
vbp = mnu->muiptr;
|
||
v = (*vbp & 0xFF);
|
||
sprintf(ivbuf,"[%2.2x]",v); /* display in hex */
|
||
break;
|
||
case MWORD:
|
||
vwp = mnu->muiptr;
|
||
v = *vwp;
|
||
sprintf(ivbuf,"[%4.4x]",v); /* display in hex */
|
||
break;
|
||
case MTEXT:
|
||
vbp = *((BYTE **)mnu->muiptr); /* go get the char ptr */
|
||
if( strlen(vbp) > 4 )
|
||
sprintf(ivbuf,"[%-4.4s>",vbp);
|
||
else sprintf(ivbuf,"[%s]",vbp);
|
||
break;
|
||
case MPROC:
|
||
/* leave blank */
|
||
break;
|
||
case MDRIV:
|
||
sprintf(ivbuf,"[%c:]",'A'+(*mnu->muiptr));
|
||
break;
|
||
}
|
||
printf("%-6s ",ival);
|
||
printf("%s\n",mnu->mudesc); /* finally the description */
|
||
}
|
||
|
||
|
||
/************************************************************************
|
||
/* whichof: scans the string 'set' (delimited by 1st char in 'set')
|
||
/* for 1st occurrence of string 'sample'.
|
||
/* Returns 0 if 'sample' not a prefix of any item in set or if
|
||
/* 'sample' is a prefix of more than one item in set;
|
||
/* delimiter# o.w.
|
||
/* E.G. if set = ",on,yes,true,off,no,false" then
|
||
/* whichof("yes",set) == 2;
|
||
/* whichof("y",set) == 2;
|
||
/* whichof("o",set) == 0;
|
||
/* whichof("x",set) == 0;
|
||
/* NOTE: results not guaranteed if item delimiter (*set) is in string 'sample',
|
||
/* or if null item is in set (adjacent delimiters).
|
||
/*************************************************************************/
|
||
|
||
/* first, a subroutine: */
|
||
/* _wget: returns NULLPTR if sa not prefix of item in st; */
|
||
/* returns end of item (ptr in st) otherwise */
|
||
BYTE *_wget(sa,st,de)
|
||
BYTE *sa;
|
||
BYTE *st;
|
||
BYTE de;
|
||
{
|
||
BYTE *s2;
|
||
|
||
for( ;; ) { /* do forever */
|
||
for( s2=sa; *s2 && *s2 == *st; ){
|
||
++s2; ++st; /* try match */
|
||
}
|
||
if( *s2 == NULL ){ /* success! we've matched all of sa */
|
||
while( *st && *st != de ) ++st; /* adv to nxt delim */
|
||
return( --st ); /* backup one char */
|
||
}
|
||
while( *st && *st != de ) /* no match, look for next item */
|
||
++st; /* adv to nxt delim */
|
||
if( *st == NULL )
|
||
return( NULLPTR ); /* end of list */
|
||
++st;
|
||
}
|
||
}
|
||
|
||
WORD whichof(sample,set)
|
||
BYTE *sample;
|
||
BYTE *set;
|
||
{
|
||
WORD sx;
|
||
BYTE *sp, *np;
|
||
|
||
if( (sp=_wget(sample,set+1,*set)) == NULLPTR )
|
||
return 0; /* not found */
|
||
if( _wget(sample,sp+1,*set) != NULLPTR )
|
||
return 0; /* ambiguous */
|
||
for( sx=0, np=set; np<sp; np++ )
|
||
if( *np == *set ) sx++; /* count delimiters */
|
||
return sx;
|
||
}
|
||
|
||
|
||
|
||
|
||
|