/**********
Copyright 1990 Regents of the University of California.  All rights reserved.
Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
**********/

#include "spice.h"
#include "util.h"
#include "misc.h"
#include "cpdefs.h"
/* XXXX */
#include "fteext.h"
#include "ftedefs.h"
/* XXXX */
#include "suffix.h"

bool cp_noglob = true;
bool cp_nonomatch = false;
bool cp_noclobber = false;
bool cp_ignoreeof = false;

struct variable *variables = NULL;

wordlist *
cp_varwl(var)
    struct variable *var;
{
    wordlist *wl = NULL, *w, *wx = NULL;
    char buf[BSIZE_SP];
    struct variable *vt;

    switch(var->va_type) {
        case VT_BOOL:
            /* Can't ever be false. */
            (void) sprintf(buf, "%s", var->va_bool ? "true" :
                    "false");
            break;
        case VT_NUM:
            (void) sprintf(buf, "%d", var->va_num);
            break;
        case VT_REAL:
            /* This is a case where printnum isn't too good... */
            (void) sprintf(buf, "%G", var->va_real);
            break;
        case VT_STRING:
            (void) strcpy(buf, cp_unquote(var->va_string));
            break;
        case VT_LIST:   /* The tricky case. */
            for (vt = var->va_vlist; vt; vt = vt->va_next) {
                w = cp_varwl(vt);
                if (wl == NULL)
                    wl = wx = w;
                else {
                    wx->wl_next = w;
                    w->wl_prev = wx;
                    wx = w;
                }
            }
            return (wl);
        default:
            fprintf(cp_err, 
            "cp_varwl: Internal Error: bad variable type %d\n", 
                var->va_type);
            return (NULL);
    }
    wl = alloc(struct wordlist);
    wl->wl_next = wl->wl_prev = NULL;
    wl->wl_word = copy(buf);
    return (wl);
}

/* Set a variable. */

void
cp_vset(varname, type, value)
    char *varname;
    char type;
    char *value;
{
    struct variable *v, *u, *w;
    int i;
    bool alreadythere = false;

/* for (v = variables; v; v = v->va_next) ; printf("ok while setting %s\n", 
        varname);*/
    varname = cp_unquote(varname);
    w = NULL;
    for (v = variables; v; v = v->va_next) {
        if (eq(varname, v->va_name)) {
            alreadythere = true;
            break;
        }
	w = v;
    }
    if (!v) {
        v = alloc(struct variable);
        v->va_name = copy(varname);
        v->va_next = NULL;
    }
    switch (type) {
        case VT_BOOL:
        if (* ((bool *) value) == false) {
            cp_remvar(varname);
            return;
        } else
            v->va_bool = true;
        break;

        case VT_NUM:
        v->va_num = * (int *) value;
        break;

        case VT_REAL:
        v->va_real = * (double *) value;
        break;

        case VT_STRING:
        v->va_string = copy(value);
        break;

        case VT_LIST:
        v->va_vlist = (struct variable *) value;
        break;

        default:
        fprintf(cp_err, 
            "cp_vset: Internal Error: bad variable type %d.\n", 
                type);
        return;
    }
    v->va_type = type;

    /* Now, see if there is anything interesting going on. We recognise
     * these special variables: noglob, nonomatch, history, echo,
     * noclobber, prompt, and verbose. cp_remvar looks for these variables
     * too. The host program will get any others.
     */

    if (eq(varname, "noglob"))
        cp_noglob = true;
    else if (eq(varname, "nonomatch"))
        cp_nonomatch = true;
    else if (eq(varname, "history") && (type == VT_NUM))
        cp_maxhistlength = v->va_num;
    else if (eq(varname, "history") && (type == VT_REAL))
        cp_maxhistlength = v->va_real;
    else if (eq(varname, "noclobber"))
        cp_noclobber = true;
    else if (eq(varname, "prompt") && (type == VT_STRING))
        cp_promptstring = copy(v->va_string);
    else if (eq(varname, "ignoreeof"))
        cp_ignoreeof = true;
    else if (eq(varname, "cpdebug")) {
        cp_debug = true;
#ifndef CPDEBUG
        fprintf(cp_err, 
        "Warning: program not compiled with cshpar debug messages\n");
#endif
    }

    switch (i = cp_usrset(v, true)) {

        case US_OK:
        /* Normal case. */
        if (!alreadythere) {
            v->va_next = variables;
            variables = v;
        }
        break;

        case US_DONTRECORD:
        /* Do nothing... */
        if (alreadythere) {
            fprintf(cp_err, 
    "cp_vset: Internal Error: %s already there, but 'dont record'\n",
                    v->va_name);
        }
        break;
    
        case US_READONLY:
        fprintf(cp_err, "Error: %s is a read-only variable.\n",
                v->va_name);
        if (alreadythere)
            fprintf(cp_err, 
        "cp_vset: Internal Error: it was already there too!!\n");
        break;

        case US_SIMVAR:
	if (alreadythere) {
		/* somehow it got into the front-end list of variables */
		if (w) {
			w->va_next = v->va_next;
		} else {
			variables = v->va_next;
		}
	}
	alreadythere = false;
	if (ft_curckt) {
	    for (u = ft_curckt->ci_vars; u; u = u->va_next)
		if (eq(varname, u->va_name)) {
		    alreadythere = true;
		    break;
		}
	    if (!alreadythere) {
		v->va_next = ft_curckt->ci_vars;
		ft_curckt->ci_vars = v;
	    } else {
		w = u->va_next;
		bcopy(v, u, sizeof(*u));
		u->va_next = w;
	    }
	}
	break;

        case US_NOSIMVAR:
	/* What do you do? */
	tfree(v);
	break;

        default:
        fprintf(cp_err, "cp_vset: Internal Error: bad US val %d\n", i);
        break;
    }

    return;
}

struct variable *
cp_setparse(wl)
    wordlist *wl;
{
    char *name, *val, *s, *ss;
    double *td;
    struct variable *listv = NULL, *vv, *lv = NULL;
    struct variable *vars = NULL;
    int balance;

    while (wl) {
        name = cp_unquote(wl->wl_word);
        wl = wl->wl_next;
        if (((wl == NULL) || (*wl->wl_word != '=')) && 
                index(name, '=') == NULL) {
            vv = alloc(struct variable);
            vv->va_name = copy(name);
            vv->va_type = VT_BOOL;
            vv->va_bool = true;
            vv->va_next = vars;
            vars = vv;
            continue;
        }
        if (wl && eq(wl->wl_word, "=")) {
            wl = wl->wl_next;
            if (wl == NULL) {
                fprintf(cp_err, "Error: bad set form.\n");
                return (NULL);
            }
            val = wl->wl_word;
            wl = wl->wl_next;
        } else if (wl && (*wl->wl_word == '=')) {
            val = wl->wl_word + 1;
            wl = wl->wl_next;
        } else if (s = index(name, '=')) {
            val = s + 1;
            *s = '\0';
            if (*val == '\0') {
                if (!wl) {
                    fprintf(cp_err,
                        "Error:  %s equals what?.\n",
                        name);
                    return (NULL);
                } else {
                    val = wl->wl_word;
                    wl = wl->wl_next;
                }
            }
        } else {
            fprintf(cp_err, "Error: bad set form.\n");
            return (NULL);
        }
        val = cp_unquote(val);
        if (eq(val, "(")) { /* ) */
            /* The beginning of a list... We have to walk down
             * the list until we find a close paren... If there
             * are nested ()'s, treat them as tokens...
             */
            balance = 1;
            while (wl && wl->wl_word) {
                if (eq(wl->wl_word, "(")) { /* ) ( */
                    balance++;
                } else if (eq(wl->wl_word, ")")) {
                    if (!--balance)
                        break;
                }
                vv = alloc(struct variable);
		vv->va_next = NULL;
                ss = cp_unquote(wl->wl_word);
                td = ft_numparse(&ss, false);
                if (td) {
                    vv->va_type = VT_REAL;
                    vv->va_real = *td;
                } else {
                    vv->va_type = VT_STRING;
                    vv->va_string = copy(ss);
                }
                if (listv) {
                    lv->va_next = vv;
                    lv = vv;
                } else
                    listv = lv = vv;
                wl = wl->wl_next;
            }
            if (balance && !wl) {
                fprintf(cp_err, "Error: bad set form.\n");
                return (NULL);
            }
            
            vv = alloc(struct variable);
            vv->va_name = copy(name);
            vv->va_type = VT_LIST;
            vv->va_vlist = listv;
            vv->va_next = vars;
            vars = vv;

            wl = wl->wl_next;
            continue;
        }

        ss = cp_unquote(val);
        td = ft_numparse(&ss, false);
        vv = alloc(struct variable);
        vv->va_name = copy(name);
        vv->va_next = vars;
        vars = vv;
        if (td) {
            /*** We should try to get VT_NUM's... */
            vv->va_type = VT_REAL;
            vv->va_real = *td;
        } else {
            vv->va_type = VT_STRING;
            vv->va_string = copy(val);
        }
    }
    return (vars);
}

void
cp_remvar(varname)
    char *varname;
{
    struct variable *v, *u, *lv = NULL;
    bool found = true;
    int i;

    for (v = variables; v; v = v->va_next) {
        if (eq(v->va_name, varname))
            break;
        lv = v;
    }
    if (!v) {
        /* Gotta make up a var struct for cp_usrset()... */
        v = alloc(struct variable);
	ZERO(v, struct variable);
        v->va_name = varname;
        v->va_type = VT_NUM;
        v->va_bool = 0;
        found = false;
    }

    /* Note that 'unset history' doesn't do anything here... Causes
     * trouble...
     */
    if (eq(varname, "noglob"))
        cp_noglob = false;
    else if (eq(varname, "nonomatch"))
        cp_nonomatch = false;
    else if (eq(varname, "noclobber"))
        cp_noclobber = false;
    else if (eq(varname, "prompt"))
        cp_promptstring = "";
    else if (eq(varname, "cpdebug"))
        cp_debug = false;
    else if (eq(varname, "ignoreeof"))
        cp_ignoreeof = false;

    switch (i = cp_usrset(v, false)) {

        case US_OK:
        /* Normal case. */
        if (found) {
            if (lv)
                lv->va_next = v->va_next;
            else
                variables = v->va_next;
        }
        break;

        case US_DONTRECORD:
        /* Do nothing... */
        if (found)
            fprintf(cp_err,
        "cp_remvar: Internal Error: var %d\n", varname);
        break;
    
        case US_READONLY:
        /* Badness... */
        fprintf(cp_err, "Error: %s is read-only.\n",
                v->va_name);
        if (found)
            fprintf(cp_err,
        "cp_remvar: Internal Error: var %d\n", varname);
        break;

        case US_SIMVAR:
	lv = NULL;
	if (ft_curckt) {
	    for (u = ft_curckt->ci_vars; u; u = u->va_next) {
		if (eq(varname, u->va_name)) {
		    break;
		}
		lv = u;
	    }
	    if (u) {
		if (lv)
		    lv->va_next = u->va_next;
		else
		    ft_curckt->ci_vars = u->va_next;
		tfree(u);
	    }
	}
	break;

        default:
        fprintf(cp_err, "cp_remvar: Internal Error: US val %d\n", i);
        break;
    }

    tfree(v);
    return;
}
