home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
QBasic & Borland Pascal & C
/
Delphi5.iso
/
C
/
Samples
/
CSAPE32.ARJ
/
SOURCE
/
CSSRC
/
MENU.C
< prev
next >
Wrap
C/C++ Source or Header
|
1990-12-19
|
35KB
|
1,316 lines
/*
menu.c
% menu_Open, menu_Printf, etc.
Menu Generator
C-scape 3.2
Copyright (c) 1986, 1987, 1988 by Oakland Group, Inc.
ALL RIGHTS RESERVED.
written by stephen ng.
rewritten by Joe DeSantis.
Revision History:
-----------------
3/02/86 sng moved static row, col, and fieldno variables in Printf
to the data structure, so that you can now make
more than one menu.
3/14/86 sng added new function GetRecordVar to support changed
field updating.
3/22/86 sng rewrote menu_printf to accept % expansion.
3/23/86 sng fixed bug that assumed the textbuf had as many lines
as the menu.
3/27/86 sng rewrote set_fa to allow % expansion inside fields.
4/06/86 sng permitted (per documentation) multiple flushes.
4/16/86 sng menu_Printf now
1) takes objects of different sizes
2) uses new @ syntax instead of & syntax
4/22/86 sng rewrote menu_printf as state machine, now does quoting
stack, state are both global vars. added (last week)
grid, and support for up, down, left, right fields
4/24/86 sng added menu_Merge2Record, address of string no longer
passed in menu_Printf.
4/28/86 sng added field data manipulators.
5/05/86 sng removed "variable" string, because field now holds
that information.
5/19/86 sng assert now checks to see that closed items aren't used
5/22/86 sng added color parsing support.
5/24/86 sng added type support.
6/16/86 sng menus with no fields in them are now acceptable.
7/08/86 sng fixed make_grid NULL pointer assignment bug,
put asserts on a single line for easier grep'ing,
fixed memory non-deallocation bug in menu_Close.
7/09/86 sng #defined out xprint for size.
7/11/86 sng made fields xarray.
7/19/86 sng removed memory hacks--menu_Printf now returns all
insufficient memory conditions via dos errno variable
menu_Close checks to see if it's necessary to flush
first.
10/21/86 sng double %'s weren't handled in percent()--now they are
10/31/86 jmd removed ifdefs and externs
10/31/86 jmd menu_Open now checks if the text buf opened OK.
11/06/86 sng restored some documentation
12/03/86 jmd set_color is now a macro
documented menu_Printf()
fixed '@['
made state a local variable.
cleaned up state machine.
12/04/86 jmd added repeat syntax.
12/18/86 jmd Adjusted tb_ commands.
12/30/86 jmd Check return value of xa_make()
1/14/87 jmd Made default TB size a static variable so that it
can be modified at run time if needed.
1/26/87 jmd Removed menu width restrictions.
2/07/87 jmd Rewrote menu_Printf with new token parser.
set_color is now done in line.
2/08/87 jmd check length of token_buf as chars are added.
process when ever token_buf is full. This
eliminates the 512 char limit.
2/11/87 jmd took strlen out of menu_SetTB
2/21/87 jmd replaced asserts
3/19/87 jmd Increased percent buffer to 511 bytes
4/20/87 jmd passed menu to support routines to improve
error messages
4/21/87 jmd fixed repeat syntax, added @fpd[]
5/04/87 jmd Replaced the grid
5/07/87 jmd added grid hint, rewrote percent, got rid of strchr
5/26/87 jmd split up files.
6/05/87 jmd added check for end of string
7/23/87 jmd removed quote condition for colors ---> "@c[@]]"
9/09/87 jmd added NO_PROTO option
9/23/87 jmd added STACK_PLUS option, field_funcs_ptr definition.
11/15/87 jmd changed memcpy to memmove
11/16/87 jmd put back quote condition for colors ---> "@c[@]]"
3/06/88 jmd UNIX symbol is now CS_UNIX
3/08/88 jmd Added STRATUS decls, added FANCY_PCT check
3/28/88 jmd Changed percent() so that it no longer temporarily
alters the format string.
4/10/88 jmd Added call to AddFieldToGrid()
Removed menu_GetSize function
4/11/88 jmd added width to field_Open
4/12/88 jmd added @fw[] syntax to menu_Printf()
4/30/88 jmd moved buffers into menu structure
5/09/88 jmd added WIDE fields
5/12/88 jmd fixed test for missing repeat string (p became *p)
5/19/88 jmd fixed update of menu->col when using field widths
6/23/88 jmd converted generic pointers to VOID*
6/24/88 jmd converted to new xarray calls
8/16/88 jmd added calls to omalloc, oak_Errno
8/20/88 jmd add list of screen objects
made set_field() a little nicer
added stack_inc macro
8/21/88 jmd rewrote, menu_Printf now pulls field args off the
stack as commands are received, no more static data
field names and object commands now parsed
9/28/88 jmd Added @a[] support, fixed menu_Flush (again),
removed spiel about menus at top of file (finally)
10/05/88 jmd Removed start size
Added %G, %i, %hd... %Lf... support to menu_Printf
11/30/88 jmd Add o_itoa
12/02/88 jmd Fixed %s == "" bug in next_char,
added error handling to menu_Open
12/20/88 jmd removed Errno test
2/02/89 jdc independent bob's shouldn't have their position set
2/07/89 jmd fixed Lattice uninitialized variable warning
3/29/89 jmd Added CSPRIV modifier
4/21/89 jmd added new @fD syntax
(makes data pointers without initializing them)
4/24/89 jdc added funcnamea for LNF
5/29/89 jdc added varnamea for LNF
7/02/89 jdc added frowcount change
8/10/89 jmd Field widths can now be 0
8/20/89 jmd fixed '\n' in repeat syntax
8/24/89 jmd added call to tb_GetCol
11/04/89 jdc changed toupper & tolower to otoupper & otolower
11/29/89 jmd added casts for DG
12/10/89 jmd added ovarargs, made create data private
12/11/89 jmd added @fh, field highlighting command
12/17/89 jdc fixed cursor positioning (row in wrap mode)
1/05/90 jmd allows negative hichar values
3/28/90 jmd ansi-fied
4/23/90 jmd added tag to enum
4/24/90 jmd added ifdef to menu_P declaration
5/17/90 jmd put in a comment to fix de-ansi endif problem
8/05/90 jmd added test for sed position when connecting a bob
8/07/90 jdc fixed @a on '\n' bug, added direct bbc_Attr call
9/24/90 jmd added test for tokbuf realloc failure
10/20/90 bkd changed ifdef OVARARG to O_VARARG.
11/01/90 ted put (void) in arg list of menu_Open.
12/18/90 jdc added ja_Ok() in menu_GetField()
Note: Long doubles (%Lf) are not supported. #define LONG_DOUBLE
and recompile this file if you wish to support them.
*/
#include "menu.h"
#include "oakarg.h" /* for variable argument macros */
#include "bordobj.h" /* we need border stuff for bobs */
#include <ctype.h>
/***** Static Functions *****/
OSTATIC char * CSPRIV repeat(menu_type menu, char *tokptr, int *cr_countp);
OSTATIC char * CSPRIV menu_TokBufReSize(menu_type menu, int newsize);
OSTATIC char CSPRIV next_char(menu_type menu, char **p);
OSTATIC void CSPRIV set_pos(char *buffer, menu_type menu);
OSTATIC field_type CSPRIV set_field(menu_type menu, int dcount, int width, int frow, int fcol);
OSTATIC boolean CSPRIV set_tb(menu_type m, char *end);
OSTATIC char * CSPRIV percent(menu_type menu, char *in, char *out);
OSTATIC int CSPRIV atox(char *string);
OSTATIC char * CSPRIV o_itoa(int val, char *out);
/***** Private Data types *****/
/* menu create struct (used by menu_Printf, destroyed by menu_Flush) */
typedef struct _menucreate {
char pct_buf[PCT_BUF_LEN + 1]; /* the percent buffer */
char *pct; /* pointer into the percent buffer */
ova_list argp; /* argument pointer */
xarray dptrs; /* array of field data pointers */
} menu_create;
/*** states for menu_Printf() ***/
typedef enum _mpstate {
RESET,
COMMAND,
REPEAT,
REP_QUOTE,
P_START,
P_BUILD,
P_QUOTE,
A_START,
A_BUILD,
A_QUOTE,
C_START,
C_BUILD,
C_QUOTE,
F_START,
F_BUILD,
F_QUOTE,
F_REPEAT,
F_REP_QUOTE,
F_DCOUNT,
F_NAME,
F_WIDTH,
F_HILITE
} mp_state;
#define TOK_BUF_START 80 /* starting size of the token buffer */
#define TOK_GROW 20 /* size the token buffer grows by */
/* Return TRUE if c is a field command */
#define isfldcmd(c) ((c) == '[' || (c) == 'p' || (c) == 'd' || (c) == 'o' || \
(c) == 'b' || (c) == '{' || (c) == 'w' || (c) == 'h' || (c) == 'D')
/**** Constructors *****/
menu_type menu_Open(void)
/*
DESCRIPTION
This function creates a new menu object and returns a handle to it.
If there is not enough memory to open a new menu object, the
function returns NULL.
RETURNS
menu_Open returns a handle to the new menu object. A NULL pointer
value indicates insufficient memory.
*/
{
menu_type m;
if ((m = (menu_type) omalloc(CSA_MENU, sizeof(struct menu_struct))) == NULL) {
goto Quit1;
}
/* build menu token buffer */
/* get a little extra just in case */
if ((m->token_buf = (char *) omalloc(CSA_TOKBUF, TOK_BUF_START + 3)) == NULL) {
goto Quit2;
}
m->token_size = TOK_BUF_START;
/* build menu sub-components */
m->colcount = 0;
m->rowcount = 1;
m->frowcount = 0;
m->dirty = FALSE;
m->vheight = -1;
m->vwidth = -1;
if ((m->textbuf = tb_Open()) == NULL) {
goto Quit3;
}
if ((m->fa = xa_Open(4)) == NULL) {
goto Quit4;
}
if ((m->fgrid = ia_Open(10)) == NULL) {
goto Quit5;
}
m->fieldcount = 0;
if ((m->boba = ia_Open(4)) == NULL) {
goto Quit6;
}
m->bobcount = 0;
m->namelist = NULL;
m->dptrlist = NULL;
m->varblock = NULL;
m->sedptr = NULL;
m->mp_count = 0; /* count number of times menu_Printf is called */
m->row = m->col = 0;
m->color = DEF_COLOR;
m->old_color = DEF_COLOR;
m->create = NULL; /* Created by menu_Printf as needed */
m->funcnamea = NULL; /* Created by _sfile_loadmenu as needed */
m->functypea = NULL; /* Created by _sfile_loadmenu as needed */
m->varnamea = NULL; /* Created by _sfile_loadmenu as needed */
return(m);
/* error handling: Free memory, return NULL */
Quit6:
ia_Close(m->fgrid);
Quit5:
xa_Close(m->fa);
Quit4:
tb_Close(m->textbuf);
Quit3:
ofree(CSA_TOKBUF, (VOID *) m->token_buf);
Quit2:
ofree(CSA_MENU, (VOID *) m);
Quit1:
return(NULL);
}
#ifdef O_VARARG
boolean menu_Printf(menu, string ova_alist)
menu_type menu;
char *string;
ova_dcl /* NO semicolon */
#else
boolean menu_Printf(menu_type menu, char *string ova_alist)
/* put this comment here to make the de-ansifier work */
#endif
/*
requires: a control_string and the right number of arguments (see
documentation).
modifies: menu.
effects: printf the control string and arguments into the menu.
repeats are allowed in text and in fields but not in @p, @c, or @[.
*/
{
register mp_state state = RESET;
register int i;
field_type field;
bob_type bob;
char c;
char *tok, *rpt;
boolean quit;
int size, hgt, wid, cr_count;
char *p, *token_buf;
boolean a_ishex; /* @a info */
boolean skip = FALSE; /* used for @f parsing */
menu_create *createp; /* pointer to create data */
VOID *var; /* field info */
field_funcs_ptr funcs;
int frow, fcol;
boolean protect, fdinit;
int dcount, fwidth, nameno, fhilite;
/* Create create data if there is none */
if (menu->create == NULL) {
if ((menu->create = (VOID *) omalloc(CSA_MCREATE, sizeof(menu_create))) == NULL) {
return(FALSE);
}
/* create create xarray */
if ((((menu_create *)(menu->create))->dptrs = xa_Open(10)) == NULL) {
ofree(CSA_MCREATE, menu->create);
return(FALSE);
}
}
createp = (menu_create *) menu->create;
token_buf = menu->token_buf;
menu->mp_count++; /* count number of times menu_Printf is called */
cs_Assert(menu_Ok(menu), CS_M_P_MENU, 0);
ova_start(createp->argp, string);
/* Initialize buffers. */
createp->pct_buf[0] = '\0';
createp->pct = createp->pct_buf;
tok = token_buf;
rpt = token_buf;
c = ' ';
p = string;
/* watch length of token_buf */
/* compare against menu->token_size when adding chars to token_buf */
for (quit = FALSE; !quit; ) {
if (!skip) { /* skip during field creation sometimes */
c = next_char(menu, &p);
/* end of string should only occur in RESET state */
cs_Assert((state == RESET || c != '\0'), CS_M_P_EOL, menu->mp_count);
}
switch (state) {
case RESET: /* Start */
if (c == '@') {
*tok = '\0';
/* add text to tb */
if (!set_tb(menu, tok)) {
goto ERROR;
}
tok = token_buf;
state = COMMAND;
}
else if (c == '\n') { /* process newline */
*tok = '\0';
/* add text to tb */
if (!set_tb(menu, tok)) {
goto ERROR;
}
tok = token_buf;
menu->row++;
menu->col = 0;
menu->old_color = menu->color; /* set correct color */
}
else if (c == '\0') { /* done */
*tok = '\0';
/* add text to tb */
if (!set_tb(menu, tok)) {
goto ERROR;
}
tok = token_buf;
quit = TRUE;
}
else { /* accumulate text */
*tok++ = c;
/* is token_buf filled? */
if (tok - token_buf >= menu->token_size) {
/* flush token_buf */
*tok = '\0';
/* add text to tb */
if (!set_tb(menu, tok)) {
goto ERROR;
}
tok = token_buf;
}
}
break;
case COMMAND: /* @ */
switch (c) {
case 'p':
state = P_START;
break;
case 'f':
/* reset field info */
bob = NULL;
protect = FALSE;
dcount = 0;
fdinit = TRUE;
fwidth = -1;
fhilite = -1;
nameno = -1;
/* pull field var and funcs off the stack */
/* get variable */
var = (VOID *) ova_arg(createp->argp, VOID *);
/* field functions */
funcs = ova_arg(createp->argp, field_funcs_ptr);
/* remember field row, col */
frow = menu->row;
fcol = menu->col;
/* process field commands */
state = F_START;
break;
case 'a':
a_ishex = FALSE;
state = A_START;
break;
case 'c':
state = C_START;
break;
case '[':
state = REPEAT; /* @[ repeat command*/
break;
case '@': /* @@ @] */
case ']':
*tok++ = c; /* add '@' or ']' to tb */
if (tok - token_buf >= menu->token_size) {
*tok = '\0';
/* add text to tb */
if (!set_tb(menu, tok)) {
goto ERROR;
}
tok = token_buf;
}
state = RESET;
break;
default:
cs_Assert(FALSE, CS_M_P_AT, menu->mp_count);
break;
}
break;
case REPEAT: /* @[... */
if (c == '@') {
state = REP_QUOTE;
}
else if (c == ']') {
*tok = '\0'; /* process repeat */
if ((tok = repeat(menu, token_buf, &cr_count)) == NULL) {
goto ERROR;
}
token_buf = menu->token_buf; /* reset in case repeat realloced */
/* if the repeat has newlines in it, don't add it to the tb */
if (cr_count > 0) {
/* advance menu location by '\n' in repeat */
menu->row += cr_count;
menu->col = 0;
}
else {
/* add text to tb */
if (!set_tb(menu, tok)) {
goto ERROR;
}
}
tok = token_buf;
state = RESET; /* reset */
}
else { /* accumulate repeat arg */
*tok++ = c;
cs_Assert((tok - token_buf < menu->token_size), CS_M_P_RLEN, menu->mp_count);
}
break;
case REP_QUOTE: /* throw away quote character */
*tok++ = c; /* add arg character */
cs_Assert((tok - token_buf < menu->token_size), CS_M_P_RLEN, menu->mp_count);
state = REPEAT;
break;
case P_START: /* @p */
cs_Assert(c == '[', CS_M_P_ATP, menu->mp_count);
state = P_BUILD; /* @p[ */
break;
case P_BUILD: /* @p[... get args or ']' */
if (c == '@') {
state = P_QUOTE;
}
else if (c == ']') { /* @p[...] process args */
*tok = '\0';
tok = token_buf;
set_pos(token_buf, menu);
menu->old_color = menu->color; /* set correct color */
state = RESET; /* reset */
}
else { /* accumulate args */
*tok++ = c;
cs_Assert((tok - token_buf < menu->token_size), CS_M_P_PLEN, menu->mp_count);
}
break;
case P_QUOTE: /* throw away quote character */
*tok++ = c; /* add arg character */
cs_Assert((tok - token_buf < menu->token_size), CS_M_P_PLEN, menu->mp_count);
state = P_BUILD;
break;
case A_START: /* @a */
cs_Assert(c == '[', CS_M_P_ATA, menu->mp_count);
state = A_BUILD; /* @a[ */
break;
case A_BUILD: /* @a[ */
if (c == '@') { /* @a[..@ */
state = A_QUOTE;
}
else if (c == ']') { /* @a[...] process arg */
*tok = '\0';
if (a_ishex) {
/* Attribute is a hex digit */
menu->color = (byte) atox(token_buf);
}
else {
menu->color = (byte) atoi(token_buf);
}
tok = token_buf;
state = RESET; /* reset */
}
else if (c == 'x' || c == 'X') {
tok = token_buf; /* reset token buf; skip x */
a_ishex = TRUE;
}
else { /* accumulate args */
*tok++ = c;
cs_Assert((tok - token_buf < menu->token_size), CS_M_P_ALEN, menu->mp_count);
}
break;
case A_QUOTE: /* throw away quote character */
*tok++ = c; /* add arg character */
cs_Assert((tok - token_buf < menu->token_size), CS_M_P_ALEN, menu->mp_count);
state = A_BUILD;
break;
case C_START: /* @c */
cs_Assert(c == '[', CS_M_P_ATC, menu->mp_count);
state = C_BUILD; /* @c[ */
break;
case C_BUILD: /* @c[ */
if (c == '@') { /* @c[..@ */
state = C_QUOTE;
}
else if (c == ']') { /* @c[...] process args */
menu->color = (byte) *token_buf; /* set the menu color */
tok = token_buf;
state = RESET; /* reset */
}
else { /* accumulate args */
*tok++ = c;
cs_Assert((tok - token_buf < menu->token_size), CS_M_P_CLEN, menu->mp_count);
}
break;
case C_QUOTE: /* throw away quote character */
*tok++ = c; /* add arg character */
cs_Assert((tok - token_buf < menu->token_size), CS_M_P_CLEN, menu->mp_count);
state = C_BUILD;
break;
case F_START: /* @f */
skip = FALSE; /* reset skip */
switch (c) {
case '[':
state = F_BUILD; /* @f[ */
break;
case 'p':
protect = TRUE;
break;
case 'd': /* parse count */
state = F_DCOUNT;
break;
case 'D': /* parse count (don't init) */
fdinit = FALSE;
state = F_DCOUNT;
break;
case 'w': /* parse width */
state = F_WIDTH;
break;
case 'h': /* parse highlighting */
state = F_HILITE;
break;
case 'b':
/* get bob from stack */
bob = ova_arg(createp->argp, bob_type);
break;
case 'm':
break;
case '{':
state = F_NAME; /* get name */
break;
default:
cs_Assert(FALSE, CS_M_P_ATF, menu->mp_count);
break;
}
break;
case F_DCOUNT: /* @fd */
if (isfldcmd(c)) {
*tok = '\0';
tok = token_buf;
dcount = atoi(token_buf); /* get data count */
if (dcount <= 0) {
dcount = 1;
}
if (fdinit) {
/* pull data pointers off arg lists,
store them in the create data xarray for now
*/
for (i = 0; i < dcount; i++) {
xa_Put(createp->dptrs, i, ova_arg(createp->argp, VOID *));
}
}
/* skip to process new command */
skip = TRUE;
state = F_START;
}
else if (isdigit(c)) {
*tok++ = c; /* add arg character */
}
else {
cs_Assert(FALSE, CS_M_P_ATF, menu->mp_count);
}
break;
case F_NAME: /* @f{... */
if (c == '}') {
*tok = '\0';
tok = token_buf;
/* put name into name list */
nameno = menu_AddName(menu, tok, menu->fieldcount);
state = F_START;
}
else {
*tok++ = c; /* add name character */
/* error: name too long */
cs_Assert((tok - token_buf < NAME_MAXLEN), CS_M_P_NAME, menu->mp_count);
}
break;
case F_WIDTH: /* @fw */
if (isfldcmd(c)) {
*tok = '\0';
tok = token_buf;
fwidth = atoi(token_buf); /* get field width */
if (fwidth < 0) {
fwidth = -1;
}
/* skip to process new command */
skip = TRUE;
state = F_START;
}
else if (isdigit(c)) {
*tok++ = c; /* add arg character */
}
else {
cs_Assert(FALSE, CS_M_P_ATF, menu->mp_count);
}
break;
case F_HILITE: /* @fh */
if (isfldcmd(c)) {
*tok = '\0';
tok = token_buf;
fhilite = atoi(token_buf); /* get hilite char */
if (fhilite < 0) {
fhilite = -1;
}
/* skip to process new command */
skip = TRUE;
state = F_START;
}
else if (isdigit(c) || c == '-') { /* allow negative hichar */
*tok++ = c; /* add arg character */
}
else {
cs_Assert(FALSE, CS_M_P_ATF, menu->mp_count);
}
break;
case F_BUILD: /* @f(pd)[ */
if (c == '@') {
state = F_QUOTE;
}
else if (c == ']') { /* @f[...] process field */
*tok = '\0';
tok = token_buf;
/* put a new field into the menu */
if ((field = set_field(menu, dcount, fwidth, frow, fcol)) == NULL) {
goto ERROR;
}
/* set the field's var and funcs, etc. */
field_SetVar(field, var);
field_SetFuncs(field, funcs);
field_SetProtected(field, protect);
field_SetNameNo(field, nameno);
if (fhilite >= 0) {
field_SetHiChar(field, fhilite);
}
wid = field_GetWidth(field);
hgt = 1;
if (bob != NULL) {
/* add bob to list, attach it to field */
/* the list contains the field no the bob is attached to */
if (!menu_SetFieldBob(menu, menu->fieldcount - 1, bob)) {
goto ERROR;
}
/* only dependent bobs get their position set,
and modify the master menu size */
if (bob_IsDepend(bob)) {
if (menu->sedptr == NULL) {
bord_SetPosition(bob, frow, fcol);
}
else {
/* compensate for sed position if it exists */
bord_SetPosition(bob, frow + win_GetTopRow(menu->sedptr),
fcol + win_GetLeftCol(menu->sedptr));
}
hgt = bord_GetHeight(bob);
wid = bord_GetWidth(bob);
}
}
/* keep track of menu size, including bobs */
menu->colcount = (menu->col + wid > menu->colcount) ?
menu->col + wid :
menu->colcount;
menu->frowcount = (menu->row + hgt > menu->frowcount) ?
menu->row + hgt :
menu->frowcount;
menu->col += field_GetWidth(field);
if (fdinit) {
/* initialize the field's data pointers,
pull them out of the xarry we stashed them in.
*/
for (i = 0; i < dcount; i++) {
field_InitData(field, xa_Get(createp->dptrs, i), i);
}
}
state = RESET; /* reset state machine */
}
else { /* add char */
*tok++ = (c == '#' ? CS_INPUT : c);
if ((size = (tok - token_buf)) >= menu->token_size) {
if ((token_buf = menu_TokBufReSize(menu, size + TOK_GROW)) == NULL) {
goto ERROR;
}
tok = token_buf + size;
}
}
break;
case F_QUOTE: /* @f[...@ */
if (c == '[') { /* @[ repeat command*/
rpt = tok; /* initialize repeat */
state = F_REPEAT;
}
else { /* throw away quote character*/
*tok++ = c; /* add arg character */
if ((size = (tok - token_buf)) >= menu->token_size) {
if ((token_buf = menu_TokBufReSize(menu, size + TOK_GROW)) == NULL) {
goto ERROR;
}
tok = token_buf + size;
}
state = F_BUILD;
}
break;
case F_REPEAT: /* @f[...@[... */
if (c == '@') {
state = F_REP_QUOTE;
}
else if (c == ']') {
*tok = '\0';
if ((tok = repeat(menu, rpt, &cr_count)) == NULL) {
goto ERROR;
}
token_buf = menu->token_buf; /* reset in case repeat realloced */
state = F_BUILD; /* continue... */
}
else { /* accumulate repeat arg */
*tok++ = (c == '#' ? CS_INPUT : c);
if ((size = (tok - token_buf)) >= menu->token_size) {
if ((token_buf = menu_TokBufReSize(menu, size + TOK_GROW)) == NULL) {
goto ERROR;
}
tok = token_buf + size;
}
}
break;
case F_REP_QUOTE: /* throw out quote character */
*tok++ = c; /* add arg character */
cs_Assert((tok - token_buf < menu->token_size), CS_M_P_FLEN, menu->mp_count);
state = F_REPEAT; /* continue from where we were */
break;
default:
cs_Assert(FALSE, CS_M_P_STATE, menu->mp_count);
}
}
ova_end(createp->argp);
return(TRUE);
ERROR:
ova_end(createp->argp);
return(FALSE);
}
/***** menu_Printf support. these are static, CSPRIV functions *****/
static char * CSPRIV repeat(menu_type menu, char *tokptr, int *cr_countp)
/*
Evaluates a repeat command and stuffs the result into the
menu's token buffer at the location pointed to by tokptr.
Resizes the token buffer if necessary.
@[3,xyz] ==> "xyzxyzxyz"
Returns a pointer to the end of the string.
Note: the token buffer could be different after this call (realloc).
*/
{
int count, len, offset, i, off;
char *p;
count = atoi(tokptr);
for (off = 0; tokptr[off] != ',' && tokptr[off] != '\0'; off++) {
;
}
cs_Assert(tokptr[off] != '\0', CS_M_P_RARG, menu->mp_count);
off++;
/* compute size of repeat string, and number of '\n's */
*cr_countp = 0;
for (p = tokptr + off; *p != '\0'; p++) {
if (*p == '\n') {
(*cr_countp)++;
}
}
len = p - (tokptr + off);
cs_Assert(len > 0, CS_M_P_RARG, menu->mp_count);
/* check for too big */
cs_Assert(count >= 0 && (count * len) <= MAX_FIELD_LEN, CS_M_P_RVAL, menu->mp_count);
/* Do we need to resize token buffer? */
offset = (int) (tokptr - menu->token_buf);
if ((offset + (count * len)) >= menu->token_size) {
if (menu_TokBufReSize(menu, (offset + (count * len)) + TOK_GROW) == NULL) {
return(NULL);
}
tokptr = menu->token_buf + offset;
}
p = tokptr + off;
if (len == 1) {
memset((VOID *) tokptr, *p, count);
tokptr[count] = '\0';
}
else {
memmove((VOID *) tokptr, p, len);
for (i = count, p = tokptr + len; i > 1; i--, p += len) {
memmove((VOID *) p, (VOID *) tokptr, len);
}
*p = '\0';
}
/* multiply '\n's by repeat count */
(*cr_countp) *= count;
return(tokptr + (count * len));
}
static char * CSPRIV menu_TokBufReSize(menu_type menu, int newsize)
/*
reallocates the menu's token buffer to newsize.
returns a pointer to the new buffer.
returns NULL if it fails.
the newsize must be larger than the previous size.
*/
{
/* Check if field is too long (same errno as above) */
cs_Assert(newsize <= MAX_FIELD_LEN + TOK_GROW, CS_M_P_FLEN, menu->mp_count);
/* increase size */
menu->token_size = newsize;
/* get a little extra just in case */
menu->token_buf = (char *) orealloc(CSA_TOKBUF, menu->token_buf, menu->token_size + 3);
return(menu->token_buf);
}
static char CSPRIV next_char(menu_type menu, char **p)
/*
This routine returns the next format character to be processed.
If there is a '%' substitution, the substitution is shoved
into the percent buffer, and then spit out until it is empty.
'p' is the address of the pointer to format string.
*/
{
char ret;
menu_create *createp; /* pointer to create data */
createp = (menu_create *) menu->create;
while (1) {
/* Loop until we find a character or a valid percent expansion */
if (*(createp->pct) != '\0') {
ret = *((createp->pct)++);
break;
}
else if (**p != '%') {
ret = *((*p)++);
break;
}
else {
/* put test char at end of pct_buf */
createp->pct_buf[PCT_BUF_LEN] = '\0';
*p = percent(menu, *p, createp->pct = createp->pct_buf);
/* if test char has changed we've overrun pct_buf */
cs_Assert(createp->pct_buf[PCT_BUF_LEN] == '\0', CS_M_NC_PCTOVF, menu->mp_count);
}
}
return(ret);
}
static void CSPRIV set_pos(char *buffer, menu_type menu)
/*
modifies: menu.
Processes the arguments in buffer and changes the menu state
information accordingly.
if there is only one argument set the col only.
*/
{
char *p;
/* get arguments */
/* hold value in col for now */
menu->col = atoi(buffer);
for (p = buffer; *p != ',' && *p != '\0'; p++) {
;
}
/* if there are two args then set the row */
if (*p != '\0') {
menu->row = menu->col;
menu->col = atoi(++p);
}
}
static field_type CSPRIV set_field(menu_type menu, int dcount, int width, int frow, int fcol)
/*
modifies: fa, row, col.
The field spec is in the menu->token_buf.
effects: creates a new field, puts it into the menu.
returns the field
*/
{
field_type field;
int fieldno;
cs_Assert(menu_Ok(menu), CS_M_SF_MENU, menu->mp_count);
/* create a new field */
if ( (field =
field_Open(menu->token_buf, width, (dcount <= 0) ? 1 : dcount) ) == NULL) {
return(NULL);
}
fieldno = menu->fieldcount;
/* put new field into array */
if (!xa_Put(menu->fa, fieldno, (VOID *) field)) {
field_Close(field);
return(NULL);
}
/* place the field into the grid */
if (!menu_AddFieldToGrid(menu, fieldno, frow, fcol)) {
field_Close(field);
return(NULL);
}
menu->fieldcount++;
return(field);
}
static boolean CSPRIV set_tb(menu_type m, char *end)
/*
effects: prints menu->token_buf into textbuf.
end points to the end of the current token.
returns: TRUE if successful
*/
{
char *buffer;
int len;
/* ignore if nothing there */
if ((buffer = m->token_buf) == end) {
/* color change on the the '\n' */
if (m->color != m->old_color) {
return(bbc_Attr(m->textbuf->bbc, (long)m->textbuf->len - 1, m->color, 1));
}
return(TRUE);
}
len = (int) (end - buffer);
if (!menu_Puts(m, m->row, m->col, buffer, len, m->color, m->old_color)) {
return(FALSE);
}
m->old_color = m->color;
m->row = tb_GetRow(m->textbuf);
m->col = tb_GetCol(m->textbuf);
return(TRUE);
}
static char * CSPRIV percent(menu_type menu, char *in, char *out)
/*
effects: does '%' substitution on the in string; places results into
out string.
modifies: out.
returns: the place in 'in' where conversion finished.
note: It is an error for the % expression to be longer
than 40 characters!
%*d not supported
*/
{
char *last, fmt[41];
int len;
boolean exit;
menu_create *createp; /* pointer to create data */
createp = (menu_create *) menu->create;
/* handle %d and %s as special cases */
if (*(in + 1) == 'd') {
o_itoa(ova_arg(createp->argp, int), out);
return(in + 2);
}
else if (*(in + 1) == 's') {
strcpy(out, ova_arg(createp->argp, char *));
return(in + 2);
}
/* find the spec string for sprintf */
last = in + 1;
exit = FALSE;
while(!exit) {
switch (*last) {
case 'c':
case 'd':
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
case 'i':
case 'o':
case 's':
case 'u':
case 'x':
case 'X':
case '%':
case '\0':
len = last - in + 1;
cs_Assert(len < 40, CS_M_P_CONV, menu->mp_count);
memmove((VOID *) fmt, (VOID *) in, len);
fmt[len] = '\0';
exit = TRUE;
break;
default:
last++;
break;
}
}
if ((*(last-1) == 'h') &&
(*last == 'd' || *last == 'u' || *last == 'o' ||
*last == 'i' || *last == 'x' || *last == 'X')) {
sprintf(out, fmt, ova_arg(createp->argp, short));
}
else if ((*(last-1) == 'l') &&
(*last == 'd' || *last == 'u' || *last == 'o' ||
*last == 'i' || *last == 'x' || *last == 'X')) {
sprintf(out, fmt, ova_arg(createp->argp, long));
}
#ifdef LONG_DOUBLE /* define this if you want to support long doubles */
else if ((*(last-1) == 'L') &&
(*last == 'e' || *last == 'E' || *last == 'f' ||
*last == 'G' || *last == 'g')) {
sprintf(out, fmt, ova_arg(createp->argp, long double));
}
#endif
else if (*last == 'e' || *last == 'E' || *last == 'f' ||
*last == 'G' || *last == 'g') {
sprintf(out, fmt, ova_arg(createp->argp, double));
}
else if (*last == 'd' || *last == 'i' || *last == 'u' || *last == 'o' ||
*last == 'x' || *last == 'X' || *last == 'c') {
sprintf(out, fmt, ova_arg(createp->argp, int));
}
else if (*last == 's') { /* in */
sprintf(out, fmt, ova_arg(createp->argp, char *));
}
else if (*last == '%') { /* two %'s */
out[0] = '%';
out[1] = '\0';
}
else {
cs_Assert(FALSE, CS_M_P_CONV, menu->mp_count);
}
return(last + 1);
}
static int CSPRIV atox(char *string)
/*
Convert a hex number to an integer.
Leading spaces and minus signs are NOT allowed.
This replaces the call to nasty sscanf.
*/
{
char *p;
int val = 0;
for (p = string; isxdigit(*p); p++) {
val = 16 * val + (isdigit(*p) ? (*p - '0') : (otolower(*p) - 'a' + 10));
}
return(val);
}
static char * CSPRIV o_itoa(int val, char *out)
/*
Convert an integer to a string
(replacement for itoa)
*/
{
int digit, sign = 0;
char *p, *q, c;
if (val == 0) {
out[0] = '0';
out[1] = '\0';
return(out);
}
if (val < 0) {
val = -val;
sign = 1;
}
for(p = out; val > 0; val = val / 10, p++) {
digit = val % 10;
*p = (char) (digit + '0');
}
if (sign) {
*p++ = '-';
}
*p = '\0';
/* result is in wrong order, reverse it */
for (q = out, p--; q < p; q++, p--) {
c = *p;
*p = *q;
*q = c;
}
return(out);
}
/**** more menu functions ****/
void menu_Flush(menu_type menu)
/*
If a menu_create struct has been created, destroy it
else do nothing.
*/
{
cs_Assert(menu_Ok(menu), CS_M_F_MENU, 0);
if (menu->create != NULL) {
xa_Close(((menu_create *)(menu->create))->dptrs);
ofree(CSA_MCREATE, (VOID *) menu->create);
menu->create = NULL;
}
}
/**** Mutators *****/
boolean menu_Ok(menu_type menu)
/*
returns: TRUE if passed a good menu, FALSE otherwise.
*/
{
if (menu == NULL || menu->textbuf == NULL || menu->fa == NULL ||
menu->fgrid == NULL || menu->rowcount < 0 || menu->colcount < 0 ||
menu->fieldcount < 0 ) {
cs_Assert(menu->rowcount != -555 && menu->colcount != -555 && menu->fieldcount != -555, CS_M_OK_CLS, 0);
return(FALSE);
}
return(TRUE);
}
/**** Misc ****/
field_type menu_GetField(menu_type menu, int fld)
/*
Extracts a field from the field array.
*/
{
return((!ja_Ok(menu->fa)) ? NULL : (field_type) xa_Get(menu->fa, fld));
}