ngspice/src/frontend/control.c

947 lines
30 KiB
C

/**********
Copyright 1990 Regents of the University of California. All rights reserved.
Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
**********/
/* The front-end command loop. */
#include "ngspice/ngspice.h"
#include "ngspice/cpdefs.h"
#include "control.h"
#include "com_cdump.h"
#include "variable.h"
#include "ngspice/fteext.h"
/* Return values from doblock(). I am assuming that nobody will use
* these characters in a string. */
#define NORMAL '\001'
#define BROKEN '\002'
#define CONTINUED '\003'
#define NORMAL_STR "\001"
#define BROKEN_STR "\002"
#define CONTINUED_STR "\003"
/* Are we waiting for a command? This lets signal handling be
* more clever. */
bool cp_cwait = FALSE;
char *cp_csep = ";";
bool cp_dounixcom = FALSE;
/* We have to keep the control structures in a stack, so that when we
* do a 'source', we can push a fresh set onto the top... Actually
* there have to be two stacks -- one for the pointer to the list of
* control structs, and one for the 'current command' pointer... */
struct control *control[CONTROLSTACKSIZE];
struct control *cend[CONTROLSTACKSIZE];
int stackp = 0;
/* If there is an argument, give this to cshpar to use instead of
* stdin. In a few places, we call cp_evloop again if it returns 1 and
* exit (or close a file) if it returns 0... Because of the way
* sources are done, we can't allow the control structures to get
* blown away every time we return -- probably every time we type
* source at the keyboard and every time a source returns to keyboard
* input is ok though -- use ft_controlreset. */
/* Notes by CDHW:
* This routine leaked like a sieve because each getcommand() created a
* wordlist that was never freed because it might have been added into
* the control structure. I've tackled this by making sure that everything
* put into the cend[stackp] is a copy. This means that wlist can be
* destroyed safely
*/
/* no redirection after the following commands (we may need more to add here!) */
static char *noredirect[] = { "stop", "define", NULL };
static struct control *
findlabel(char *s, struct control *ct)
{
while (ct) {
if ((ct->co_type == CO_LABEL) && eq(s, ct->co_text->wl_word))
break;
ct = ct->co_next;
}
return (ct);
}
/* This is also in cshpar.c ... */
static void
pwlist(wordlist *wlist, char *name)
{
wordlist *wl;
if (!cp_debug)
return;
fprintf(cp_err, "%s : [ ", name);
for (wl = wlist; wl; wl = wl->wl_next)
fprintf(cp_err, "%s ", wl->wl_word);
fprintf(cp_err, "]\n");
}
/* CDHW defined functions */
static void
pwlist_echo(wordlist *wlist, char *name) /*CDHW used to perform function of set echo */
{
wordlist *wl;
if ((!cp_echo)||cp_debug) /* cpdebug prints the same info */
return;
fprintf(cp_err, "%s ", name);
for (wl = wlist; wl; wl = wl->wl_next)
fprintf(cp_err, "%s ", wl->wl_word);
fprintf(cp_err, "\n");
}
/*CDHW Remove control structure and free the memory its hogging CDHW*/
static void
ctl_free(struct control *ctrl)
{
if (!ctrl)
return;
wl_free(ctrl->co_cond);
ctrl->co_cond = NULL;
tfree(ctrl->co_foreachvar);
ctrl->co_foreachvar = NULL;
wl_free(ctrl->co_text);
ctrl->co_text = NULL;
ctl_free(ctrl->co_children);
ctrl->co_children = NULL;
ctl_free(ctrl->co_elseblock);
ctrl->co_elseblock = NULL;
ctl_free(ctrl->co_next);
ctrl->co_next = NULL;
tfree(ctrl);
ctrl = NULL;
}
/* Note that we only do io redirection when we get to here - we also
* postpone some other things until now. */
static void
docommand(wordlist *wlist)
{
wordlist *rwlist;
if (cp_debug) {
printf("docommand ");
wl_print(wlist, stdout);
putc('\n', stdout);
}
/* Do all the things that used to be done by cshpar when the line
* was read... */
wlist = cp_variablesubst(wlist);
pwlist(wlist, "After variable substitution");
wlist = cp_bquote(wlist);
pwlist(wlist, "After backquote substitution");
wlist = cp_doglob(wlist);
pwlist(wlist, "After globbing");
pwlist_echo(wlist, "Becomes >");
if (!wlist || !wlist->wl_word) /*CDHW need to free wlist in second case? CDHW*/
return;
/* Now loop through all of the commands given. */
rwlist = wlist;
while (wlist) {
char *s;
int i;
struct comm *command;
wordlist *nextc, *ee;
nextc = wl_find(cp_csep, wlist);
if (nextc == wlist) { /* skip leading `;' */
wlist = wlist->wl_next;
continue;
}
/* Temporarily hide the rest of the command... */
ee = wlist->wl_prev;
wl_chop(nextc);
wl_chop(wlist);
/* And do the redirection. */
cp_ioreset();
for (i = 0; noredirect[i]; i++)
if (eq(wlist->wl_word, noredirect[i]))
break;
if (!noredirect[i])
if ((wlist = cp_redirect(wlist)) == NULL) {
cp_ioreset();
return;
}
/* Get rid of all the 8th bits now... */
cp_striplist(wlist);
s = wlist->wl_word;
/* Look for the command in the command list. */
for (i = 0; cp_coms[i].co_comname; i++)
if (strcasecmp(cp_coms[i].co_comname, s) == 0)
break;
command = &cp_coms[i];
/* Now give the user-supplied command routine a try... */
if (!command->co_func && cp_oddcomm(s, wlist->wl_next))
goto out;
/* If it's not there, try it as a unix command. */
if (!command->co_comname) {
if (cp_dounixcom && cp_unixcom(wlist))
goto out;
fprintf(cp_err, "%s: no such command available in %s\n",
s, cp_program);
goto out;
/* If it hasn't been implemented */
} else if (!command->co_func) {
fprintf(cp_err, "%s: command is not implemented\n", s);
goto out;
/* If it's there but spiceonly, and this is nutmeg, error. */
} else if (ft_nutmeg && command->co_spiceonly) {
fprintf(cp_err, "%s: command available only in spice\n", s);
goto out;
}
/* The command was a valid spice/nutmeg command. */
{
int nargs = wl_length(wlist->wl_next);
if (nargs < command->co_minargs) {
if (command->co_argfn) {
command->co_argfn (wlist->wl_next, command);
} else {
fprintf(cp_err, "%s: too few args.\n", s);
}
} else if (nargs > command->co_maxargs) {
fprintf(cp_err, "%s: too many args.\n", s);
} else {
command->co_func (wlist->wl_next);
}
}
out:
wl_append(ee, wlist);
wl_append(wlist, nextc);
if (!ee)
rwlist = wlist;
wlist = nextc;
}
wl_free(rwlist);
/* Do periodic sorts of things... */
cp_periodic();
cp_ioreset();
}
/* Execute a block. There can be a number of return values from this routine.
* NORMAL indicates a normal termination
* BROKEN indicates a break -- if the caller is a breakable loop,
* terminate it, otherwise pass the break upwards
* CONTINUED indicates a continue -- if the caller is a continuable loop,
* continue, else pass the continue upwards
* Any other return code is considered a pointer to a string which is
* a label somewhere -- if this label is present in the block,
* goto it, otherwise pass it up. Note that this prevents jumping
* into a loop, which is good.
*
* Note that here is where we expand variables, ``, and globs for
* controls.
*
* The 'num' argument is used by break n and continue n. */
static char *
doblock(struct control *bl, int *num)
{
struct control *ch, *cn = NULL;
wordlist *wl, *wltmp;
char *i, *wlword;
int nn;
nn = *num + 1; /*CDHW this is a guess... CDHW*/
switch (bl->co_type) {
case CO_WHILE:
if (!bl->co_children) {
fprintf(cp_err, "Warning: Executing empty 'while' block.\n");
fprintf(cp_err, " (Use a label statement as a no-op to suppress this warning.)\n");
}
while (bl->co_cond && cp_istrue(bl->co_cond)) {
if (!bl->co_children) cp_periodic(); /*CDHW*/
for (ch = bl->co_children; ch; ch = cn) {
cn = ch->co_next;
i = doblock(ch, &nn);
switch (*i) {
case NORMAL:
break;
case BROKEN: /* Break. */
if (nn < 2) {
return (NORMAL_STR);
} else {
*num = nn - 1;
return (BROKEN_STR);
}
case CONTINUED: /* Continue. */
if (nn < 2) {
cn = NULL;
break;
} else {
*num = nn - 1;
return (CONTINUED_STR);
}
default:
cn = findlabel(i, bl->co_children);
if (!cn)
return (i);
}
}
}
break;
case CO_DOWHILE:
do {
for (ch = bl->co_children; ch; ch = cn) {
cn = ch->co_next;
i = doblock(ch, &nn);
switch (*i) {
case NORMAL:
break;
case BROKEN: /* Break. */
if (nn < 2) {
return (NORMAL_STR);
} else {
*num = nn - 1;
return (BROKEN_STR);
}
case CONTINUED: /* Continue. */
if (nn < 2) {
cn = NULL;
break;
} else {
*num = nn - 1;
return (CONTINUED_STR);
}
default:
cn = findlabel(i, bl->co_children);
if (!cn)
return (i);
}
}
} while (bl->co_cond && cp_istrue(bl->co_cond));
break;
case CO_REPEAT:
if (!bl->co_children) {
fprintf(cp_err, "Warning: Executing empty 'repeat' block.\n");
fprintf(cp_err, " (Use a label statement as a no-op to suppress this warning.)\n");
}
if (!bl->co_timestodo) bl->co_timestodo = bl->co_numtimes;
/*bl->co_numtimes: total repeat count
bl->co_numtimes = -1: repeat forever
bl->co_timestodo: remaining repeats*/
while ((bl->co_timestodo > 0) ||
(bl->co_timestodo == -1)) {
if (!bl->co_children) cp_periodic(); /*CDHW*/
if (bl->co_timestodo != -1) bl->co_timestodo--;
/* loop through all stements inside rpeat ... end */
for (ch = bl->co_children; ch; ch = cn) {
cn = ch->co_next;
i = doblock(ch, &nn);
switch (*i) {
case NORMAL:
break;
case BROKEN: /* Break. */
/* before leaving repeat loop set remaining timestodo to 0 */
bl->co_timestodo = 0;
if (nn < 2) {
return (NORMAL_STR);
} else {
*num = nn - 1;
return (BROKEN_STR);
}
case CONTINUED: /* Continue. */
if (nn < 2) {
cn = NULL;
break;
} else {
/* before leaving repeat loop set remaining timestodo to 0 */
bl->co_timestodo = 0;
*num = nn - 1;
return (CONTINUED_STR);
}
default:
cn = findlabel(i, bl->co_children);
if (!cn) {
/* no label found inside repeat loop:
before leaving loop set remaining timestodo to 0 */
bl->co_timestodo = 0;
return (i);
}
}
}
}
break;
case CO_IF:
if (bl->co_cond && cp_istrue(bl->co_cond)) {
for (ch = bl->co_children; ch; ch = cn) {
cn = ch->co_next;
i = doblock(ch, &nn);
if (*i > 2) {
cn = findlabel(i,
bl->co_children);
if (!cn)
return (i);
else
tfree(i);
} else if (*i != NORMAL) {
*num = nn;
return (i);
}
}
} else {
for (ch = bl->co_elseblock; ch; ch = cn) {
cn = ch->co_next;
i = doblock(ch, &nn);
if (*i > 2) {
cn = findlabel(i, bl->co_elseblock);
if (!cn)
return (i);
} else if (*i != NORMAL) {
*num = nn;
return (i);
}
}
}
break;
case CO_FOREACH:
wltmp = cp_variablesubst(cp_bquote(cp_doglob(wl_copy(bl->co_text))));
for (wl = wltmp; wl; wl = wl->wl_next) {
cp_vset(bl->co_foreachvar, CP_STRING, wl->wl_word);
for (ch = bl->co_children; ch; ch = cn) {
cn = ch->co_next;
i = doblock(ch, &nn);
switch (*i) {
case NORMAL:
break;
case BROKEN: /* Break. */
if (nn < 2) {
wl_free(wltmp);
return (NORMAL_STR);
} else {
*num = nn - 1;
wl_free(wltmp);
return (BROKEN_STR);
}
case CONTINUED: /* Continue. */
if (nn < 2) {
cn = NULL;
break;
} else {
*num = nn - 1;
wl_free(wltmp);
return (CONTINUED_STR);
}
default:
cn = findlabel(i, bl->co_children);
if (!cn) {
wl_free(wltmp);
return (i);
}
}
}
}
wl_free(wltmp);
break;
case CO_BREAK:
if (bl->co_numtimes > 0) {
*num = bl->co_numtimes;
return (BROKEN_STR);
} else {
fprintf(cp_err, "Warning: break %d a no-op\n",
bl->co_numtimes);
return (NORMAL_STR);
}
case CO_CONTINUE:
if (bl->co_numtimes > 0) {
*num = bl->co_numtimes;
return (CONTINUED_STR);
} else {
fprintf(cp_err, "Warning: continue %d a no-op\n",
bl->co_numtimes);
return (NORMAL_STR);
}
case CO_GOTO:
wl = cp_variablesubst(cp_bquote(cp_doglob(wl_copy(bl->co_text))));
wlword = wl->wl_word;
wl->wl_word = NULL;
wl_free(wl);
return (wlword);
case CO_LABEL:
/* Do nothing. */
cp_periodic(); /*CDHW needed to avoid lock-ups when loop contains only a label CDHW*/
break;
case CO_STATEMENT:
docommand(wl_copy(bl->co_text));
break;
case CO_UNFILLED:
/* There was probably an error here... */
fprintf(cp_err, "Warning: ignoring previous error\n");
break;
default:
fprintf(cp_err,
"doblock: Internal Error: bad block type %d\n",
bl->co_type);
return (NORMAL_STR);
}
return (NORMAL_STR);
}
/* Maxiumum number of cheverons used for the alternative prompt */
#define MAX_CHEVRONS 16
/* Get the alternate prompt.
Number of chevrons indicates stack depth.
Returns NULL when there is no alternate prompt.
SJB 28th April 2005 */
char *
get_alt_prompt(void)
{
int i = 0, j;
static char buf[MAX_CHEVRONS + 2]; /* includes terminating space & null */
struct control *c;
/* if nothing on the command stack return NULL */
if (cend[stackp] == NULL)
return NULL;
/* measure stack depth */
for (c = cend[stackp]->co_parent; c; c = c->co_parent)
i++;
if (i <= 0)
return NULL;
/* Avoid overflow of buffer and
indicate when we've limited the chevrons by starting with a '+' */
if (i > MAX_CHEVRONS) {
i = MAX_CHEVRONS;
buf[0] = '+';
} else {
buf[0] = '>';
}
/* return one chevron per command stack depth */
for (j = 1; j < i; j++)
buf[j] = '>';
/* Add space and terminate */
buf[j] = ' ';
buf[j + 1] = '\0';
return buf;
}
/* Get a command. This does all the bookkeeping things like turning
* command completion on and off... */
static wordlist *
getcommand(char *string)
{
wordlist *wlist;
if (cp_debug)
fprintf(cp_err, "calling getcommand %s\n", string ? string : "");
#if !defined(HAVE_GNUREADLINE) && !defined(HAVE_BSDEDITLINE)
/* set cp_altprompt for use by the lexer - see parser/lexical.c */
cp_altprompt = get_alt_prompt();
#endif /* !defined(HAVE_GNUREADLINE) && !defined(HAVE_BSDEDITLINE) */
cp_cwait = TRUE;
wlist = cp_parse(string);
cp_cwait = FALSE;
if (cp_debug) {
printf("getcommand ");
wl_print(wlist, stdout);
putc('\n', stdout);
}
return (wlist);
}
/* va: TODO: free control structure(s) before overwriting (memory leakage) */
int
cp_evloop(char *string)
{
wordlist *wlist, *ww, *freewl;
struct control *x;
char *i;
int nn;
#define newblock \
do { \
cend[stackp]->co_children = TMALLOC(struct control, 1); \
ZERO(cend[stackp]->co_children, struct control); \
cend[stackp]->co_children->co_parent = cend[stackp]; \
cend[stackp] = cend[stackp]->co_children; \
cend[stackp]->co_type = CO_UNFILLED; \
} while(0)
for (;;) {
freewl = wlist = getcommand(string);
if (wlist == NULL) { /* End of file or end of user input. */
if (cend[stackp] && cend[stackp]->co_parent && !string) {
cp_resetcontrol();
continue;
} else {
return (0);
}
}
if ((wlist->wl_word == NULL) || (*wlist->wl_word == '\0')) {
/* User just typed return. */
wl_free(wlist); /* va, avoid memory leak */
if (string) {
return (1);
} else {
cp_event--;
continue;
}
}
/* Just a check... */
for (ww = wlist; ww; ww = ww->wl_next)
if (!ww->wl_word) {
fprintf(cp_err,
"cp_evloop: Internal Error: NULL word pointer\n");
continue;
}
/* Add this to the control structure list. If cend->co_type is
* CO_UNFILLED, the last line was the beginning of a block,
* and this is the unfilled first statement.
*/
/* va: TODO: free old structure and its content, before overwriting */
if (cend[stackp] && (cend[stackp]->co_type != CO_UNFILLED)) {
cend[stackp]->co_next = TMALLOC(struct control, 1);
ZERO(cend[stackp]->co_next, struct control);
cend[stackp]->co_next->co_prev = cend[stackp];
cend[stackp]->co_next->co_parent = cend[stackp]->co_parent;
cend[stackp] = cend[stackp]->co_next;
} else if (!cend[stackp]) {
control[stackp] = cend[stackp] = TMALLOC(struct control, 1);
ZERO(cend[stackp], struct control);
}
if (eq(wlist->wl_word, "while")) {
cend[stackp]->co_type = CO_WHILE;
cend[stackp]->co_cond = wl_copy(wlist->wl_next); /* va, wl_copy */
if (!cend[stackp]->co_cond) {
fprintf(stderr,
"Error: missing while condition, 'false' will be assumed.\n");
}
newblock;
} else if (eq(wlist->wl_word, "dowhile")) {
cend[stackp]->co_type = CO_DOWHILE;
cend[stackp]->co_cond = wl_copy(wlist->wl_next); /* va, wl_copy */
if (!cend[stackp]->co_cond) {
/* va: prevent misinterpretation as trigraph sequence with \-sign */
fprintf(stderr,
"Error: missing dowhile condition, '?\?\?' will be assumed.\n");
}
newblock;
} else if (eq(wlist->wl_word, "repeat")) {
cend[stackp]->co_type = CO_REPEAT;
if (!wlist->wl_next) {
cend[stackp]->co_numtimes = -1;
} else {
char *s;
double *dd;
struct wordlist *t; /*CDHW*/
/*CDHW wlist = cp_variablesubst(cp_bquote(cp_doglob(wl_copy(wlist)))); Wrong order? Leak? CDHW*/
t = cp_doglob(cp_bquote(cp_variablesubst(wl_copy(wlist)))); /*CDHW leak from cp_doglob? */
s = t->wl_next->wl_word;
dd = ft_numparse(&s, FALSE);
if (dd) {
if (*dd < 0) {
fprintf(cp_err,
"Error: can't repeat a negative number of times\n");
*dd = 0.0;
}
cend[stackp]->co_numtimes = (int) *dd;
} else {
fprintf(cp_err,
"Error: bad repeat argument %s\n",
t->wl_next->wl_word); /* CDHW */
}
wl_free(t);
t = NULL; /* CDHW */
}
newblock;
} else if (eq(wlist->wl_word, "if")) {
cend[stackp]->co_type = CO_IF;
cend[stackp]->co_cond = wl_copy(wlist->wl_next); /* va, wl_copy */
if (!cend[stackp]->co_cond) {
fprintf(stderr,
"Error: missing if condition.\n");
}
newblock;
} else if (eq(wlist->wl_word, "foreach")) {
cend[stackp]->co_type = CO_FOREACH;
if (wlist->wl_next) {
wlist = wlist->wl_next;
cend[stackp]->co_foreachvar =
copy(wlist->wl_word);
wlist = wlist->wl_next;
} else {
fprintf(stderr,
"Error: missing foreach variable.\n");
}
wlist = cp_doglob(wlist);
cend[stackp]->co_text = wl_copy(wlist);
newblock;
} else if (eq(wlist->wl_word, "label")) {
cend[stackp]->co_type = CO_LABEL;
if (wlist->wl_next) {
cend[stackp]->co_text = wl_copy(wlist->wl_next);
/* I think of everything, don't I? */
cp_addkword(CT_LABEL, wlist->wl_next->wl_word);
if (wlist->wl_next->wl_next)
fprintf(cp_err,
"Warning: ignored extra junk after label.\n");
} else {
fprintf(stderr, "Error: missing label.\n");
}
} else if (eq(wlist->wl_word, "goto")) {
/* Incidentally, this won't work if the values 1 and 2 ever get
* to be valid character pointers -- I think it's reasonably
* safe to assume they aren't... */
cend[stackp]->co_type = CO_GOTO;
if (wlist->wl_next) {
cend[stackp]->co_text = wl_copy(wlist->wl_next);
if (wlist->wl_next->wl_next)
fprintf(cp_err,
"Warning: ignored extra junk after goto.\n");
} else {
fprintf(stderr, "Error: missing label.\n");
}
} else if (eq(wlist->wl_word, "continue")) {
cend[stackp]->co_type = CO_CONTINUE;
if (wlist->wl_next) {
cend[stackp]->co_numtimes = scannum(wlist->wl_next->wl_word);
if (wlist->wl_next->wl_next)
fprintf(cp_err,
"Warning: ignored extra junk after continue %d.\n",
cend[stackp]->co_numtimes);
} else {
cend[stackp]->co_numtimes = 1;
}
} else if (eq(wlist->wl_word, "break")) {
cend[stackp]->co_type = CO_BREAK;
if (wlist->wl_next) {
cend[stackp]->co_numtimes = scannum(wlist->wl_next->wl_word);
if (wlist->wl_next->wl_next)
fprintf(cp_err,
"Warning: ignored extra junk after break %d.\n",
cend[stackp]->co_numtimes);
} else {
cend[stackp]->co_numtimes = 1;
}
} else if (eq(wlist->wl_word, "end")) {
/* Throw away this thing. */
if (!cend[stackp]->co_parent) {
fprintf(stderr, "Error: no block to end.\n");
cend[stackp]->co_type = CO_UNFILLED;
} else if (cend[stackp]->co_prev) {
cend[stackp]->co_prev->co_next = NULL;
x = cend[stackp];
cend[stackp] = cend[stackp]->co_parent;
tfree(x);
x = NULL;
} else {
x = cend[stackp];
cend[stackp] = cend[stackp]->co_parent;
cend[stackp]->co_children = NULL;
tfree(x);
x = NULL;
}
} else if (eq(wlist->wl_word, "else")) {
if (!cend[stackp]->co_parent ||
(cend[stackp]->co_parent->co_type !=
CO_IF)) {
fprintf(stderr, "Error: misplaced else.\n");
cend[stackp]->co_type = CO_UNFILLED;
} else {
if (cend[stackp]->co_prev)
cend[stackp]->co_prev->co_next = NULL;
else
cend[stackp]->co_parent->co_children = NULL;
cend[stackp]->co_parent->co_elseblock = cend[stackp];
cend[stackp]->co_prev = NULL;
}
} else {
cend[stackp]->co_type = CO_STATEMENT;
cend[stackp]->co_text = wl_copy(wlist);
}
if (!cend[stackp]->co_parent) {
x = cend[stackp];
/* We have to toss this do-while loop in here so
* that gotos at the top level will work.
*/
do {
nn = 0; /* CDHW */
i = doblock(x, &nn);
switch (*i) {
case NORMAL:
break;
case BROKEN:
fprintf(cp_err,
"Error: break not in loop or too many break levels given\n");
break;
case CONTINUED:
fprintf(cp_err,
"Error: continue not in loop or too many continue levels given\n");
break;
default:
x = findlabel(i, control[stackp]);
if (!x)
fprintf(cp_err, "Error: label %s not found\n", i);
tfree(i);
}
if (x)
x = x->co_next;
} while (x);
}
wl_free(freewl);
if (string)
return (1); /* The return value is irrelevant. */
}
}
/* This blows away the control structures... */
void cp_free_control(void); /* needed by resetcontrol */
void cp_resetcontrol(void)
{
fprintf(cp_err, "Warning: clearing control structures\n");
if (cend[stackp] && cend[stackp]->co_parent)
fprintf(cp_err, "Warning: EOF before block terminated\n");
/* We probably should free the control structures... */
cp_free_control(); /* va: free it */
control[0] = cend[0] = NULL;
stackp = 0;
cp_kwswitch(CT_LABEL, NULL);
}
/* Push or pop a new control structure set... */
void
cp_popcontrol(void)
{
if (cp_debug)
fprintf(cp_err, "pop: stackp: %d -> %d\n", stackp, stackp - 1);
if (stackp < 1) {
fprintf(cp_err, "cp_popcontrol: Internal Error: stack empty\n");
} else {
/* va: free unused control structure */
ctl_free(control[stackp]);
stackp--;
}
}
void
cp_pushcontrol(void)
{
if (cp_debug)
fprintf(cp_err, "push: stackp: %d -> %d\n", stackp, stackp + 1);
if (stackp > CONTROLSTACKSIZE - 2) {
fprintf(cp_err, "Error: stack overflow -- max depth = %d\n",
CONTROLSTACKSIZE);
stackp = 0;
} else {
stackp++;
control[stackp] = cend[stackp] = NULL;
}
}
/* And this returns to the top level (for use in the interrupt handlers). */
void
cp_toplevel(void)
{
stackp = 0;
if (cend[stackp])
while (cend[stackp]->co_parent)
cend[stackp] = cend[stackp]->co_parent;
}
/* va: This totally frees the control structures */
void
cp_free_control(void)
{
int i;
for (i = stackp; i >= 0; i--)
ctl_free(control[i]);
control[0] = cend[0] = NULL;
stackp = 0;
}