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

/*
 * Routines to query and alter devices.
 */

#include "ngspice/ngspice.h"
#include "ngspice/gendefs.h"
#include "ngspice/cktdefs.h"
#include "ngspice/cpdefs.h"
#include "ngspice/ftedefs.h"
#include "ngspice/dgen.h"

#include "circuits.h"
#include "device.h"
#include "variable.h"
#include "com_commands.h"
#include "../misc/util.h" /* ngdirname() */

#include "gens.h" /* wl_forall */


static wordlist *devexpand(char *name);
static void all_show(wordlist *wl, int mode);
static void all_show_old(wordlist *wl, int mode);
static void com_alter_mod(wordlist *wl);
static void if_set_binned_model(CKTcircuit *, char *, char *, struct dvec *);


/*
 *      devhelp: lists available devices and information on parameters
 *              devhelp                 : shows all available devices
 *              devhelp devname         : shows all parameters of that model/instance
 *              devhelp devname parname : shows parameter meaning
 *              Options: -csv (comma separated value for generating docs)
 *
 */

void com_devhelp(wordlist *wl)
{
    /* Just a simple driver now */
    devhelp(wl);
}


void devhelp(wordlist *wl)
{
    int i, k = 0;
    int devindex = -1, devInstParNo = 0 , devModParNo = 0;
    bool found = FALSE;
    bool csv = FALSE;
    wordlist *wlist;
    IFparm *plist;

    /*First copy the base pointer */
    wlist = wl;

    /* If there are no arguments output the list of available devices */
    if (!wlist) {
        out_init();
        out_printf("\nDevices available in the simulator\n\n");
        for (k = 0; k < ft_sim->numDevices; k++)
            if (ft_sim->devices[k])
                out_printf("%-*s:\t%s\n",
                           DEV_WIDTH, ft_sim->devices[k]->name,
                           ft_sim->devices[k]->description);
        out_send("\n");
        return;
    }

    /* The first argument must be the csv option or a device name */
    if (wlist && wlist->wl_word && eq(wlist->wl_word, "-csv")) {
        csv = TRUE;
        if (wlist->wl_next)
            wlist = wlist->wl_next;
        else
            return;
    }

    /* This argument, if exists, must be the device name */
    if (wlist && wlist->wl_word) {
        while (k < ft_sim->numDevices && !found) {
            if (ft_sim->devices[k])
                if (strcasecmp(ft_sim->devices[k]->name, wlist->wl_word) == 0) {
                    devindex = k;
                    if (ft_sim->devices[devindex]->numInstanceParms)
                        devInstParNo = *(ft_sim->devices[devindex]->numInstanceParms);
                    else
                        devInstParNo = 0;

                    if (ft_sim->devices[devindex]->numModelParms)
                        devModParNo = *(ft_sim->devices[devindex]->numModelParms);
                    else
                        devModParNo = 0;

                    wlist = wlist->wl_next;
                    found = TRUE;
                }
            k++;
        }

        if (!found) {
            fprintf(cp_out, "Error: Device %s not found\n", wlist->wl_word);
            return;
        }
    }

    /* At this point, found is TRUE and we have found the device.
     * Now we have to scan the model and instance parameters to print
     * the string
     */
    found = FALSE;
    if (wlist && wlist->wl_word) {
        plist = ft_sim->devices[devindex]->modelParms;
        for (i = 0; i < devModParNo; i++) { /* Scan model parameters first */
            if (strcasecmp(plist[i].keyword, wlist->wl_word) == 0) {
                found = TRUE;
                out_init();
                out_printf("Model Parameters\n");
                if (csv)
                    out_printf("id#, Name, Dir, Description\n");
                else
                    out_printf("%5s\t %-10s\t Dir\t Description\n", "id#", "Name");
                printdesc(plist[i], csv);
                out_send("\n");
            }
        }

        if (!found) {
            plist = ft_sim->devices[devindex]->instanceParms;
            for (i = 0; i < devInstParNo; i++) { /* Scan instance parameters then */
                if (strcasecmp(plist[i].keyword, wlist->wl_word) == 0) {
                    found = TRUE;
                    out_init();
                    out_printf("Instance Parameters\n");
                    if (csv)
                        out_printf("id#, Name, Dir, Description\n");
                    else
                        out_printf("%5s\t %-10s\t Dir\t Description\n", "id#", "Name");
                    printdesc(plist[i], csv);
                    out_send("\n");
                }
            }
        }

        if (!found)
            fprintf(cp_out, "Error: Parameter %s not found\n", wlist->wl_word);
        return;

    }

    /* No arguments - we want all the parameters*/
    out_init();
    out_printf("%s - %s\n\n", ft_sim->devices[devindex]->name, ft_sim->devices[devindex]->description);
    out_printf("Model Parameters\n");
    if (csv)
        out_printf("id#, Name, Dir, Description\n");
    else
        out_printf("%5s\t %-10s\t Dir\t Description\n", "id#", "Name");

    plist = ft_sim->devices[devindex]->modelParms;
    for (i = 0; i < devModParNo; i++)
        printdesc(plist[i], csv);
    out_printf("\n");
    out_printf("Instance Parameters\n");
    if (csv)
        out_printf("id#, Name, Dir, Description\n");
    else
        out_printf("%5s\t %-10s\t Dir\t Description\n", "id#", "Name");

    plist = ft_sim->devices[devindex]->instanceParms;
    for (i = 0; i < devInstParNo; i++)
        printdesc(plist[i], csv);

    out_send("\n");
}


/*
 * Pretty print parameter descriptions
 * This function prints description of device parameters
 */
void
printdesc(IFparm p, bool csv)
{
    char sep;
    int spacer1, spacer2;

    /* First we indentify the separator */
    if (csv) {
        sep = ',';
        spacer1 = 0;
        spacer2 = 0;
    } else {
        sep = '\t';
        spacer1 = 5;
        spacer2 = 10;
    }

    out_printf("%*d%c %-*s%c ", spacer1, p.id, sep, spacer2, p.keyword, sep);

    if (p.dataType & IF_SET)
        if (p.dataType & IF_ASK)
            out_printf("inout%c ", sep);
        else
            out_printf("in%c ", sep);
    else
        out_printf("out%c ", sep);

    if (p.description)
        out_printf("%s\n", p.description);
    else
        out_printf("n.a.\n");
}


/*
 *      show: list device operating point info
 *              show
 *              show devs : params
 *              show devs : params ; devs : params
 *              show dev dev dev : param param param , dev dev : param param
 *              show t : param param param, t : param param
 *
 */

static  int     count;

void
com_showmod(wordlist *wl)
{
    if (cp_getvar("altshow", CP_BOOL, NULL))
        all_show(wl, 1);
    else
        all_show_old(wl, 1);
}


void
com_show(wordlist *wl)
{
    if (cp_getvar("altshow", CP_BOOL, NULL))
        all_show(wl, 0);
    else
        all_show_old(wl, 0);
}


static void
all_show(wordlist *wl, int mode)
{
    wordlist    *params, *nextgroup, *thisgroup;
    wordlist    *prev, *next, *w;
    int         screen_width;
    dgen        *dg;
    int         instances;
    int         i, j, n;
    int         param_flag, dev_flag;

    if (!ft_curckt) {
        fprintf(cp_err, "Error: no circuit loaded\n");
        return;
    }

    if (wl && wl->wl_word && eq(wl->wl_word, "-v")) {
        old_show(wl->wl_next);
        return;
    }

    if (!cp_getvar("width", CP_NUM, &screen_width))
        screen_width = DEF_WIDTH;
    count = (screen_width - LEFT_WIDTH) / (DEV_WIDTH + 1);
    count = 1;

    n = 0;
    do {
        prev = NULL;
        params = NULL;
        nextgroup = NULL;
        thisgroup = wl;
        param_flag = 0;
        dev_flag = 0;

        /* find the parameter list and the nextgroup */
        for (w = wl; w && !nextgroup; w = next) {
            next = w->wl_next;

            if (eq(w->wl_word, "*")) {
                tfree(w->wl_word);
                w->wl_word = strdup("all");
            }

            if (eq(w->wl_word, "++") || eq(w->wl_word, "all")) {
                if (params) {
                    param_flag = DGEN_ALLPARAMS;
                    if (prev)
                        prev->wl_next = w->wl_next;
                    else
                        params = next;
                } else {
                    dev_flag = DGEN_ALLDEVS;
                    if (prev)
                        prev->wl_next = w->wl_next;
                    else
                        thisgroup = next;
                }
                /* w must not be freed here */
                w = NULL;
            } else if (eq(w->wl_word, "+")) {
                if (params) {
                    param_flag = DGEN_DEFPARAMS;
                    if (prev)
                        prev->wl_next = w->wl_next;
                    else
                        params = next;
                } else {
                    dev_flag = DGEN_DEFDEVS;
                    if (prev)
                        prev->wl_next = w->wl_next;
                    else
                        thisgroup = next;
                }
                /* w must not be freed here */
                w = NULL;
            } else if (eq(w->wl_word, ":")) {
                /* w must not be freed here */
                w = NULL;
                if (!params) {
                    params = next;
                    if (prev)
                        prev->wl_next = NULL;
                    else
                        thisgroup = NULL;
                } else {
                    if (prev)
                        prev->wl_next = next;
                    else
                        params = next;
                }
            } else if (eq(w->wl_word, ";") || eq(w->wl_word, ",")) {
                nextgroup = next;
                /* w must not be freed here */
                w = NULL;
                if (prev)
                    prev->wl_next = NULL;
                break;
            }
            prev = w;
        }

        instances = 0;
        for (dg = dgen_init(ft_curckt->ci_ckt, thisgroup, 1, dev_flag, mode);
             dg; dgen_nth_next(&dg, count))
        {
            instances = 1;
            if (dg->flags & DGEN_INSTANCE) {
                instances = 2;
                n += 1;

                fprintf(cp_out, "%s:\n", dg->instance->GENname);
                fprintf(cp_out, "    %-19s= %s\n", "model", dg->model->GENmodName);

                if (param_flag)
                    param_forall(dg, param_flag);
                else if (!params)
                    param_forall(dg, DGEN_DEFPARAMS);

                if (params)
                    wl_forall(params, listparam, dg);

            } else if (ft_sim->devices[dg->dev_type_no]->numModelParms) {
                fprintf(cp_out, " %s models (%s)\n",
                        ft_sim->devices[dg->dev_type_no]->name,
                        ft_sim->devices[dg->dev_type_no]->description);
                n += 1;
                i = 0;
                do {
                    fprintf(cp_out, "%*s", LEFT_WIDTH, "model");
                    j = dgen_for_n(dg, count, printstr_m, NULL, i);
                    i += 1;
                    fprintf(cp_out, "\n");
                } while (j);
                fprintf(cp_out, "\n");

                if (param_flag)
                    param_forall(dg, param_flag);
                else if (!params)
                    param_forall(dg, DGEN_DEFPARAMS);

                if (params)
                    wl_forall(params, listparam, dg);
                fprintf(cp_out, "\n");
            }
        }

        wl = nextgroup;

    } while (wl);

    if (!n) {
        if (instances == 0)
            printf("No matching instances or models\n");
        else if (instances == 1)
            printf("No matching models\n");
        else
            printf("No matching elements\n");
    }
}


static void
all_show_old(wordlist *wl, int mode)
{
    wordlist    *params, *nextgroup, *thisgroup;
    wordlist    *prev, *next, *w;
    int         screen_width;
    dgen        *dg;
    int         instances;
    int         i, j, n;
    int         param_flag, dev_flag;

    if (!ft_curckt) {
        fprintf(cp_err, "Error: no circuit loaded\n");
        return;
    }

    if (wl && wl->wl_word && eq(wl->wl_word, "-v")) {
        old_show(wl->wl_next);
        return;
    }

    if (!cp_getvar("width", CP_NUM, &screen_width))
        screen_width = DEF_WIDTH;
    count = (screen_width - LEFT_WIDTH) / (DEV_WIDTH + 1);

    n = 0;
    do {
        prev = NULL;
        params = NULL;
        nextgroup = NULL;
        thisgroup = wl;
        param_flag = 0;
        dev_flag = 0;

        /* find the parameter list and the nextgroup */
        for (w = wl; w && !nextgroup; w = next) {
            next = w->wl_next;

            if (eq(w->wl_word, "*")) {
                tfree(w->wl_word);
                w->wl_word = strdup("all");
            }

            if (eq(w->wl_word, "++") || eq(w->wl_word, "all")) {
                if (params) {
                    param_flag = DGEN_ALLPARAMS;
                    if (prev)
                        prev->wl_next = w->wl_next;
                    else
                        params = next;
                } else {
                    dev_flag = DGEN_ALLDEVS;
                    if (prev)
                        prev->wl_next = w->wl_next;
                    else
                        thisgroup = next;
                }
                /* w must not be freed here */
                w = NULL;
            } else if (eq(w->wl_word, "+")) {
                if (params) {
                    param_flag = DGEN_DEFPARAMS;
                    if (prev)
                        prev->wl_next = w->wl_next;
                    else
                        params = next;
                } else {
                    dev_flag = DGEN_DEFDEVS;
                    if (prev)
                        prev->wl_next = w->wl_next;
                    else
                        thisgroup = next;
                }
                /* w must not be freed here */
                w = NULL;
            } else if (eq(w->wl_word, ":")) {
                /* w must not be freed here */
                w = NULL;
                if (!params) {
                    params = next;
                    if (prev)
                        prev->wl_next = NULL;
                    else
                        thisgroup = NULL;
                } else {
                    if (prev)
                        prev->wl_next = next;
                    else
                        params = next;
                }
            } else if (eq(w->wl_word, ";") || eq(w->wl_word, ",")) {
                nextgroup = next;
                /* w must not be freed here */
                w = NULL;
                if (prev)
                    prev->wl_next = NULL;
                break;
            }
            prev = w;
        }

        instances = 0;
        for (dg = dgen_init(ft_curckt->ci_ckt, thisgroup, 1, dev_flag, mode);
             dg; dgen_nth_next(&dg, count))
        {
            instances = 1;
            if (dg->flags & DGEN_INSTANCE) {
                instances = 2;
                fprintf(cp_out, " %s: %s\n",
                        ft_sim->devices[dg->dev_type_no]->name,
                        ft_sim->devices[dg->dev_type_no]->description);
                n += 1;

                i = 0;
                do {
                    fprintf(cp_out, "%*s", LEFT_WIDTH, "device");
                    j = dgen_for_n(dg, count, printstr_n, NULL, i);
                    i += 1;
                    fprintf(cp_out, "\n");
                } while (j);

                if (ft_sim->devices[dg->dev_type_no]->numModelParms) {
                    i = 0;
                    do {
                        fprintf(cp_out, "%*s", LEFT_WIDTH, "model");
                        j = dgen_for_n(dg, count, printstr_m, NULL, i);
                        i += 1;
                        fprintf(cp_out, "\n");
                    } while (j);
                }

                if (param_flag)
                    param_forall_old(dg, param_flag);
                else if (!params)
                    param_forall_old(dg, DGEN_DEFPARAMS);

                if (params)
                    wl_forall(params, listparam, dg);
                fprintf(cp_out, "\n");

            } else if (ft_sim->devices[dg->dev_type_no]->numModelParms) {
                fprintf(cp_out, " %s models (%s)\n",
                        ft_sim->devices[dg->dev_type_no]->name,
                        ft_sim->devices[dg->dev_type_no]->description);
                n += 1;
                i = 0;
                do {
                    fprintf(cp_out, "%*s", LEFT_WIDTH, "model");
                    j = dgen_for_n(dg, count, printstr_m, NULL, i);
                    i += 1;
                    fprintf(cp_out, "\n");
                } while (j);
                fprintf(cp_out, "\n");

                if (param_flag)
                    param_forall_old(dg, param_flag);
                else if (!params)
                    param_forall_old(dg, DGEN_DEFPARAMS);

                if (params)
                    wl_forall(params, listparam, dg);
                fprintf(cp_out, "\n");
            }
        }

        wl = nextgroup;

    } while (wl);

    if (!n) {
        if (instances == 0)
            printf("No matching instances or models\n");
        else if (instances == 1)
            printf("No matching models\n");
        else
            printf("No matching elements\n");
    }
}


int
printstr_n(dgen *dg, IFparm *p, int i)
{
    NG_IGNORE(p);
    NG_IGNORE(i);

    if (dg->instance)
        fprintf(cp_out, " %*.*s", DEV_WIDTH, DEV_WIDTH, dg->instance->GENname);
    else
        fprintf(cp_out, " %*s", DEV_WIDTH, "<\?\?\?\?\?\?\?>");
    return 0;
}


int
printstr_m(dgen *dg, IFparm *p, int i)
{
    NG_IGNORE(p);
    NG_IGNORE(i);

    if (dg->model)
        fprintf(cp_out, " %*.*s", DEV_WIDTH, DEV_WIDTH, dg->model->GENmodName);
    else
        fprintf(cp_out, " %*s", DEV_WIDTH, "<\?\?\?\?\?\?\?>");
    return 0;
}


void
param_forall(dgen *dg, int flags)
{
    int i, j, k;
    int xcount;
    IFparm *plist;

    if (dg->flags & DGEN_INSTANCE) {
        xcount = *ft_sim->devices[dg->dev_type_no]->numInstanceParms;
        plist = ft_sim->devices[dg->dev_type_no]->instanceParms;
    } else {
        xcount = *ft_sim->devices[dg->dev_type_no]->numModelParms;
        plist = ft_sim->devices[dg->dev_type_no]->modelParms;
    }

    for (i = 0; i < xcount; i++)
        if (plist[i].dataType & IF_ASK)
            if ((((CKTcircuit *) (dg->ckt))->CKTrhsOld ||
                 (plist[i].dataType & IF_SET)) &&
                (!(plist[i].dataType & (IF_REDUNDANT | IF_UNINTERESTING)) ||
                 (flags == DGEN_ALLPARAMS && !(plist[i].dataType & IF_REDUNDANT))))
            {
                j = 0;
                do {
                    fprintf(cp_out, "    %-19s=", plist[i].keyword);

                    k = dgen_for_n(dg, count, printvals, (plist + i), j);
                    fprintf(cp_out, "\n");
                    j += 1;

                } while (k);
            }
}


void
param_forall_old(dgen *dg, int flags)
{
    int i, j, k;
    int xcount;
    IFparm *plist;

    if (dg->flags & DGEN_INSTANCE) {
        xcount = *ft_sim->devices[dg->dev_type_no]->numInstanceParms;
        plist = ft_sim->devices[dg->dev_type_no]->instanceParms;
    } else {
        xcount = *ft_sim->devices[dg->dev_type_no]->numModelParms;
        plist = ft_sim->devices[dg->dev_type_no]->modelParms;
    }

    for (i = 0; i < xcount; i++)
        if (plist[i].dataType & IF_ASK)
            if ((((CKTcircuit *) (dg->ckt))->CKTrhsOld ||
                 (plist[i].dataType & IF_SET)) &&
                (!(plist[i].dataType & (IF_REDUNDANT | IF_UNINTERESTING)) ||
                 (flags == DGEN_ALLPARAMS && !(plist[i].dataType & IF_REDUNDANT))))
            {
                j = 0;
                do {
                    if (!j)
                        fprintf(cp_out, "%*.*s", LEFT_WIDTH, LEFT_WIDTH,
                                plist[i].keyword);
                    else
                        fprintf(cp_out, "%*.*s", LEFT_WIDTH, LEFT_WIDTH, " ");
                    k = dgen_for_n(dg, count, printvals_old, (plist + i), j);
                    fprintf(cp_out, "\n");
                    j += 1;
                } while (k);
            }
}


void
listparam(wordlist *p, dgen *dg)
{
    int i, j, k, found;
    int xcount;
    IFparm *plist;

    found = 0;

    if (dg->flags & DGEN_INSTANCE) {
        xcount = *ft_sim->devices[dg->dev_type_no]->numInstanceParms;
        plist = ft_sim->devices[dg->dev_type_no]->instanceParms;
    } else {
        xcount = *ft_sim->devices[dg->dev_type_no]->numModelParms;
        plist = ft_sim->devices[dg->dev_type_no]->modelParms;
    }

    for (i = 0; i < xcount; i++)
        if (eqc(p->wl_word, plist[i].keyword) && (plist[i].dataType & IF_ASK)) {
            found = 1;
            break;
        }

    if (found) {
        if ((((CKTcircuit *) (dg->ckt))->CKTrhsOld ||
             (plist[i].dataType & IF_SET)))
        {
            j = 0;
            do {
                if (!j)
                    fprintf(cp_out, "%*.*s", LEFT_WIDTH, LEFT_WIDTH, p->wl_word);
                else
                    fprintf(cp_out, "%*.*s", LEFT_WIDTH, LEFT_WIDTH, " ");
                k = dgen_for_n(dg, count, printvals_old, (plist + i), j);
                printf("\n");
                j += 1;
            } while (k > 0);
        } else {
            j = 0;
            do {
                if (!j)
                    fprintf(cp_out, "%*.*s", LEFT_WIDTH, LEFT_WIDTH, p->wl_word);
                else
                    fprintf(cp_out, "%*s", LEFT_WIDTH, " ");
                k = dgen_for_n(dg, count, bogus1, 0, j);
                fprintf(cp_out, "\n");
                j += 1;
            } while (k > 0);
        }
    } else {
        j = 0;
        do {
            if (!j)
                fprintf(cp_out, "%*.*s", LEFT_WIDTH, LEFT_WIDTH, p->wl_word);
            else
                fprintf(cp_out, "%*s", LEFT_WIDTH, " ");
            k = dgen_for_n(dg, count, bogus2, 0, j);
            fprintf(cp_out, "\n");
            j += 1;
        } while (k > 0);
    }
}


int
bogus1(dgen *dg, IFparm *p, int i)
{
    NG_IGNORE(dg);
    NG_IGNORE(p);
    NG_IGNORE(i);

    fprintf(cp_out, " %*s", DEV_WIDTH, "---------");
    return 0;
}


int
bogus2(dgen *dg, IFparm *p, int i)
{
    NG_IGNORE(dg);
    NG_IGNORE(p);
    NG_IGNORE(i);

    fprintf(cp_out, " %*s", DEV_WIDTH, "?????????");
    return 0;
}


int
printvals(dgen *dg, IFparm *p, int i)
{
    IFvalue     val;
    int         n;

    if (dg->flags & DGEN_INSTANCE)
        ft_sim->askInstanceQuest
            (ft_curckt->ci_ckt, dg->instance, p->id, &val, &val);
    else
        ft_sim->askModelQuest
            (ft_curckt->ci_ckt, dg->model, p->id, &val, &val);

    if (p->dataType & IF_VECTOR)
        n = val.v.numValue;
    else
        n = 1;

    if (((p->dataType & IF_VARTYPES) & ~IF_VECTOR) == IF_COMPLEX)
        n *= 2;

    if (i >= n) {
        if (i == 0)
            fprintf(cp_out, "         -");
        else
            fprintf(cp_out, "          ");
        return 0;
    }

    if (p->dataType & IF_VECTOR) {
        /* va: ' ' is no flag for %s */
        switch ((p->dataType & IF_VARTYPES) & ~IF_VECTOR) {
        case IF_FLAG:
            fprintf(cp_out, " %d", val.v.vec.iVec[i]);
            break;
        case IF_INTEGER:
            fprintf(cp_out, " %d", val.v.vec.iVec[i]);
            break;
        case IF_REAL:
            fprintf(cp_out, " %.6g", val.v.vec.rVec[i]);
            break;
        case IF_COMPLEX:
            if (!(i % 2))
                fprintf(cp_out, " %.6g", val.v.vec.cVec[i / 2].real);
            else
                fprintf(cp_out, " %.6g", val.v.vec.cVec[i / 2].imag);
            break;
        case IF_STRING:
            fprintf(cp_out, " %s", val.v.vec.sVec[i]);
            break;
        case IF_INSTANCE:
            fprintf(cp_out, " %s", val.v.vec.uVec[i]);
            break;
        default:
            fprintf(cp_out, " %s", " ******** ");
        }
    } else {
        switch ((p->dataType & IF_VARTYPES) & ~IF_VECTOR) {
        case IF_FLAG:
            fprintf(cp_out, " %d", val.iValue);
            break;
        case IF_INTEGER:
            fprintf(cp_out, " %d", val.iValue);
            break;
        case IF_REAL:
            fprintf(cp_out, " %.6g", val.rValue);
            break;
        case IF_COMPLEX:
            if (i % 2)
                fprintf(cp_out, " %.6g", val.cValue.real);
            else
                fprintf(cp_out, " %.6g", val.cValue.imag);
            break;
        case IF_STRING:
            fprintf(cp_out, " %s", val.sValue);
            break;
        case IF_INSTANCE:
            fprintf(cp_out, " %s", val.uValue);
            break;
        default:
            fprintf(cp_out, " %s", " ******** ");
        }
    }

    return n - 1;
}


int
printvals_old(dgen *dg, IFparm *p, int i)
{
    IFvalue     val;
    int         n, error;

    if (dg->flags & DGEN_INSTANCE)
        error = ft_sim->askInstanceQuest
            (ft_curckt->ci_ckt, dg->instance, p->id, &val, &val);
    else
        error = ft_sim->askModelQuest
            (ft_curckt->ci_ckt, dg->model, p->id, &val, &val);

    if (p->dataType & IF_VECTOR)
        n = val.v.numValue;
    else
        n = 1;

    if (((p->dataType & IF_VARTYPES) & ~IF_VECTOR) == IF_COMPLEX)
        n *= 2;

    if (i >= n) {
        if (i == 0)
            fprintf(cp_out, "         -");
        else
            fprintf(cp_out, "          ");
        return 0;
    }

    if (error) {
        fprintf(cp_out, " <<NAN, error = %d>>", error);
    } else if (p->dataType & IF_VECTOR) {
        /* va: ' ' is no flag for %s */
        switch ((p->dataType & IF_VARTYPES) & ~IF_VECTOR) {
        case IF_FLAG:
            fprintf(cp_out, " % *d", DEV_WIDTH, val.v.vec.iVec[i]);
            break;
        case IF_INTEGER:
            fprintf(cp_out, " % *d", DEV_WIDTH, val.v.vec.iVec[i]);
            break;
        case IF_REAL:
            fprintf(cp_out, " % *.6g", DEV_WIDTH, val.v.vec.rVec[i]);
            break;
        case IF_COMPLEX:
            if (!(i % 2))
                fprintf(cp_out, " % *.6g", DEV_WIDTH, val.v.vec.cVec[i / 2].real);
            else
                fprintf(cp_out, " % *.6g", DEV_WIDTH, val.v.vec.cVec[i / 2].imag);
            break;
        case IF_STRING:
            fprintf(cp_out, " %*.*s", DEV_WIDTH, DEV_WIDTH, val.v.vec.sVec[i]);
            break;
        case IF_INSTANCE:
            fprintf(cp_out, " %*.*s", DEV_WIDTH, DEV_WIDTH, val.v.vec.uVec[i]);
            break;
        default:
            fprintf(cp_out, " %*.*s", DEV_WIDTH, DEV_WIDTH, " ******** ");
        }
    } else {
        switch ((p->dataType & IF_VARTYPES) & ~IF_VECTOR) {
        case IF_FLAG:
            fprintf(cp_out, " % *d", DEV_WIDTH, val.iValue);
            break;
        case IF_INTEGER:
            fprintf(cp_out, " % *d", DEV_WIDTH, val.iValue);
            break;
        case IF_REAL:
            fprintf(cp_out, " % *.6g", DEV_WIDTH, val.rValue);
            break;
        case IF_COMPLEX:
            if (i % 2)
                fprintf(cp_out, " % *.6g", DEV_WIDTH, val.cValue.real);
            else
                fprintf(cp_out, " % *.6g", DEV_WIDTH, val.cValue.imag);
            break;
        case IF_STRING:
            fprintf(cp_out, " %*.*s", DEV_WIDTH, DEV_WIDTH, val.sValue);
            break;
        case IF_INSTANCE:
            fprintf(cp_out, " %*.*s", DEV_WIDTH, DEV_WIDTH, val.uValue);
            break;
        default:
            fprintf(cp_out, " %*.*s", DEV_WIDTH, DEV_WIDTH, " ******** ");
        }
    }

    return n - 1;
}


/* (old "show" command)
 * Display various device parameters.  The syntax of this command is
 *   show devicelist : parmlist
 * where devicelist can be "all", the name of a device, a string like r*,
 * which means all devices with names that begin with 'r', repeated one
 * or more times.   The parms are names of parameters that are (hopefully)
 * valid for all the named devices, or "all".
 */

void
old_show(wordlist *wl)
{
    wordlist *devs, *parms, *tw, *ww;
    struct variable *v;
    char *nn;

    devs = wl;
    wl = wl_find(":", wl);
    if (!wl) {
        parms = NULL;
    } else {
        if (wl->wl_prev)
            wl->wl_prev->wl_next = NULL;
        parms = wl->wl_next;
        if (parms)
            parms->wl_prev = NULL;
    }

    /* Now expand the devicelist... */
    for (tw = NULL; devs; devs = devs->wl_next) {
        inp_casefix(devs->wl_word);
        tw = wl_append(tw, devexpand(devs->wl_word));
    }

    devs = tw;
    tw = wl_find("all", parms);
    if (tw)
        parms = NULL;

    /* This is a crock... */
    if (!devs)
        devs = cp_cctowl(ft_curckt->ci_devices);

    out_init();

    while (devs) {
        out_printf("%s:\n", devs->wl_word);
        if (parms) {
            for (tw = parms; tw; tw = tw->wl_next) {
                nn = copy(devs->wl_word);
                v = if_getparam(ft_curckt->ci_ckt, &nn, tw->wl_word, 0, 0);
                if (!v)
                    v = if_getparam(ft_curckt->ci_ckt, &nn, tw->wl_word, 0, 1);
                if (v) {
                    out_printf("\t%s =", tw->wl_word);
                    for (ww = cp_varwl(v); ww; ww = ww->wl_next)
                        out_printf(" %s", ww->wl_word);
                    out_send("\n");
                }
            }
        } else {
            nn = copy(devs->wl_word);
            v = if_getparam(ft_curckt->ci_ckt, &nn, "all", 0, 0);
            if (!v)
                v = if_getparam(ft_curckt->ci_ckt, &nn, "all", 0, 1);
            while (v) {
                out_printf("\t%s =", v->va_name);
                for (ww = cp_varwl(v); ww; ww = ww->wl_next)
                    out_printf(" %s", ww->wl_word);
                out_send("\n");
                v = v->va_next;
            }
        }
        devs = devs->wl_next;
    }
}


/* Alter a device parameter.  The new syntax here is
 *      alter @device[parameter] = expr
 *      alter device = expr
 *      alter device parameter = expr
 * expr must be real (complex isn't handled right now, integer is fine though,
 * but no strings ... for booleans, use 0/1).
 */

static void com_alter_common(wordlist *wl, int do_model);

void
com_alter(wordlist *wl)
{
    if (!wl) {
        fprintf(cp_err, "usage: alter dev param = expression\n");
        fprintf(cp_err, "  or   alter @dev[param] = expression\n");
        fprintf(cp_err, "  or   alter dev = expression\n");
        return;
    }

    com_alter_common(wl, 0);
}


void
com_altermod(wordlist *wl)
{
    wordlist *fileword;
    bool newfile = FALSE;

    fileword = wl;
    while (fileword) {
        if (ciprefix("file", fileword->wl_word))
            newfile = TRUE;
        fileword = fileword->wl_next;
    }

    if (newfile)
        com_alter_mod(wl);
    else
        com_alter_common(wl, 1);
}


static void
if_set_binned_model(CKTcircuit *ckt, char *devname, char *param, struct dvec *val)
{
    char *width_length;
    double w = 0.0, l = 0.0;
    struct variable *v;

    v = if_getparam(ckt, &devname, "w", 0, 0);
    if (!v) {
        fprintf(cp_err, "Error: Can't access width instance parameter.\n");
        return;
    }
    w = v->va_V.vV_real;

    v = if_getparam(ckt, &devname, "l", 0, 0);
    if (!v) {
        fprintf(cp_err, "Error: Can't access length instance parameter.\n");
        return;
    }
    l = v->va_V.vV_real;

    if (param[0] == 'w')
        w = *val->v_realdata; /* overwrite the width with the alter param */
    else
        l = *val->v_realdata; /* overwrite the length with the alter param */

    width_length = tprintf("w=%15.7e l=%15.7e", w, l);

    if_setparam_model(ft_curckt->ci_ckt, &devname, width_length);
    FREE(width_length);
}


static void
com_alter_common(wordlist *wl, int do_model)
{
    wordlist *eqword = NULL, *words;
    char *dev, *p;
    char *param;
    struct dvec *dv;
    struct pnode *names;

    /* DIE 2009_02_06 */
    char *argument;
    char **arglist;
    int i = 0, step = 0, n, wlen, maxelem = 3;
    wordlist *wl2 = NULL, *wlin, *rhs;
    bool eqfound = FALSE;

    if (!ft_curckt) {
        fprintf(cp_err, "Error: no circuit loaded\n");
        return;
    }

    /*
      wordlist 'wl' will be splitted into a wordlist wl2 with three elements,
      containing
      1) '@dev[param]' string (i.e.: the substring before '=' char);
      2) '=' string;
      3) 'expression' string.

      Spaces around the '=' sign have to be removed. This is provided
      by inp_remove_excess_ws(). But take care if command is entered manually!

      If the 'altermod' argument is 'altermod m1 vth0=0.7', 'm1' has to be kept as the
      element in wl2 before splitting inserts the three new elements.
      If 'expression' is a vector (e.g. [ 1.0 1.2 1.4 ] ), its elements
      in wl2 have to follow the splitting. wl_splice() will take care of this.
    */
    wlin = wl;
    while (wl) {
        argument = wl->wl_word;
        /* searching for '=' ... */
        i = 0;
        while (argument[i] != '=' && argument[i] != '\0')
            i++;

        /* argument may be '=', then do nothing
           or =token
           or token=
           or token1=token2
           ...and if found split argument into three chars and make a new wordlist */
        if (argument[i] != '\0') {
            /* We found '=' */
            eqfound = TRUE;
            if (strlen(argument) == 1) {
                wl = wl->wl_next;
                step = -1;
                wl2 = wlin;
            } else if (strlen(argument) > 1) {
                arglist = TMALLOC(char*, 4);
                arglist[3] = NULL;
                arglist[0] = TMALLOC(char, i + 1);
                arglist[2] = TMALLOC(char, strlen(&argument[i + 1]) + 1);
                /* copy argument */
                strncpy(arglist[0], argument, (size_t) i);
                arglist[0][i] = '\0';
                /* copy equal sign */
                arglist[1] = copy("=");
                /* copy expression */
                strncpy(arglist[2], &argument[i+1], strlen(&argument[i+1])+1);

                /* create a new wordlist from array arglist */
                wl2 = wl_build(arglist);
                /* combine wordlists into wl2, free wl */
                wl_splice(wl, wl2);
                wl = NULL;
                /* free arglist */
                for (n = 0; n < 3; n++)
                    tfree(arglist[n]);
                tfree(arglist);
            }
        } else {
            /* deal with 'altermod m1 vth0=0.7' by moving
               forward beyond 'm1' */
            wl = wl->wl_next;
            step++;
        }
    }

    if (eqfound) {
        /* step back in the wordlist, if we have moved forward, to catch 'm1' */
        for (n = step; n > 0; n--)
            wl2 = wl2->wl_prev;
    } else {
        /* no equal sign found, probably a pre3f4 input format
           'alter device value'
           'alter device parameter value'
           are supported,
           'alter device parameter value parameter value [ parameter value ]'
           multiple param value pairs are not supported!
        */
        wl2 = wlin;
        wlen = wl_length(wlin);
        /* Return the last element of wlin */
        wlin = wl_nthelem(100, wlin); /* no more than 100 vector elements */

        if (eq(wlin->wl_word, "]"))     /* we have a vector */
            for (n = 0; n < 100; n++) { /* no more than 100 vector elements */
                wlin = wlin->wl_prev;
                maxelem++;
                if (eq(wlin->wl_word, "[")) {
                    break;
                }
                if (wlin->wl_prev == NULL) {
                    fprintf(cp_err, "Error: '[' is missing.\n");
                    fprintf(cp_err, "Cannot alter parameters.\n");
                    return;
                }
            }

        if (wlen > maxelem) {
            fprintf(cp_err, "Error: Only a single param - value pair supported.\n");
            fprintf(cp_err, "Cannot alter parameters.\n");
            return;
        }
        /* add the '=' */
        wlin = wlin->wl_prev;
        rhs = wl_chop_rest(wlin);
        wlin = wl_append(wlin, wl_cons(copy("="), rhs));
        /* step back until 'alter' or 'altermod' is found,
           then move one step forward */
        while (!ciprefix("alter", wlin->wl_word)) //while (!ciprefix(wlin->wl_word, "alter"))
            wlin = wlin->wl_prev;
        wlin = wlin->wl_next;
        wl2 = wlin;
    }

    /* Everything is ready, parsing of the wordlist starts here. */
    words = wl2;
    while (words) {
        p = words->wl_word;
        eqword = words;
        words = words->wl_next;
        if (eq(p, "="))
            break;
    }

    if (!words) {
        fprintf(cp_err, "Error: no assignment found.\n");
        fprintf(cp_err, "Cannot alter parameters.\n");
        return;
    }

    /* device parameter = expr
       device = expr
       @dev[param] = expr
    */

    dev = NULL;
    param = NULL;
    words = wl2;
    while (words != eqword) {
        p = words->wl_word;
        if (param) {
            fprintf(cp_err, "Warning: excess parameter name \"%s\" ignored.\n", p);
        } else if (dev) {
            param = words->wl_word;
        } else if (*p == '@' || *p == '#') {
            dev = p + 1;
            p = strchr(p, '[');
            if (p) {
                *p++ = 0;
                param = p;
                p = strchr(p, ']');
                if (p)
                    *p = 0;
            }
        } else {
            dev = p;
        }
        words = words->wl_next;
    }

    if (!dev) {
        fprintf(cp_err, "Error: no model or device name provided.\n");
        fprintf(cp_err, "Cannot alter parameters.\n");
        return;
    }

    words = eqword->wl_next;
    /* skip next line if words is a vector */
    if (!eq(words->wl_word, "["))
        names = ft_getpnames(words, FALSE);
    else
        names = NULL;

    if (!names) {
        /* Put this to try to resolve the case of
           alter @vin[pulse] = [ 0 5 10n 10n 10n 50n 100n ]
        */
        char *xsbuf, *rem_xsbuf;
        int type = IF_REALVEC, i = 0;

        double *list;
        double tmp;
        int error;
        /* move beyond '[' to allow INPevaluate() */
        if (eq(words->wl_word, "["))
            words = words->wl_next;
        xsbuf = rem_xsbuf = wl_flatten(words);
        /* fprintf(cp_err, "Chain    converted  %s \n", xsbuf); */
        dv = TMALLOC(struct dvec, 1);
        dv->v_name = copy("real vector");
        type &= IF_VARTYPES;
        if (type == IF_REALVEC) {
            list = TMALLOC(double, 1);
            tmp = INPevaluate(&xsbuf, &error, 1);
            while (error == 0) {
                /*printf(" returning vector value %g\n", tmp); */
                i++;
                list = TREALLOC(double, list, i);
                list[i-1] = tmp;
                tmp = INPevaluate(&xsbuf, &error, 1);
            }
            dv->v_realdata = list;
        }
        dv->v_length = i;

        if (!dv)
            return;

        if (dv->v_length < 1) {
            fprintf(cp_err, "Error: cannot evaluate new parameter value.\n");
            return;
        }

        /*       Here I was, to change the inclusion in the circuit.
         * will have to revise that dv is right for its insertion.
         */
        if_setparam(ft_curckt->ci_ckt, &dev, param, dv, do_model);

        tfree(rem_xsbuf);
        vec_free(dv);
        return;
    }

    dv = ft_evaluate(names);
    if (!dv)
        goto done;

    if (dv->v_length < 1) {
        fprintf(cp_err, "Error: cannot evaluate new parameter value.\n");
        goto done;
    }

    /* If we want alter the geometry of a MOS device
       we have to ensure that we are in the valid model bin. */
    if ((dev[0] == 'm') && ((param[0] == 'w') || (param[0] == 'l')))
        if_set_binned_model(ft_curckt->ci_ckt, dev, param, dv);

    if_setparam(ft_curckt->ci_ckt, &dev, param, dv, do_model);

    /* va: garbage collection for dv, if pnode names is no simple value */
    if (names->pn_value == NULL && dv != NULL)
        vec_free(dv);

done:
    free_pnode(names); /* free also dv, if pnode names is simple value */
}


/* Given a device name, possibly with wildcards, return the matches. */

static wordlist *
devexpand(char *name)
{
    wordlist *wl, *devices;

    if (strchr(name, '*') || strchr(name, '[') || strchr(name, '?')) {
        devices = cp_cctowl(ft_curckt->ci_devices);
        for (wl = NULL; devices; devices = devices->wl_next)
            if (cp_globmatch(name, devices->wl_word))
                wl = wl_cons(devices->wl_word, wl);
    } else if (cieq(name, "all")) {
        wl = cp_cctowl(ft_curckt->ci_devices);
    } else {
        wl = wl_cons(name, NULL);
    }

    wl_sort(wl);
    return (wl);
}


/* altermod mod_1 [mod_nn] file=modelparam.mod
   load model file and overwrite models mod_1 till mod_nn with
   all new parameters (limited to 16 models) */
static void
com_alter_mod(wordlist *wl)
{
#define MODLIM 16 /* max number of models */
    FILE *modfile;
    char *modellist[MODLIM] = {NULL}, *modellines[MODLIM] = {NULL}, *newmodelname, *newmodelline;
    char *filename = NULL, *eqword, *input, *modelline = NULL, *inptoken;
    int modno = 0, molineno = 0, i, j;
    wordlist *newcommand;
    struct line *modeldeck, *tmpdeck;
    char *readmode = "r";
    char **arglist;
    bool modelfound = FALSE;
    int ij[MODLIM];

    /* initialize */
    for (i = 0; i < MODLIM; i++)
        ij[i] = -1;

    /* read all model names */
    while (!ciprefix("file", wl->wl_word)) {
        if (modno == MODLIM) {
            fprintf(cp_err, "Error: too many model names in altermod command\n");
            controlled_exit(1);
        }
        modellist[modno] = copy(wl->wl_word);
        modno++;
        wl = wl->wl_next;
    }
    input = wl_flatten(wl);
    /* get the file name */
    eqword = strstr(input, "=");
    if (eqword) {
        eqword++;
        while (*eqword == ' ')
            eqword++;
        if (eqword == '\0') {
            fprintf(cp_err, "Error: no filename given\n");
            controlled_exit(1);
        }
        filename = copy(eqword);
    } else {
        eqword = strstr(input, "file");
        eqword += 4;
        while (*eqword == ' ')
            eqword++;
        if (eqword == '\0') {
            fprintf(cp_err, "Error: no filename given\n");
            controlled_exit(1);
        }
        filename = copy(eqword);
    }
    modfile = inp_pathopen(filename, readmode);
    {
        char *dir_name = ngdirname(filename);
        modeldeck = inp_readall(modfile, dir_name, 0, 0);
        free(dir_name);
    }
    tfree(input);
    tfree(filename);
    /* get all lines starting with *model */
    for (tmpdeck = modeldeck; tmpdeck; tmpdeck = tmpdeck->li_next)
        if (ciprefix("*model", tmpdeck->li_line)) {
            if (molineno == MODLIM) {
                fprintf(cp_err, "Error: more than %d models in deck, rest ignored\n", molineno);
                break;
            }
            modellines[molineno] = tmpdeck->li_line;
            molineno++;
        }
    /* Check if all models named in altermod command are to be found in input deck.
       Exit if not successfull */
    for (i = 0; i < modno; i++) {
        for (j = 0; j < molineno; j++) {
            newmodelline = modellines[j];
            /* get model name from model line */
            inptoken = gettok(&newmodelline); /* *model */
            tfree(inptoken);
            newmodelname = gettok(&newmodelline); /* modelname */
            if (cieq(newmodelname, modellist[i])) {
                modelfound = TRUE;
                tfree(newmodelname);
                break;
            }
            tfree(newmodelname);
        }
        if (modelfound) {
            modelfound = FALSE;
            ij[i] = j; /* model in altermod, found in model line */
            continue;
        } else {
            fprintf(cp_err, "Error: could not find model %s in input deck\n", modellist[i]);
            controlled_exit(1);
        }
    }
    /* read the model line, generate the altermod commands as a wordlist,
       and call com_alter_common() */
    arglist = TMALLOC(char*, 4);
    arglist[0] = copy("altermod");
    arglist[3] = NULL;
    /* for each model name of altermod command */
    for (i = 0; i < modno; i++) {
        /* model name */
        arglist[1] = copy(modellist[i]);
        /* parse model line from deck */
        modelline = modellines[ij[i]];
        inptoken = gettok(&modelline); /* skip *model */
        tfree(inptoken);
        inptoken = gettok(&modelline); /* skip modelname */
        tfree(inptoken);
        inptoken = gettok(&modelline); /* skip model type */
        tfree(inptoken);
        while ((inptoken = gettok(&modelline)) != NULL) {
            /* exclude level and version */
            if (ciprefix("version", inptoken) || ciprefix("level", inptoken)) {
                tfree(inptoken);
                continue;
            }
            arglist[2] = inptoken;
            /* create a new wordlist from array arglist */
            newcommand = wl_build(arglist);
            com_alter_common(newcommand->wl_next, 1);
            wl_free(newcommand);
            tfree(inptoken);
        }
        tfree(arglist[1]);
    }
    tfree(arglist[0]);
    tfree(arglist[3]);
}
