home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
listings
/
v_10_01
/
cmenu.exe
/
CMENU2.C
< prev
next >
Wrap
Text File
|
1991-12-11
|
14KB
|
600 lines
/************************************************************
* Program: CMENU Menu Compiler
* Module: cmenu2.c
* Menu Compiler:
* Menu/Item Token Processing Functions
* Written by: Leor Zolman, 7/91
************************************************************/
#include "cmenu.h"
#include "ccmenu.h"
#include <ctype.h>
/************************************************************
* do_menu():
* Process the MENU keyword
************************************************************/
int do_menu()
{
int tok;
if (in_menu) /* Are we currently processing a menu? */
{ /* yes. */
warning("Endmenu missing from previous menu");
do_endmenu(); /* give them the benefit of the doubt */
}
if ((tok = gettok()) != T_STRING)
{
if (n_menus)
error("Menu name missing; menu unreachable");
sprintf(tparam, "menu%d", n_menus + 1); /* force a name */
ungettok(tok);
}
if (strlen(tparam) > MAX_NAME)
{
error("The name '%s' is too long (%d chars max)",
tparam, MAX_NAME);
tparam[MAX_NAME] = '\0'; /* truncate name */
}
if ((MIp = find_menu(tparam)) == NULL) /* menu exist? */
{
MInfo[n_menus] = create_menu(tparam); /* no. */
if (fatal)
return ERROR; /* creation error */
else
MIp = &MInfo[n_menus++]; /* OK, bump count */
}
else
if (MIp -> Processed) /* redefinition? */
return fatalerr("Duplicate Menu definition"); /* yes. */
Mp = &MIp -> Menu;
*Mp->title = *Mp->path = '\0';
Mp->nitems = Mp->widest = 0;
Mp->spacing = Mp->columns = Mp->escape = DEFAULT;
Mp->align = DEFAULT;
in_item = FALSE; /* state variables describing the */
in_menu = TRUE; /* current menu being processed */
n_items = 0;
n_refs = 0; /* no forward item references yet */
if ((tok = gettok()) != T_COLON) /* optional colon */
ungettok(tok);
return OK;
}
/************************************************************
* do_title():
* Process the TITLE clause for a menu.
************************************************************/
int do_title()
{
int tok;
if ((tok = gettok()) != T_STRING)
{
error("Title text missing");
ungettok(tok);
}
if (!in_item) /* Before all items? */
{ /* yes. */
if (*Mp->title)
return error("A Menu Title has already been specified");
strcpy(Mp->title, tparam);
}
else
return error("The Menu Title must precede all Menu Items.");
return OK;
}
/************************************************************
* do_path():
* Process the PATH option.
* Note that the PATH option may apply to an entire
* menu or just to a single menu item (determined
* by context.)
************************************************************/
int do_path()
{
int tok;
char *cp;
if ((tok = gettok()) != T_STRING)
{
error("Path text missing");
ungettok(tok);
}
if (tparam[strlen(tparam)-1] == '/' || tparam[strlen(tparam)-1] == '\\')
tparam[strlen(tparam) - 1] = '\0'; /* delete traling slash */
if (!in_item) /* Referring to the menu? */
{ /* yes. */
if (*Mp->path)
return error("A Menu Path has already been specified");
strcpy(Mp->path, tparam);
}
else
{ /* Must be for the item. */
if (*Ip->path)
return error("An Item Path has already been specified");
strcpy(Ip->path, tparam);
}
return OK;
}
/************************************************************
* do_align():
* Process text alignment option.
* Note: this option is a no-op. I decided there wasn't
* any real need for any other than left-justified item
* alignment. But, in case anyone thinks of a use for it,
* I'm leaving in the ability to process the option.
************************************************************/
int do_align()
{
int tok;
if (in_item)
return error("The Align clause must precede all Menu Items.");
if (Mp->align)
return error("Align option already specified for this menu");
switch (tok = gettok())
{
case T_LEFT:
Mp->align = 'L';
break;
case T_RIGHT:
Mp->align = 'R';
break;
case T_CENTER:
Mp->align = 'C';
break;
default:
ungettok(tok);
return error("Align missing valid modifier");
}
return OK;
}
/************************************************************
* do_spacing():
* Process the SPACING option (applies
* to menus only.)
************************************************************/
int do_spacing()
{
int tok;
if ((tok = gettok()) != T_VALUE)
{
error("Spacing value missing");
ungettok(tok);
}
if (in_item)
return error("Spacing option must precede all menu items");
if (Mp->spacing)
return error("Spacing option already specified");
/* only single and double spacing supported */
if (vparam != 1 && vparam != 2)
return error("Spacing value must be either 1 or 2");
Mp->spacing = vparam;
return OK;
}
/************************************************************
* do_columns():
* Process the COLUMNS option
************************************************************/
int do_columns()
{
int tok;
if ((tok = gettok()) != T_VALUE)
{
error("Columns value missing");
ungettok(tok);
}
if (in_item)
return error("Columns option must precede all menu items");
if (Mp->columns)
return error("Columns option already specified");
if (vparam < 1 || vparam > 6) /* 6 seems a reasonable max. */
return error("Columns value must be between 1 and 6");
Mp->columns = vparam;
return OK;
}
/************************************************************
* do_escape():
* Process "escape" and "noescape" menu options
************************************************************/
int do_escape()
{
if (in_item)
return error("\"%s\" must appear before all menu items",
keywords[token].keyword);
if (Mp->escape)
return error("Escape option already specified");
Mp->escape = (token == T_ESCAPE) ? YES : NO;
return OK;
}
/************************************************************
* do_endmenu():
* Process ENDMENU keyword
************************************************************/
int do_endmenu()
{
int i;
if (!n_items)
error("No menu items specified for this menu");
for (i = 0; i < n_refs; i++) /* check for unresolved */
{ /* forward Item references */
if (*fwd_refs[i].refp == UNDEF_FWD)
{
int save_lineno = lineno;
lineno = fwd_refs[i].lineno;
error("Unresolved reference to Item \"%s\"",
fwd_refs[i].iname);
lineno = save_lineno;
}
}
in_menu = in_item = FALSE; /* done with current menu */
MIp -> Processed = TRUE; /* it is now processed */
Mp -> nitems = n_items;
return OK;
}
/************************************************************
* do_item():
* Process the ITEM clause. Create a new ite
* and fill in any existing forward references to it.
************************************************************/
int do_item()
{
int tok, i;
char *cp, c;
if (n_items)
itemcheck(); /* check for previous item's completion */
if ((tok = gettok()) != T_STRING) /* label specified? */
{ /* If not, stuff unique */
sprintf(tparam,"dummy!%d", n_items); /* dummy name in */
ungettok(tok);
}
else
{
if (strlen(tparam) > MAX_NAME)
{
error("Item name \"%s\" too long. Max %d chars.",
tparam, MAX_NAME);
tparam[MAX_NAME] = '\0';
}
else for (cp = tparam; c = *cp; cp++)
if (!(isalpha(c) || isdigit(c) || c == '_'))
{
error("Invalid char in identifier name: \"%s\"",
tparam);
*cp = '\0';
break;
}
}
if ((IIp = find_item(tparam)) != NULL) /* Item name found? */
return error("Item name previously used.");
if ((MIp->Items[n_items] = IIp = create_item(tparam))
== NULL)
return ERROR;
in_item = TRUE;
Ip = &IIp->Item;
for (i = 0; i < n_refs; i++) /* check for fwd refs */
if (!strcmp(fwd_refs[i].iname, tparam))
*fwd_refs[i].refp = n_items; /* fill in with item # */
n_items++; /* bump item count */
if ((token = gettok()) != T_COLON) /* optional colon? */
{
ungettok(token); /* if not, all done */
return OK;
}
if ((token = gettok()) == T_STRING) /* short-form text? */
return do_text2(); /* if so, go process */
else
{
ungettok(token); /* else all done */
return OK;
}
}
/************************************************************
* do_opts():
* Process simple "binary" options for prompt,
* pre- and post-clear specifications.
* Note: upon entry, global "token" contains the
* value of the token to be processed.
************************************************************/
int do_opts()
{
if (!in_item)
return error("\"%s\" only valid within an item",
keywords[token].keyword);
switch(token)
{
case T_PROMPT: case T_PAUSE:
case T_NOPROMPT: case T_NOPAUSE:
if (Ip->prompt != DEFAULT)
return error("Prompt option already specified");
Ip->prompt= (token==T_PROMPT || token==T_PAUSE)? YES :NO;
break;
case T_POSTCLEAR: /* these are actually no-ops, */
case T_NOPOSTCLEAR: /* but again, I've left them in */
if (Ip->post_clear != DEFAULT)
return error("Postclear option already specified");
Ip->post_clear = (token == T_POSTCLEAR) ? YES : NO;
break;
case T_PRECLEAR:
case T_NOPRECLEAR:
if (Ip->pre_clear != DEFAULT)
return error("Preclear option already specified");
Ip->pre_clear = (token == T_PRECLEAR) ? YES : NO;
break;
}
return OK;
}
/************************************************************
* do_nextitem():
* Process NEXTIEM option.
************************************************************/
int do_nextitem()
{
int tok;
if (Ip->nextcode != DEFAULT)
error("Nextitem option already specified");
switch (tok = gettok())
{
case T_FIRST:
Ip->nextcode = NXT_FIRST;
break;
case T_LAST:
Ip->nextcode = NXT_LAST;
break;
case T_NEXT:
Ip->nextcode = NXT_NEXT;
break;
case T_STRING:
Ip->nextcode = NXT_DIRECT;
if (find_item(tparam))
Ip->nextitem = item_num;
else
{ /* record forward item reference */
strcpy(fwd_refs[n_refs].iname, tparam);
fwd_refs[n_refs].refp = &Ip->nextitem;
fwd_refs[n_refs++].lineno = lineno;
Ip->nextitem = UNDEF_FWD;
}
break;
default:
ungettok(tok);
return error("Bad Nextitem specification");
}
return OK;
}
/************************************************************
* do_text():
* Process Text parameter
************************************************************/
int do_text()
{
int tok;
if (!in_item)
return error("Text clause must be within an item");
if (*Ip->text)
return error("Text clause already specified for this item");
if ((tok = gettok()) != T_STRING)
{
ungettok(tok);
return error("Text clause specified without the text.");
}
return do_text2();
}
/************************************************************
* do_text():
* Continued TEXT clause processing, shared between
* do_text() and do_item().
************************************************************/
int do_text2()
{
if (strlen(tparam) > MAX_TXTWID)
{
*Ip->text = 'x'; /* to avoid "missing text" error */
return error("Text is too long; maximum %d chars",
MAX_TXTWID);
}
else
strcpy(Ip->text, tparam);
if (strlen(tparam) > Mp -> widest)
Mp -> widest = strlen(tparam);
return OK;
}
/************************************************************
* do_action():
* Process standard action, Exit, Lmenu or Emenu clause
************************************************************/
int do_action()
{
int tok;
int old_acttyp = Ip->acttyp;
if (!in_item)
return error("%s clause only valid within an item",
keywords[token].keyword);
if (token == T_EXIT || (tok = gettok()) == T_EXIT)
Ip->acttyp = ACT_EXIT;
else
if (tok != T_STRING)
{
ungettok(tok);
error("Incomplete %s specification",
keywords[token].keyword);
}
else
if (strlen(tparam) > MAX_CMD)
error("%s parameter too long (max %d chars)",
keywords[token].keyword, MAX_CMD);
else
switch(token)
{
case T_ACTION:
strcpy(Ip->action, tparam);
Ip->acttyp = ACT_CMND;
break;
case T_LMENU:
if (find_menu(tparam) != NULL) /* named menu defined? */
Ip->lmenunum = menu_num; /* yes. */
else
{ /* no. create entry */
MInfo[n_menus] = create_menu(tparam);
if (fatal)
return ERROR; /* creation error */
else
Ip->lmenunum = n_menus++; /* OK; assign. */
}
Ip->acttyp = ACT_LMENU;
break;
case T_EMENU:
strcpy(Ip->action, tparam);
Ip->acttyp = ACT_EMENU;
break;
}
if (old_acttyp)
return error("Only one Action clause allowed per item");
return OK;
}
/************************************************************
* do_help():
* Process help clause.
************************************************************/
int do_help()
{
int tok;
if (!in_item)
return error("Help clause only valid within an item");
if ((tok = gettok()) != T_STRING)
{
ungettok(tok);
return error("No Help text specified");
}
if (strlen(tparam) > MAX_HELP)
return error("Help text too long (max %d chars)",
MAX_HELP);
if (*Ip->help)
return error("Only one help line allowed per item");
strcpy(Ip->help, tparam);
return OK;
}
/************************************************************
* do_err():
* Diagnose hopelessly bad syntax (i.e., encountering a
* totally unexpected keyword)
************************************************************/
int do_err()
{
return fatalerr("Unrecoverable Syntax error.");
}