/**********
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.h>
#include <cpdefs.h>

#include "control.h"
#include "com_cdump.h"
#include "variable.h"
#include "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
 */

static char *noredirect[] = { "stop", NULL } ;  /* Only one?? */


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");
    return;
}


/* 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");
    return;
}

/*CDHW Remove control structure and free the memory its hogging CDHW*/

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)
{
    char *r, *s, *t;
    int nargs;
    int i;
    struct comm *command;
    wordlist *wl, *nextc, *ee, *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;
    do {
        for (nextc = wlist; nextc; nextc = nextc->wl_next)
            if (eq(nextc->wl_word, cp_csep))
                break;

        /* Temporarily hide the rest of the command... */
        if (nextc && nextc->wl_prev)
            nextc->wl_prev->wl_next = NULL;
        ee = wlist->wl_prev;
        if (ee)
            wlist->wl_prev = NULL;

        if (nextc == wlist) {
            /* There was no text... */
            goto out;
        }

        /* 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))) {
                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++) {
            /* strcmp(cp_coms[i].co_comname, s) ... */
            for (t = cp_coms[i].co_comname, r = s; *t && *r;
		 t++, r++)
                if (*t != *r)
                    break;
            if (!*t && !*r)
                break; 
        }
        
        /* Now give the user-supplied command routine a try... */
        if (!cp_coms[i].co_func && cp_oddcomm(s, wlist->wl_next))
            goto out;

        /* If it's not there, try it as a unix command. */
        if (!cp_coms[i].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's there but spiceonly, and this is nutmeg, error. */
        } else if (!cp_coms[i].co_func && ft_nutmeg && 
		   (cp_coms[i].co_spiceonly)) {
            fprintf(cp_err,"%s: command available only in spice\n",
                    s);
            goto out;
        }

        /* The command was a valid spice/nutmeg command. */
        command = &cp_coms[i];
        nargs = 0;
        for (wl = wlist->wl_next; wl; wl = wl->wl_next)
            nargs++;
        {
            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);
            }
        }

        /* Now fix the pointers and advance wlist. */
    out:        wlist->wl_prev = ee;
        if (nextc) {
            for(wl=wlist; wl->wl_next; wl=wl->wl_next)
                ;
            wl->wl_next = nextc;
            nextc->wl_prev = wl;
            wlist = nextc->wl_next;
        }
    } while (nextc && wlist);

    wl_free(rwlist);

    /* Do periodic sorts of things... */
    cp_periodic();

    cp_ioreset();
    return;
}


/* 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;
    char *i;
    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 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:
        for (wl = cp_variablesubst(cp_bquote(cp_doglob(wl_copy(bl->co_text))));
	     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)
                        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_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)))); /*CDHW Leak ? CDHW*/
        return (wl->wl_word);

    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] == 0)
	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;
    struct control *x;
    char *i;
    int nn;

#define newblock    cend[stackp]->co_children = alloc(struct control);      \
	    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;

    for (;;) {
	wlist = getcommand(string);
	if (wlist == NULL) {    /* End of file or end of user input. */
	    if (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 = alloc(struct control);
	    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] = alloc(struct control);
	    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);  /*CDHW Possible leak around here? */
	    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);
		}
		if (x)
		    x = x->co_next;
	    } while (x);
	}
	wl_free(wlist); wlist = NULL;
	if (string) {
	    return (1); /* The return value is irrelevant. */
	}
    }
    wl_free(wlist); wlist = NULL;
    return (0); /* va: which value? */
}

/* 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, (char *) NULL);
    return;
}


/* 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--;
    }
    return;
}


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;
    }
    return;
}


/* 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;
    return;
}


/* 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;
}

