ngspice/src/frontend/inp.c

2603 lines
87 KiB
C

/**********
Copyright 1990 Regents of the University of California. All rights reserved.
Author: 1985 Wayne A. Christopher
**********/
/*
* Stuff for dealing with spice input decks and command scripts, and
* the listing routines.
*/
#include "ngspice/ngspice.h"
#include "ngspice/cktdefs.h"
#include "ngspice/cpdefs.h"
#include "ngspice/inpdefs.h"
#include "ngspice/ftedefs.h"
#include "ngspice/dvec.h"
#include "ngspice/fteinp.h"
#include "inp.h"
#include "runcoms.h"
#include "inpcom.h"
#include "circuits.h"
#include "completion.h"
#include "variable.h"
#include "breakp2.h"
#include "dotcards.h"
#include "../misc/util.h" /* ngdirname() */
#include "../misc/mktemp.h"
#include "../misc/misc_time.h"
#include "subckt.h"
#include "spiceif.h"
#include "com_let.h"
#include "com_commands.h"
#ifdef XSPICE
#include "ngspice/ipctiein.h"
#include "ngspice/enh.h"
#endif
#include "numparam/numpaif.h"
#include "ngspice/stringskip.h"
#include "ngspice/randnumb.h"
#include "ngspice/compatmode.h"
#define line_free(line, flag) \
do { \
line_free_x(line, flag); \
line = NULL; \
} while(0)
static struct card *com_options = NULL;
static struct card *mc_deck = NULL;
static struct card *recent_deck = NULL;
static void cktislinear(CKTcircuit *ckt, struct card *deck);
void create_circbyline(char *line, bool reset, bool lastline);
static bool doedit(char *filename);
static void dotifeval(struct card *deck);
static void eval_agauss(struct card *deck, char *fcn);
static wordlist *inp_savecurrents(struct card *deck, struct card *options,
wordlist *wl, wordlist *controls);
void line_free_x(struct card *deck, bool recurse);
static void recifeval(struct card *pdeck);
static char *upper(register char *string);
static void rem_unused_mos_models(struct card* deck);
//void inp_source_recent(void);
//void inp_mc_free(void);
//void inp_remove_recent(void);
static bool mc_reload = FALSE;
void eval_opt(struct card *deck);
extern bool ft_batchmode;
/* from inpcom.c */
extern struct nscope* inp_add_levels(struct card *deck);
extern void comment_out_unused_subckt_models(struct card *deck);
extern void inp_rem_unused_models(struct nscope *root, struct card *deck);
#ifdef SHARED_MODULE
extern void exec_controls(wordlist *controls);
#endif
/* display the source file name in the source window */
#ifdef HAS_WINGUI
extern void SetSource(char *Name);
#endif
/* structure used to save expression parse trees for .model and
* device instance lines
*/
struct pt_temper {
char *expression;
wordlist *wl;
wordlist *wlend;
INPparseTree *pt;
struct pt_temper *next;
};
static int inp_parse_temper(struct card *deck,
struct pt_temper **motdlist_p,
struct pt_temper **devtlist_p);
static void inp_parse_temper_trees(struct circ *ckt);
/*
* create an unique artificial *unusable* FILE ptr
* meant to be used with Xprintf() only to eventually
* redirect output to the `out_vprintf()' family
*/
static FILE *cp_more;
static FILE *cp_more = (FILE*) &cp_more;
static void
Xprintf(FILE *fdst, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (fdst == cp_more)
out_vprintf(fmt, ap);
else
vfprintf(fdst, fmt, ap);
va_end(ap);
}
/* Do a listing. Use is listing [expanded] [logical] [physical] [deck] */
void
com_listing(wordlist *wl)
{
int type = LS_LOGICAL;
bool expand = FALSE, do_param_listing = FALSE;
char *s;
if (ft_curckt) { /* if there is a current circuit . . . . */
while (wl) {
s = wl->wl_word;
if (strcmp(s, "param") == 0) {
do_param_listing = TRUE;
} else {
switch (*s) {
case 'l':
case 'L':
type = LS_LOGICAL;
break;
case 'p':
case 'P':
type = LS_PHYSICAL;
break;
case 'd':
case 'D':
type = LS_DECK;
break;
case 'e':
case 'E':
expand = TRUE;
break;
default:
fprintf(cp_err, "Error: bad listing type %s\n", s);
return; /* SJB - don't go on after an error */
}
}
wl = wl->wl_next;
}
if (do_param_listing) {
nupa_list_params(cp_out);
} else {
if (type != LS_DECK)
fprintf(cp_out, "\t%s\n\n", ft_curckt->ci_name);
inp_list(cp_out,
expand ? ft_curckt->ci_deck : ft_curckt->ci_origdeck,
ft_curckt->ci_options, type);
}
} else {
fprintf(cp_err, "Error: no circuit loaded.\n");
}
}
/* returns inp_casefix() or NULL */
static char *
upper(char *string)
{
static char buf[LBSIZE_SP];
if (string) {
if (strlen(string) > LBSIZE_SP - 1)
fprintf(stderr, "Warning: output of command 'listing' will be truncated\n");
strncpy(buf, string, LBSIZE_SP - 1);
buf[LBSIZE_SP - 1] = '\0';
inp_casefix(buf);
} else {
strcpy(buf, "<null>");
}
return buf;
}
/* Provide an input listing on the specified file of the given card
* deck. The listing should be of either LS_PHYSICAL or LS_LOGICAL or
* LS_DECK lines as specified by the type parameter. */
void
inp_list(FILE *file, struct card *deck, struct card *extras, int type)
{
struct card *here;
struct card *there;
bool renumber;
bool useout = (file == cp_out);
int i = 1;
/* gtri - wbk - 03/07/91 - Don't use 'more' type output if ipc enabled */
#ifdef XSPICE
if (g_ipc.enabled)
useout = FALSE;
#endif
/* gtri - end - 03/07/91 */
if (useout) {
out_init();
file = cp_more;
}
renumber = cp_getvar("renumber", CP_BOOL, NULL, 0);
if (type == LS_LOGICAL) {
top1:
for (here = deck; here; here = here->nextcard) {
if (renumber)
here->linenum = i;
if (ciprefix(".end", here->line) && !isalpha_c(here->line[4]))
continue;
if (*here->line != '*') {
Xprintf(file, "%6d : %s\n", here->linenum, upper(here->line));
if (here->error)
Xprintf(file, "%s\n", here->error);
}
i++;
}
if (extras) {
deck = extras;
extras = NULL;
goto top1;
}
Xprintf(file, "%6d : .end\n", i);
} else if ((type == LS_PHYSICAL) || (type == LS_DECK)) {
top2:
for (here = deck; here; here = here->nextcard) {
if ((here->actualLine == NULL) || (here == deck)) {
if (renumber)
here->linenum = i;
if (ciprefix(".end", here->line) && !isalpha_c(here->line[4]))
continue;
if (type == LS_PHYSICAL)
Xprintf(file, "%6d : %s\n",
here->linenum, upper(here->line));
else
Xprintf(file, "%s\n", upper(here->line));
if (here->error && (type == LS_PHYSICAL))
Xprintf(file, "%s\n", here->error);
} else {
for (there = here->actualLine; there; there = there->nextcard) {
there->linenum = i++;
if (ciprefix(".end", here->line) && isalpha_c(here->line[4]))
continue;
if (type == LS_PHYSICAL)
Xprintf(file, "%6d : %s\n",
there->linenum, upper(there->line));
else
Xprintf(file, "%s\n", upper(there->line));
if (there->error && (type == LS_PHYSICAL))
Xprintf(file, "%s\n", there->error);
}
here->linenum = i;
}
i++;
}
if (extras) {
deck = extras;
extras = NULL;
goto top2;
}
if (type == LS_PHYSICAL)
Xprintf(file, "%6d : .end\n", i);
else
Xprintf(file, ".end\n");
} else {
fprintf(cp_err, "inp_list: Internal Error: bad type %d\n", type);
}
}
/*
* Free memory used by a line.
* If recurse is TRUE then recursively free all lines linked via the ->nextcard field.
* If recurse is FALSE free only this line.
* All lines linked via the ->actualLine field are always recursivly freed.
* SJB - 22nd May 2001
*/
void
line_free_x(struct card *deck, bool recurse)
{
while (deck) {
struct card *next_deck = deck->nextcard;
line_free_x(deck->actualLine, TRUE);
tfree(deck->line);
tfree(deck->error);
tfree(deck);
if (!recurse)
return;
deck = next_deck;
}
}
/* concatenate two lists, destructively altering the first one */
struct card *
line_nconc(struct card *head, struct card *rest)
{
struct card *p = head;
if (!rest)
return head;
if (!head)
return rest;
while (p->nextcard)
p = p->nextcard;
p->nextcard = rest;
return head;
}
/* reverse the linked list struct card */
struct card *
line_reverse(struct card *head)
{
struct card *prev = NULL;
struct card *next;
while (head) {
next = head->nextcard;
head->nextcard = prev;
prev = head;
head = next;
}
return prev;
}
/* store ft_curckt->ci_mcdeck into a 'previous' deck */
void
inp_mc_free(void)
{
if (ft_curckt && ft_curckt->ci_mcdeck) {
if (recent_deck && recent_deck != ft_curckt->ci_mcdeck) {
struct circ *pp;
/* NULL any ci_mcdeck entry from ft_circuits whose address equals recent_deck,
then free this address */
for (pp = ft_circuits; pp; pp = pp->ci_next)
if (pp->ci_mcdeck == recent_deck) {
pp->ci_mcdeck = NULL;
}
line_free(recent_deck, TRUE);
}
recent_deck = ft_curckt->ci_mcdeck;
ft_curckt->ci_mcdeck = NULL;
}
}
/* called by com_rset: reload most recent circuit */
void
inp_source_recent(void) {
mc_deck = recent_deck;
mc_reload = TRUE;
inp_spsource(NULL, FALSE, NULL, FALSE);
}
/* remove the 'recent' deck */
void
inp_remove_recent(void) {
if (recent_deck)
line_free(recent_deck, TRUE);
}
/* Check for .option seed=[val|random] and set the random number generator.
Check for .option cshunt=val and set a global variable
Input is the option deck (already sorted for .option) */
void
eval_opt(struct card* deck)
{
struct card* card;
bool has_seed = FALSE;
bool has_cshunt = FALSE;
for (card = deck; card; card = card->nextcard) {
char* line = card->line;
if (strstr(line, "seedinfo"))
setseedinfo();
char* begtok = strstr(line, "seed=");
if (begtok)
begtok = &begtok[5]; /*skip seed=*/
if (begtok) {
if (has_seed)
fprintf(cp_err, "Warning: Multiple 'option seed=val|random' found!\n");
char* token = gettok(&begtok);
/* option seed=random [seed='random'] */
if (eq(token, "random") || eq(token, "{random}")) {
time_t acttime = time(NULL);
/* get random value from time in seconds since 1.1.1970 */
int rseed = (int)(acttime - 1600000000);
cp_vset("rndseed", CP_NUM, &rseed);
com_sseed(NULL);
has_seed = TRUE;
}
/* option seed=val*/
else {
int sr = atoi(token);
if (sr <= 0)
fprintf(cp_err, "Warning: Cannot convert 'option seed=%s' to seed value, skipped!\n", token);
else {
cp_vset("rndseed", CP_NUM, &sr);
com_sseed(NULL);
has_seed = TRUE;
}
}
tfree(token);
}
begtok = strstr(line, "cshunt=");
if (begtok)
begtok = &begtok[7]; /*skip cshunt=*/
if (begtok) {
int err = 0;
if (has_cshunt)
fprintf(cp_err, "Warning: Multiple '.option cshunt=val' found!\n");
/* option cshunt=val*/
double sr = INPevaluate(&begtok, &err, 0);
if (sr <= 0 || err)
fprintf(cp_err, "Warning: Cannot convert 'option cshunt=%s' to capacitor value, skipped!\n", begtok);
else {
cp_vset("cshunt_value", CP_REAL, &sr);
has_cshunt = TRUE;
}
}
}
}
/* The routine to source a spice input deck. We read the deck in, take
* out the front-end commands, and create a CKT structure. Also we
* filter out the following cards: .save, .width, .four, .print, and
* .plot, to perform after the run is over.
* Then, we run dodeck, which parses up the deck. */
int
inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile)
/* arguments:
* *fp = pointer to the input file
* comfile = whether it is a command file. Values are TRUE/FALSE
* *filename = name of input file
* intfile = whether input is from internal array. Values are TRUE/FALSE
*/
{
struct card *deck = NULL, *dd, *ld, *prev_param = NULL, *prev_card = NULL;
struct card *realdeck = NULL, *options = NULL, *curr_meas = NULL;
char *tt = NULL, name[BSIZE_SP + 1], *s, *t, *temperature = NULL;
bool commands = FALSE;
wordlist *wl = NULL, *end = NULL, *wl_first = NULL;
wordlist *controls = NULL, *pre_controls = NULL;
FILE *lastin, *lastout, *lasterr;
double temperature_value;
bool expr_w_temper = FALSE;
double startTime, loadTime = 0., endTime;
#ifdef HAS_PROGREP
if (!comfile)
SetAnalyse("Source Deck", 0);
#endif
/* read in the deck from a file */
char *dir_name = ngdirname(filename ? filename : ".");
startTime = seconds();
/* inp_source() called with fp: load from file, */
/* called with *fp == NULL and intfile: we want to load circuit from circarray */
if (fp || intfile) {
deck = inp_readall(fp, dir_name, comfile, intfile, &expr_w_temper);
/* files starting with *ng_script are user supplied command files */
if (deck && ciprefix("*ng_script", deck->line))
comfile = TRUE;
/* save a copy of the deck for later reloading with 'mc_source' */
if (deck && !comfile) {
/* stored to new circuit ci_mcdeck in fcn */
mc_deck = inp_deckcopy_oc(deck);
}
}
/* called with *fp == NULL and not intfile: we want to reload circuit from mc_deck */
else {
/* re-load deck due to command 'reset' via function inp_source_recent() */
if (mc_reload && mc_deck) {
deck = inp_deckcopy(mc_deck);
expr_w_temper = TRUE;
mc_reload = FALSE;
fprintf(stdout, "Reset re-loads circuit %s\n", mc_deck->line);
}
/* re-load input deck from the current circuit structure */
else if (ft_curckt && ft_curckt->ci_mcdeck) {
deck = inp_deckcopy(ft_curckt->ci_mcdeck);
expr_w_temper = TRUE;
}
/* re-load input deck from the recent circuit structure with mc_source */
else if (!ft_curckt && mc_deck) {
deck = inp_deckcopy(mc_deck);
expr_w_temper = TRUE;
}
/* no circuit available, should not happen */
else {
fprintf(stderr, "Error: No circuit loaded, cannot copy internally using mc_source or reset\n");
controlled_exit(1);
}
/* print out the re-loaded deck into debug-out-mc.txt */
if (ft_ngdebug) {
/*debug: print into file*/
FILE *fdo = fopen("debug-out-mc.txt", "w");
if (fdo) {
struct card *tc = NULL;
fprintf(fdo, "****************** complete mc deck ***************\n\n");
/* now completely */
for (tc = deck; tc; tc = tc->nextcard)
fprintf(fdo, "%6d %6d %s\n", tc->linenum_orig, tc->linenum, tc->line);
fclose(fdo);
}
else
fprintf(stderr, "Warning: Cannot open file debug-out-mc.txt for saving debug info\n");
}
}
endTime = seconds();
/* store input directory to a variable */
if (fp) {
cp_vset("inputdir", CP_STRING, dir_name);
}
tfree(dir_name);
/* if nothing came back from inp_readall, e.g. after calling ngspice without parameters,
just close fp and return to caller */
if (!deck) {
if (!intfile && fp)
fclose(fp);
return 0;
}
/* files starting with *ng_script are user supplied command files */
if (ciprefix("*ng_script", deck->line))
comfile = TRUE;
if (!comfile) {
/* Extract the .option lines from the deck into 'options',
and remove them from the deck. Exceptions are .option with params. */
options = inp_getopts(deck);
/* Check for .option seed=[val|random] and set the random number generator.
Check for .option cshunt=val and set a global variable cshunt_value */
eval_opt(options);
/* copy a deck before subckt substitution. */
realdeck = inp_deckcopy(deck);
/* Save the title before INPgetTitle gets it. */
tt = copy(deck->line);
if (!deck->nextcard) {
fprintf(cp_err, "Warning: no lines in input\n");
}
}
if (fp && !intfile) {
fclose(fp);
}
/* Now save the IO context and start a new control set. After we
are done with the source we'll put the old file descriptors
back. I guess we could use a FILE stack, but since this
routine is recursive anyway. */
lastin = cp_curin;
lastout = cp_curout;
lasterr = cp_curerr;
cp_curin = cp_in;
cp_curout = cp_out;
cp_curerr = cp_err;
cp_pushcontrol();
/* We should now go through the deck and execute front-end
* commands and remove them. Front-end commands are enclosed by
* the cards .control and .endc, unless comfile is TRUE, in which
* case every line must be a front-end command. There are too
* many problems with matching the first word on the line. */
ld = deck;
if (comfile) {
/* Process each command, except 'option' which is assembled
in a list and ingnored here */
for (dd = deck; dd; dd = ld) {
ld = dd->nextcard;
if ((dd->line[0] == '*') && (dd->line[1] != '#'))
continue;
if (!ciprefix(".control", dd->line) && !ciprefix(".endc", dd->line)) {
if (dd->line[0] == '*')
cp_evloop(dd->line + 2);
/* option line stored but not processed */
else if (ciprefix("option", dd->line))
com_options = inp_getoptsc(dd->line, com_options);
else
cp_evloop(dd->line);
}
}
/* free the control deck */
line_free(deck, TRUE);
/* set to NULL to allow generation of a new dbs */
/* do this here and in the 'else' branch of 'if (comfile)' */
dbs = NULL;
ft_dotsaves();
} /* end if (comfile) */
else { /* must be regular deck . . . . */
/* loop through deck and handle control cards */
for (dd = deck->nextcard; dd; dd = ld->nextcard) {
/* Ignore comment lines, but not lines begining with '*#',
but remove them, if they are in a .control ... .endc section */
s = skip_ws(dd->line);
if ((*s == '*') && ((s != dd->line) || (s[1] != '#'))) {
if (commands) {
/* Remove comment lines in control sections, so they don't
* get considered as circuits. */
ld->nextcard = dd->nextcard;
line_free(dd, FALSE);
continue;
}
ld = dd;
continue;
}
/* Put the first token from line into s */
strncpy(name, dd->line, BSIZE_SP);
s = skip_ws(name);
t = skip_non_ws(s);
*t = '\0';
if (ciprefix(".control", dd->line)) {
ld->nextcard = dd->nextcard;
line_free(dd, FALSE); /* SJB - free this line's memory */
if (commands)
fprintf(cp_err, "Warning: redundant .control card\n");
else
commands = TRUE;
} else if (ciprefix(".endc", dd->line)) {
ld->nextcard = dd->nextcard;
line_free(dd, FALSE); /* SJB - free this line's memory */
if (commands)
commands = FALSE;
else
fprintf(cp_err, "Warning: misplaced .endc card\n");
} else if (commands || prefix("*#", dd->line)) {
/* assemble all commands starting with pre_ after stripping
* pre_, to be executed before circuit parsing */
if (ciprefix("pre_", dd->line)) {
s = copy(dd->line + 4);
pre_controls = wl_cons(s, pre_controls);
}
/* assemble all other commands to be executed after circuit
* parsing */
else {
/* special control lines outside of .control section*/
if (prefix("*#", dd->line)) {
s = copy(dd->line + 2);
/* all commands from within .control section */
} else {
s = dd->line;
dd->line = NULL; /* SJB - prevent line_free() freeing the string (now pointed at by wl->wl_word) */
}
controls = wl_cons(s, controls);
}
ld->nextcard = dd->nextcard;
line_free(dd, FALSE);
} else if (!*dd->line) {
/* So blank lines in com files don't get considered as circuits. */
ld->nextcard = dd->nextcard;
line_free(dd, FALSE);
} else {
/* lines .width, .four, .plot, .print, .save added to wl_first, removed from deck */
/* lines .op, .meas, .tf added to wl_first */
inp_casefix(s); /* s: first token from line */
/* Do not eliminate " around netnames, to allow '/' or '-' in netnames */
if (!eq(s, ".plot") && !eq(s, ".print"))
inp_casefix(dd->line);
if (eq(s, ".width") ||
ciprefix(".four", s) ||
eq(s, ".plot") ||
eq(s, ".print") ||
eq(s, ".save") ||
eq(s, ".op") ||
ciprefix(".meas", s) ||
eq(s, ".tf")) {
wl_append_word(&wl_first, &end, copy(dd->line));
if (!eq(s, ".op") && !eq(s, ".tf") && !ciprefix(".meas", s)) {
ld->nextcard = dd->nextcard;
line_free(dd, FALSE);
} else {
ld = dd;
}
} else {
ld = dd;
}
}
} /* end for (dd = deck->nextcard . . . . */
/* Now that the deck is loaded, do the pre commands, if there are any,
before the circuit structure is set up */
if (pre_controls) {
pre_controls = wl_reverse(pre_controls);
for (wl = pre_controls; wl; wl = wl->wl_next)
cp_evloop(wl->wl_word);
wl_free(pre_controls);
}
/* We are done handling the control stuff. Now process remainder of deck.
Go on if there is something left after the controls.*/
if (deck->nextcard) {
fprintf(cp_out, "\nCircuit: %s\n\n", tt);
#ifdef HAS_PROGREP
SetAnalyse("Prepare Deck", 0);
#endif
endTime = seconds();
loadTime = endTime - startTime;
startTime = endTime;
/*This is for the globel param setting only */
/* replace agauss(x,y,z) in each b-line by suitable value, one for all */
bool statlocal = cp_getvar("statlocal", CP_BOOL, NULL, 0);
if (!statlocal) {
static char *statfcn[] = {"agauss", "gauss", "aunif", "unif", "limit"};
int ii;
for (ii = 0; ii < 5; ii++)
eval_agauss(deck, statfcn[ii]);
}
/* If we have large PDK deck, search for scale option and set
the variable 'scale'*/
if (newcompat.hs || newcompat.spe) {
struct card* scan;
double dscale = 1;
/* from options in a script */
for (scan = com_options; scan; scan = scan->nextcard) {
char* tmpscale = strstr(scan->line, "scale=");
if (tmpscale) {
int err;
tmpscale = tmpscale + 6;
dscale = INPevaluate(&tmpscale, &err, 1);
if (err == 0) {
cp_vset("scale", CP_REAL, &dscale);
printf("option SCALE: Scale is set to %g for instance and model parameters\n", dscale);
}
else
fprintf(stderr, "\nError: Could not set 'scale' variable\n");
}
tmpscale = strstr(scan->line, "scalm=");
if (tmpscale) {
int err;
tmpscale = tmpscale + 6;
dscale = INPevaluate(&tmpscale, &err, 1);
if (err == 0) {
cp_vset("scalm", CP_REAL, &dscale);
fprintf(stderr, "Warning: option SCALM is not supported.\n");
}
else
fprintf(stderr, "\nError: Could not set 'scalm' variable\n");
}
}
/* from .options (will override the previous settings) */
for (scan = options; scan; scan = scan->nextcard) {
char* tmpscale = strstr(scan->line, "scale=");
if (tmpscale) {
int err;
tmpscale = tmpscale + 6;
dscale = INPevaluate(&tmpscale, &err, 1);
if (err == 0) {
cp_vset("scale", CP_REAL, &dscale);
printf("option SCALE: Scale is set to %g for instance and model parameters\n", dscale);
}
else
fprintf(stderr, "\nError: Could not set 'scale' variable\n");
}
tmpscale = strstr(scan->line, "scalm=");
if (tmpscale) {
int err;
tmpscale = tmpscale + 6;
dscale = INPevaluate(&tmpscale, &err, 1);
if (err == 0) {
cp_vset("scalm", CP_REAL, &dscale);
fprintf(stderr, "Warning: option SCALM is not supported\n");
}
else
fprintf(stderr, "\nError: Could not set 'scalm' variable\n");
}
}
}
/* Now expand subcircuit macros and substitute numparams.*/
if (!cp_getvar("nosubckt", CP_BOOL, NULL, 0))
if ((deck->nextcard = inp_subcktexpand(deck->nextcard)) == NULL) {
line_free(realdeck, TRUE);
line_free(deck->actualLine, TRUE);
tfree(tt);
return 1;
}
/* Now handle translation of spice2c6 POLYs. */
#ifdef XSPICE
/* Translate all SPICE 2G6 polynomial type sources */
deck->nextcard = ENHtranslate_poly(deck->nextcard);
#endif
line_free(deck->actualLine, FALSE);
deck->actualLine = realdeck;
/* print out the expanded deck into debug-out2.txt */
if (ft_ngdebug) {
/*debug: print into file*/
FILE *fdo = fopen("debug-out2.txt", "w");
if (fdo) {
struct card *tc = NULL;
fprintf(fdo, "**************** uncommented deck **************\n\n");
/* always print first line */
fprintf(fdo, "%6d %6d %s\n", deck->linenum_orig, deck->linenum, deck->line);
/* here without out-commented lines */
for (tc = deck->nextcard; tc; tc = tc->nextcard) {
if (*(tc->line) == '*')
continue;
fprintf(fdo, "%6d %6d %s\n", tc->linenum_orig, tc->linenum, tc->line);
}
fprintf(fdo, "\n****************** complete deck ***************\n\n");
/* now completely */
for (tc = deck; tc; tc = tc->nextcard)
fprintf(fdo, "%6d %6d %s\n", tc->linenum_orig, tc->linenum, tc->line);
fclose(fdo);
}
else
fprintf(stderr, "Warning: Cannot open file debug-out2.txt for saving debug info\n");
}
for (dd = deck; dd; dd = dd->nextcard) {
/* get csparams and create vectors, being
available in .control section, in plot 'const' */
if (ciprefix(".csparam", dd->line)) {
wordlist *wlist = NULL;
char *cstoken[3];
int i;
dd->line[0] = '*';
s = skip_ws(dd->line + 8);
cstoken[0] = gettok_char(&s, '=', FALSE, FALSE);
cstoken[1] = gettok_char(&s, '=', TRUE, FALSE);
cstoken[2] = gettok(&s);
for (i = 3; --i >= 0; ) {
wlist = wl_cons(cstoken[i], wlist);
}
com_let(wlist);
wl_free(wlist);
}
}
/* handle .if ... .elseif ... .else ... .endif statements. */
dotifeval(deck);
/* merge the two option line structs
com_options (comfile == TRUE, filled in from spinit, .spiceinit, and *ng_sript), and
options (comfile == FALSE, filled in from circuit with .OPTIONS)
into options, thus keeping com_options,
options is loaded into circuit and freed when circuit is removed */
options = line_reverse(line_nconc(options, inp_deckcopy(com_options)));
/* List of all expressions found in instance and .model lines */
struct pt_temper *devtlist = NULL;
struct pt_temper *modtlist = NULL;
/* prepare parse trees from 'temper' expressions */
if (expr_w_temper)
inp_parse_temper(deck, &modtlist, &devtlist);
/* replace agauss(x,y,z) in each b-line by suitable value */
/* FIXME: This is for the local param setting (not yet implemented in
inp_fix_agauss_in_param() for model parameters according to HSPICE manual)*/
if (statlocal) {
static char *statfcn[] = {"agauss", "gauss", "aunif", "unif", "limit"};
int ii;
for (ii = 0; ii < 5; ii++)
eval_agauss(deck, statfcn[ii]);
}
/* If user wants all currents saved (.options savecurrents), add .save
to wl_first with all terminal currents available on selected devices */
wl_first = inp_savecurrents(deck, options, wl_first, controls);
/* Circuit is flat, all numbers expanded.
So again try to remove unused MOS models.
All binning models are still here when w or l have been
determined by an expression. */
if (newcompat.hs || newcompat.spe)
rem_unused_mos_models(deck->nextcard);
/* now load deck into ft_curckt -- the current circuit. */
if(inp_dodeck(deck, tt, wl_first, FALSE, options, filename) != 0)
return 1;
if (ft_curckt) {
ft_curckt->devtlist = devtlist;
ft_curckt->modtlist = modtlist;
}
/* inp_dodeck did take ownership */
tt = NULL;
options = NULL;
} /* if (deck->nextcard) */
/* look for and set temperature; also store param and .meas statements in circuit struct */
if (ft_curckt) {
ft_curckt->ci_param = NULL;
ft_curckt->ci_meas = NULL;
}
for (dd = deck; dd; dd = dd->nextcard) {
/* all parameter lines should be sequentially ordered and placed at
beginning of deck */
if (ciprefix(".para", dd->line)) {
ft_curckt->ci_param = dd;
/* find end of .param statements */
while (ciprefix(".para", dd->line)) {
prev_param = dd;
dd = dd->nextcard;
if (dd == NULL)
break; // no line after .param line
}
prev_card->nextcard = dd;
prev_param->nextcard = NULL;
if (dd == NULL) {
fprintf(cp_err, "Warning: Missing .end card!\n");
break; // no line after .param line
}
}
/* remove the .measure cards from the deckand store them in ft_curckt->ci_meas */
if (ciprefix(".meas", dd->line)) {
if (cp_getvar("autostop", CP_BOOL, NULL, 0)) {
if (strstr(dd->line, " max ") ||
strstr(dd->line, " min ") ||
strstr(dd->line, " avg ") ||
strstr(dd->line, " rms ") ||
strstr(dd->line, " integ "))
{
printf("Warning: .OPTION AUTOSTOP will not be effective because one of 'max|min|avg|rms|integ' is used in .meas\n");
printf(" AUTOSTOP being disabled...\n");
cp_remvar("autostop");
}
}
if (curr_meas == NULL) {
curr_meas = ft_curckt->ci_meas = dd;
} else {
curr_meas->nextcard = dd;
curr_meas = dd;
}
prev_card->nextcard = dd->nextcard;
curr_meas->nextcard = NULL;
dd = prev_card;
}
/* get temp from deck */
if (ciprefix(".temp", dd->line)) {
s = skip_ws(dd->line + 5);
if (temperature) {
txfree(temperature);
}
temperature = copy(s);
*(dd->line) = '*';
}
prev_card = dd;
} //end of for-loop
/* set temperature, if defined, to new value.
cp_vset will set the variable "temp" and also set CKTtemp,
so we can do it only here because the circuit has to be already there */
if (temperature) {
temperature_value = atof(temperature);
cp_vset("temp", CP_REAL, &temperature_value);
txfree(temperature);
}
#ifdef TRACE
/* SDB debug statement */
printf("In inp_spsource, done with dodeck.\n");
#endif
/* print out the expanded deck into debug-out3.txt */
if (ft_ngdebug) {
/*debug: print into file*/
FILE *fdo = fopen("debug-out3.txt", "w");
if (fdo) {
struct card *tc = NULL;
fprintf(fdo, "**************** uncommented deck **************\n\n");
/* always print first line */
fprintf(fdo, "%6d %6d %s\n", deck->linenum_orig, deck->linenum, deck->line);
/* here without out-commented lines */
for (tc = deck->nextcard; tc; tc = tc->nextcard) {
if (*(tc->line) == '*')
continue;
fprintf(fdo, "%6d %6d %s\n", tc->linenum_orig, tc->linenum, tc->line);
}
fprintf(fdo, "\n****************** complete deck ***************\n\n");
/* now completely */
for (tc = deck; tc; tc = tc->nextcard)
fprintf(fdo, "%6d %6d %s\n", tc->linenum_orig, tc->linenum, tc->line);
fclose(fdo);
}
else
fprintf(stderr, "Warning: Cannot open file debug-out3.txt for saving debug info\n");
}
/* Remove comment lines
if (newcompat.hs || newcompat.spe) {
struct card *prev, *fcard, *tmpdeck;
prev = deck;
tmpdeck = deck->nextcard;
for (fcard = tmpdeck; fcard; fcard = fcard->nextcard) {
if (*(prev->nextcard->line) == '*') {
struct card* tmpcard = fcard->nextcard;
line_free_x(prev->nextcard, FALSE);
fcard = prev->nextcard = tmpcard;
}
prev = fcard;
}
}*/
/* Now the circuit is defined, so generate the parse trees */
inp_parse_temper_trees(ft_curckt);
/* Get the actual data for model and device instance parameters */
inp_evaluate_temper(ft_curckt);
/* linked list dbs is used to store the "save" or .save data (defined in breakp2.c),
(When controls are executed later on, also stores TRACE, IPLOT, and STOP data) */
/* set to NULL to allow generation of a new dbs */
dbs = NULL;
/* .save data stored in dbs.
Do this here before controls are run: .save is thus recognized even if
.control is used */
ft_dotsaves();
/* Now that the deck is loaded, do the commands, if there are any */
controls = wl_reverse(controls);
/* statistics for preparing the deck */
endTime = seconds();
if (ft_curckt) {
ft_curckt->FTEstats->FTESTATnetLoadTime = loadTime;
ft_curckt->FTEstats->FTESTATnetPrepTime = seconds() - startTime;
}
/* in shared ngspice controls a execute in the primary thread, typically
before the background thread has finished. This leads to premature execution
of commands. Thus this is delegated to a function using a third thread, that
only starts when the background thread has finished (sharedspice.c).*/
#ifdef SHARED_MODULE
for (wl = controls; wl; wl = wl->wl_next)
if (cp_getvar("controlswait", CP_BOOL, NULL, 0)) {
exec_controls(wl_copy(wl));
break;
}
else
cp_evloop(wl->wl_word);
#else
for (wl = controls; wl; wl = wl->wl_next)
cp_evloop(wl->wl_word);
#endif
wl_free(controls);
}
/* Now reset everything. Pop the control stack, and fix up the IO
* as it was before the source. */
cp_popcontrol();
cp_curin = lastin;
cp_curout = lastout;
cp_curerr = lasterr;
tfree(tt);
return 0;
}
/* This routine is cut in half here because com_rset has to do what
* follows also. End is the list of commands we execute when the job
* is finished: we only bother with this if we might be running in
* batch mode, since it isn't much use otherwise. */
/*------------------------------------------------------------------
* It appears that inp_dodeck adds the circuit described by *deck
* to the current circuit (ft_curckt).
*-----------------------------------------------------------------*/
int
inp_dodeck(
struct card *deck, /*in: the spice deck */
char *tt, /*in: the title of the deck */
wordlist *end, /*in: all lines with .width, .plot, .print, .save, .op, .meas, .tf */
bool reuse, /*in: TRUE if called from runcoms2.c com_rset,
FALSE if called from inp_spsource() */
struct card *options, /*in: all .option lines from deck */
char *filename /*in: input file of deck */
)
{
struct circ *ct;
struct card *dd;
CKTcircuit *ckt;
INPtables *tab = NULL;
struct variable *eev = NULL;
bool noparse;
int print_listing;
bool have_err = FALSE;
int warn; /* whether SOA check should be performed */
int maxwarns = 0; /* specifies the maximum number of SOA warnings */
double startTime;
/* First throw away any old error messages there might be and fix
the case of the lines. */
for (dd = deck; dd; dd = dd->nextcard)
if (dd->error) {
tfree(dd->error);
dd->error = NULL;
}
if (reuse) {
/* re-use existing circuit structure */
ct = ft_curckt;
} else {
if (ft_curckt) {
ft_curckt->ci_devices = cp_kwswitch(CT_DEVNAMES, NULL);
ft_curckt->ci_nodes = cp_kwswitch(CT_NODENAMES, NULL);
}
/* create new circuit structure */
ft_curckt = ct = TMALLOC(struct circ, 1);
/*PN FTESTATS*/
ft_curckt->FTEstats = TMALLOC(FTESTATistics, 1);
}
noparse = cp_getvar("noparse", CP_BOOL, NULL, 0);
/* Read the options, create variables and store them
in ftcurckt->ci_vars */
if (!noparse) {
char* s;
bool ii;
wordlist* wl;
struct card* opt_beg = options;
for (; options; options = options->nextcard) {
s = skip_non_ws(options->line);
ii = cp_interactive;
cp_interactive = FALSE;
wl = cp_lexer(s);
cp_interactive = ii;
if (!wl || !wl->wl_word || !*wl->wl_word)
continue;
if (eev)
eev->va_next = cp_setparse(wl);
else
ct->ci_vars = eev = cp_setparse(wl);
wl_free(wl);
while (eev && (eev->va_next))
eev = eev->va_next;
}
for (eev = ct->ci_vars; eev; eev = eev->va_next) {
switch (eev->va_type) {
case CP_BOOL:
break;
case CP_NUM:
break;
case CP_REAL:
break;
case CP_STRING:
break;
default: {
fprintf(stderr, "ERROR: wrong format in option %s!\n", eev->va_name);
fprintf(stderr, " Aborting...\n");
controlled_exit(EXIT_FAILURE);
}
} /* switch . . . */
}
options = opt_beg; // back to the beginning
} /* if (!noparse) . . . */
/*----------------------------------------------------
* Now assuming that we wanna parse this deck, we call
* if_inpdeck which takes the deck and returns a
* a pointer to the circuit ckt.
*---------------------------------------------------*/
if (!noparse) {
startTime = seconds();
ckt = if_inpdeck(deck, &tab);
ft_curckt->FTEstats->FTESTATnetParseTime = seconds() - startTime;
} else {
ckt = NULL;
}
/* set ckt->CKTisLinear=1 if circuit only contains R, L, C */
if (ckt)
cktislinear(ckt, deck);
/* set some output terminal data */
out_init();
/* if_inpdeck() may return NULL upon error */
if (ckt) {
if (cp_getvar("warn", CP_NUM, &warn, 0))
ckt->CKTsoaCheck = warn;
else
ckt->CKTsoaCheck = 0;
if (cp_getvar("maxwarns", CP_NUM, &maxwarns, 0))
ckt->CKTsoaMaxWarns = maxwarns;
else
ckt->CKTsoaMaxWarns = 5;
}
ft_curckt->FTEstats->FTESTATdeckNumLines = 0;
/*----------------------------------------------------
Now run through the deck and look to see if there are
errors on any line (message contained in ->error).
Error messages have been generated either by writing
directly to ->error from a struct card or to
->error from a struct card , or by using one of the
macros as defined in inpmacs.h. Functions INPerror(),
INPerrCat(), and SPerror() are invoked.
*---------------------------------------------------*/
for (dd = deck; dd; dd = dd->nextcard) {
ft_curckt->FTEstats->FTESTATdeckNumLines += 1;
#ifdef TRACE
/* SDB debug statement */
printf("In inp_dodeck, looking for errors and examining line %s . . . \n", dd->line);
#endif
if (dd->error) {
char *p, *q;
#ifdef XSPICE
/* add setting of ipc syntax error flag */
g_ipc.syntax_error = IPC_TRUE;
#endif
p = dd->error;
do {
q = strchr(p, '\n');
if (q)
*q = '\0';
if (p == dd->error) {
if (strstr(dd->line, ".model"))
out_printf("Warning: Model issue on line %d :\n %.*s ...\n%s\n",
dd->linenum_orig, 72, dd->line, dd->error);
else {
out_printf("Error on line %d :\n %s\n%s\n",
dd->linenum_orig, dd->line, dd->error);
have_err = TRUE;
return 1;
}
if (ft_stricterror)
controlled_exit(EXIT_BAD);
} else {
out_printf("%s\n", p);
}
if (q)
*q++ = '\n';
p = q;
} while (p && *p);
} /* end if (dd->error) */
} /* for (dd = deck; dd; dd = dd->nextcard) */
/* Stop here and exit if error occurred in batch mode */
if (have_err && ft_batchmode) {
fprintf(stderr, "\nngspice stopped due to error, no simulation run!\n");
controlled_exit(EXIT_BAD);
}
/* Only print out netlist if brief is FALSE */
if (!cp_getvar("brief", CP_BOOL, NULL, 0)) {
/* output deck */
out_printf("\nProcessed Netlist\n");
out_printf("=================\n");
print_listing = 1;
for (dd = deck; dd; dd = dd->nextcard) {
if (ciprefix(".prot", dd->line))
print_listing = 0;
if (print_listing == 1)
out_printf("%s\n", dd->line);
if (ciprefix(".unprot", dd->line))
print_listing = 1;
}
out_printf("\n");
}
/* Add this circuit to the circuit list. If reuse is TRUE
(command 'reset'), then use the existing ft_curckt structure. */
if (!reuse) {
/* Be sure that ci_devices and ci_nodes are valid */
ft_curckt->ci_devices = cp_kwswitch(CT_DEVNAMES, NULL);
cp_kwswitch(CT_DEVNAMES, ft_curckt->ci_devices);
ft_curckt->ci_nodes = cp_kwswitch(CT_NODENAMES, NULL);
cp_kwswitch(CT_NODENAMES, ft_curckt->ci_nodes);
ft_newcirc(ct);
/* Assign current circuit */
ft_curckt = ct;
}
ct->ci_name = tt;
ct->ci_deck = deck;
ct->ci_mcdeck = mc_deck;
ct->ci_options = options;
if (deck && deck->actualLine)
ct->ci_origdeck = deck->actualLine;
else
ct->ci_origdeck = ct->ci_deck;
ct->ci_ckt = ckt; /* attach the input ckt to the list of circuits */
ct->ci_symtab = tab;
ct->ci_inprogress = FALSE;
ct->ci_runonce = FALSE;
ct->ci_commands = end;
ct->ci_dicos = nupa_add_dicoslist();
/* prevent false reads in multi-threaded ngshared */
#ifndef SHARED_MODULE
if (reuse)
tfree(ct->ci_filename);
#endif
ct->ci_filename = copy(filename);
if (!noparse) {
/*
* for (; options; options = options->nextcard) {
* s = skip_non_ws(options->line);
* ii = cp_interactive;
* cp_interactive = FALSE;
* wl = cp_lexer(s);
* cp_interactive = ii;
* if (!wl || !wl->wl_word || !*wl->wl_word)
* continue;
* if (eev)
* eev->va_next = cp_setparse(wl);
* else
* ct->ci_vars = eev = cp_setparse(wl);
* while (eev->va_next)
* eev = eev->va_next;
* }
*/
for (eev = ct->ci_vars; eev; eev = eev->va_next) {
bool one = TRUE; /* FIXME, actually eev->va_bool should be TRUE anyway */
switch (eev->va_type) {
case CP_BOOL:
if_option(ct->ci_ckt, eev->va_name, eev->va_type, &one);
break;
case CP_NUM:
if_option(ct->ci_ckt, eev->va_name, eev->va_type, &eev->va_num);
break;
case CP_REAL:
if_option(ct->ci_ckt, eev->va_name, eev->va_type, &eev->va_real);
break;
case CP_STRING:
if_option(ct->ci_ckt, eev->va_name, eev->va_type, eev->va_string);
break;
default: {
fprintf(stderr, "ERROR: enumeration value `CP_LIST' not handled in inp_dodeck\nAborting...\n");
controlled_exit(EXIT_FAILURE);
}
} // switch . . .
}
} // if (!noparse) . . .
/* add title of deck to data base */
/* this won't work if the title is the empty string
* cp_addkword() doesn't work for tt === ""
* since CT_CKTNAMES doesn't seem to be used anywhere
* I've disabled this piece.
*/
#if 0
cp_addkword(CT_CKTNAMES, tt);
#endif
return 0;
}
void
com_mc_source(wordlist *wl)
{
NG_IGNORE(wl);
inp_spsource(NULL, FALSE, NULL, FALSE);
}
/* Edit and re-load the current input deck. Note that if these
* commands are used on a non-unix machine, they will leave spice.tmp
* junk files lying around. */
void
com_edit(wordlist *wl)
{
char *filename;
FILE *fp;
bool inter, permfile;
char buf[BSIZE_SP];
if (!cp_getvar("interactive", CP_BOOL, NULL, 0)) {
fprintf(cp_err,
"Warning: `edit' is disabled because 'interactive' has not been set.\n"
" perhaps you want to 'set interactive'\n");
return;
}
inter = cp_interactive;
cp_interactive = FALSE;
if (wl) {
if (!doedit(wl->wl_word)) {
cp_interactive = inter;
return;
}
if ((fp = inp_pathopen(wl->wl_word, "r")) == NULL) {
perror(wl->wl_word);
cp_interactive = inter;
return;
}
inp_spsource(fp, FALSE, wl->wl_word, FALSE);
} else {
/* If there is no circuit yet, then create one */
if (ft_curckt && ft_curckt->ci_filename) {
filename = ft_curckt->ci_filename;
permfile = TRUE;
} else {
filename = smktemp("sp");
permfile = FALSE;
}
if (ft_curckt && !ft_curckt->ci_filename) {
if ((fp = fopen(filename, "w")) == NULL) {
perror(filename);
cp_interactive = inter;
return;
}
inp_list(fp, ft_curckt->ci_deck, ft_curckt->ci_options, LS_DECK);
fprintf(cp_err,
"Warning: editing a temporary file -- "
"circuit not saved\n");
fclose(fp);
} else if (!ft_curckt) {
if ((fp = fopen(filename, "w")) == NULL) {
perror(filename);
cp_interactive = inter;
return;
}
fprintf(fp, "SPICE 3 test deck\n");
fclose(fp);
}
if (!doedit(filename)) {
cp_interactive = inter;
return;
}
if ((fp = fopen(filename, "r")) == NULL) {
perror(filename);
cp_interactive = inter;
return;
}
inp_spsource(fp, FALSE, permfile ? filename : NULL, FALSE);
/* fclose(fp); */
/* MW. inp_spsource already closed fp */
if (ft_curckt && !ft_curckt->ci_filename)
unlink(filename);
}
cp_interactive = inter;
/* note: default is to run circuit after successful edit */
fprintf(cp_out, "run circuit? ");
fflush(cp_out);
if (fgets(buf, BSIZE_SP, stdin) == (char *) NULL || buf[0] != 'n') {
fprintf(cp_out, "running circuit\n");
com_run(NULL);
}
}
/* alter a parameter, either
subckt param: alterparam subcktname pname=vpval
global .param: alterparam pname=pval
Changes params in mc_deck
To become effective, 'mc_source' has to be called after 'alterparam' */
void
com_alterparam(wordlist *wl)
{
struct card *dd;
char *pname, *pval, *tmp, *subcktname = NULL, *linein, *linefree, *s;
bool found = FALSE;
if (!ft_curckt) {
fprintf(stderr, "Warning: No circuit loaded!\n");
fprintf(stderr, " Command 'alterparam' ignored\n");
return;
}
if (!ft_curckt->ci_mcdeck) {
fprintf(cp_err, "Error: No internal deck available\n");
fprintf(stderr, " Command 'alterparam' ignored\n");
return;
}
linefree = wl_flatten(wl);
linein = skip_ws(linefree);
s = tmp = gettok_char(&linein, '=', FALSE, FALSE);
if (!s) {
fprintf(cp_err, "\nError: Wrong format in line 'alterparam %s'\n command 'alterparam' skipped\n", linefree);
tfree(linefree);
return;
}
linein++; /* skip the '=' */
pval = gettok(&linein);
subcktname = gettok(&tmp);
if (!pval || !subcktname) {
fprintf(cp_err, "\nError: Wrong format in line 'alterparam %s'\n command 'alterparam' skipped\n", linefree);
tfree(pval);
tfree(subcktname);
tfree(linefree);
return;
}
pname = gettok(&tmp);
if (!pname) {
pname = subcktname;
subcktname = NULL;
}
tfree(linefree);
tfree(s);
for (dd = ft_curckt->ci_mcdeck->nextcard; dd; dd = dd->nextcard) {
char *curr_line = dd->line;
/* alterparam subcktname pname=vpval
Parameters from within subcircuit are no longer .param lines, but have been added to
the .subckt line as pname=paval and to the x line as pval. pval in the x line takes
precedence when subciruit is called, so has to be replaced here.
Find subcircuit with subcktname.
After params: Count the number of parameters (notok) until parameter pname is found.
When found, search for x-line with subcktname.
Replace parameter value number notok by pval.
*/
if (subcktname) {
/* find subcircuit */
if (ciprefix(".subckt", curr_line)) {
curr_line = nexttok(curr_line); /* skip .subckt */
char *sname = gettok(&curr_line);
if (eq(sname, subcktname)) {
tfree(sname);
curr_line = strstr(curr_line, "params:");
curr_line = skip_non_ws(curr_line); /* skip params: */
/* string to search for */
char *pname_eq = tprintf("%s=", pname);
int notok = 0;
while (*curr_line) {
char *token = gettok(&curr_line);
if (ciprefix(pname_eq, token)) {
tfree(token);
found = TRUE;
break;
}
notok++;
tfree(token);
}
tfree(pname_eq);
if (found) {
/* find x line with same subcircuit name */
struct card *xx;
char *bsubb = tprintf(" %s ", subcktname);
for (xx = ft_curckt->ci_mcdeck->nextcard; xx; xx = xx->nextcard) {
char *xline = xx->line;
if (*xline == 'x') {
xline = strstr(xline, bsubb);
if (xline) {
xline = nexttok(xline); /* skip subcktname */
int ii;
for (ii = 0; ii < notok; ii++)
xline = nexttok(xline); /* skip parameter values */
char *beg = copy_substring(xx->line, xline);
xline = nexttok(xline); /* skip parameter value to be replaced */
char *newline = tprintf("%s %s %s", beg, pval, xline);
tfree(xx->line);
xx->line = newline;
tfree(beg);
}
else
continue;
}
}
tfree(bsubb);
}
}
else {
tfree(sname);
continue;
}
}
} /* subcktname */
/* alterparam pname=vpval */
else {
if (ciprefix(".para", curr_line)) {
curr_line = nexttok(curr_line); /* skip .param */
char *name = gettok_char(&curr_line, '=', FALSE, FALSE);
if (eq(name, pname)) {
curr_line = dd->line;
char *start = gettok_char(&curr_line, '=', TRUE, FALSE);
tfree(dd->line);
dd->line = tprintf("%s%s", start, pval);
found = TRUE;
tfree(start);
}
tfree(name);
}
}
}
if (!found)
fprintf(cp_err, "\nError: parameter '%s' not found,\n command 'alterparam' skipped\n", pname);
tfree(pval);
tfree(pname);
tfree(subcktname);
}
static bool
doedit(char *filename)
{
char buf[BSIZE_SP], buf2[BSIZE_SP], *editor;
if (cp_getvar("editor", CP_STRING, buf2, sizeof(buf2))) {
editor = buf2;
} else {
if ((editor = getenv("EDITOR")) == NULL) {
if (Def_Editor && *Def_Editor)
editor = Def_Editor;
else
editor = "/usr/bin/vi";
}
}
int len = snprintf(buf, BSIZE_SP - 1, "%s %s", editor, filename);
if (len > BSIZE_SP - 1)
fprintf(stderr, "Error: the filename is probably tuncated\n");
return (system(buf) ? FALSE : TRUE);
}
void
com_source(wordlist *wl)
{
FILE *fp, *tp;
char buf[BSIZE_SP];
bool inter;
char *tempfile = NULL, *firstfile;
wordlist *owl = wl;
size_t n;
if (wl == NULL)
return;
inter = cp_interactive;
cp_interactive = FALSE;
firstfile = wl->wl_word;
if (wl->wl_next) {
/* There are several files -- put them into a temp file */
tempfile = smktemp("sp");
if ((fp = inp_pathopen(tempfile, "w+")) == NULL) {
perror(tempfile);
fprintf(cp_err, " Simulation interrupted due to error!\n\n");
cp_interactive = TRUE;
/* If we cannot open the temporary file, stop all further command execution */
#ifdef SHARED_MODULE
controlled_exit(1);
#else
cp_evloop(NULL);
#endif
}
while (wl) {
if ((tp = inp_pathopen(wl->wl_word, "r")) == NULL) {
fprintf(cp_err, "Command 'source' failed:\n");
perror(wl->wl_word);
fprintf(cp_err, " Simulation interrupted due to error!\n\n");
fclose(fp);
cp_interactive = TRUE;
unlink(tempfile);
/* If we cannot source the file, stop all further command execution */
#ifdef SHARED_MODULE
controlled_exit(1);
#else
cp_evloop(NULL);
#endif
}
while ((n = fread(buf, 1, BSIZE_SP, tp)) > 0)
fwrite(buf, 1, n, fp);
fclose(tp);
wl = wl->wl_next;
}
fseek(fp, 0L, SEEK_SET);
} else {
fp = inp_pathopen(wl->wl_word, "r");
}
if (fp == NULL) {
fprintf(cp_err, "Command 'source' failed:\n");
perror(wl->wl_word);
fprintf(cp_err, " Simulation interrupted due to error!\n\n");
cp_interactive = TRUE;
/* If we cannot source the file, stop all further command execution */
#ifdef SHARED_MODULE
controlled_exit(1);
#else
cp_evloop(NULL);
#endif
return;
}
/* Don't print the title if this is a spice initialisation file. */
if (ft_nutmeg || substring(INITSTR, owl->wl_word) || substring(ALT_INITSTR, owl->wl_word))
inp_spsource(fp, TRUE, tempfile ? NULL : wl->wl_word, FALSE);
else {
#ifdef HAS_WINGUI
/* set the source window */
SetSource(wl->wl_word);
#endif
/* Save path name for use in XSPICE fopen_with_path() */
if (Infile_Path)
tfree(Infile_Path);
Infile_Path = ngdirname(firstfile);
if (inp_spsource(fp, FALSE, tempfile ? NULL : wl->wl_word, FALSE) != 0) {
fprintf(stderr, " Simulation interrupted due to error!\n\n");
}
}
cp_interactive = inter;
if (tempfile)
unlink(tempfile);
}
void inp_source(const char *file)
{
/* This wordlist is special in that nothing in it should be freed --
* the file name word is "borrowed" from the argument to file and
* the wordlist is allocated on the stack. */
static struct wordlist wl = { NULL, NULL, NULL };
wl.wl_word = (char *) file;
com_source(&wl);
}
/* check the input deck (after inpcom and numparam extensions)
for linear elements. If only linear elements are found,
ckt->CKTisLinear is set to 1. Return immediately if a first
non-linear element is found. */
static void cktislinear(CKTcircuit *ckt, struct card *deck)
{
struct card *dd;
char firstchar;
if (deck->nextcard)
for (dd = deck->nextcard; dd; dd = dd->nextcard) {
firstchar = *dd->line;
switch (firstchar) {
case 'r':
case 'l':
case 'c':
case 'i':
case 'v':
case '*':
case '.':
case 'e':
case 'g':
case 'f':
case 'h':
case 'k':
continue;
break;
default:
ckt->CKTisLinear = 0;
return;
}
}
ckt->CKTisLinear = 1;
}
/* global array for assembling circuit lines entered by fcn circbyline
* or receiving array from external caller. Array is created whenever
* a new deck is started. Last line of the array has to get the string ".end" */
char **circarray;
void create_circbyline(char *line, bool reset, bool lastline)
{
static unsigned int linec = 0;
static unsigned int n_elem_alloc = 0;
if (reset) {
linec = 0;
n_elem_alloc = 0;
tfree(circarray);
}
/* Ensure up to 2 cards can be added */
if (n_elem_alloc < linec + 2) {
n_elem_alloc = n_elem_alloc == 0 ? 256 : 2 * n_elem_alloc;
circarray = TREALLOC(char *, circarray, n_elem_alloc);
}
/* Remove any leading whitespace by shifting */
char *p_src = skip_ws(line);
if (p_src != line) {
char *p_dst = line;
char ch_cur;
do {
ch_cur = *p_dst++ = *p_src++;
}
while (ch_cur != '\0');
}
if (ft_ngdebug) {
if (linec == 0)
fprintf(stdout, "**** circbyline: circuit netlist sent to shared ngspice ****\n");
fprintf(stdout, "%d %s\n", linec, line);
}
circarray[linec++] = line; /* add card to deck */
/* If the card added ended the deck, send it for processing and
* free the deck. The card allocations themselves will be freed
* elsewhere */
if (ciprefix(".end", line) && (line[4] == '\0' || isspace_c(line[4]))) {
circarray[linec] = NULL; /* termiante the deck */
inp_spsource((FILE *) NULL, FALSE, NULL, TRUE); /* process */
tfree(circarray); /* set to empty */
linec = 0;
n_elem_alloc = 0;
}
/* If the .end statement is missing */
else if (lastline) {
fprintf(stderr, "Error: .end statement is missing in netlist!\n");
}
} /* end of function create_circbyline */
/* fcn called by command 'circbyline' */
void com_circbyline(wordlist *wl)
{
/* undo the automatic wordline creation.
wl_flatten allocates memory on the heap for each newline.
This memory will be released line by line in inp_source(). */
char *newline = wl_flatten(wl);
create_circbyline(newline, FALSE, FALSE);
}
/* handle .if('expr') ... .elseif('expr') ... .else ... .endif statements.
numparam has evaluated .if('boolean expression') to
.if ( 1.000000000e+000 ) or .elseif ( 0.000000000e+000 ).
Evaluation is done recursively, starting with .IF, ending with .ENDIF*/
static void recifeval(struct card *pdeck)
{
struct card *nd;
int iftrue = 0, elseiftrue = 0, elsetrue = 0, iffound = 0, elseiffound = 0, elsefound = 0;
char *t;
char *s = t = pdeck->line;
/* get parameter to .if */
elsefound = 0;
elseiffound = 0;
iffound = 1;
*t = '*';
s = pdeck->line + 3;
iftrue = atoi(s);
nd = pdeck->nextcard;
while(nd) {
s = nd->line;
if (ciprefix(".if", nd->line))
recifeval(nd);
else if (ciprefix(".elseif", nd->line) && elseiftrue == 0) {
elsefound = 0;
elseiffound = 1;
iffound = 0;
*s = '*';
if (!iftrue) {
s = nd->line + 7;
elseiftrue = atoi(s);
}
}
else if (ciprefix(".else", nd->line)) {
elsefound = 1;
elseiffound = 0;
iffound = 0;
if (!iftrue && !elseiftrue)
elsetrue = 1;
*s = '*';
}
else if (ciprefix(".endif", nd->line)) {
elsefound = elseiffound = iffound = 0;
elsetrue = elseiftrue = iftrue = 0;
*s = '*';
return;
}
else {
if (iffound && !iftrue) {
*s = '*';
}
else if (elseiffound && !elseiftrue) {
*s = '*';
}
else if (elsefound && !elsetrue) {
*s = '*';
}
}
nd = nd->nextcard;
}
}
/* Scan through all lines of the deck */
static void dotifeval(struct card *deck)
{
struct card *dd;
char *dottoken;
char *s, *t;
/* skip the first line (title line) */
for (dd = deck->nextcard; dd; dd = dd->nextcard) {
s = t = dd->line;
if (*s == '*')
continue;
dottoken = gettok(&t);
/* find '.if', the starter of any .if --- .endif clause, and call the recursive evaluation.
recifeval() returns when .endif is found */
if (cieq(dottoken, ".if")) {
recifeval(dd);
}
tfree(dottoken);
}
}
/*
Evaluate expressions containing 'temper' keyword, found in
.model lines or device instance lines.
Activity has four steps:
1) Prepare the expressions to survive numparam expansion
(see function inp_temper_compat() in inpcom.c). A global
variable expr_w_temper is set TRUE if any expression with
'temper' has been found.
2) After numparam insertion and subcircuit expansion,
get the expressions, store them with a place holder for the
pointer to the expression parse tree and a wordlist containing
device/model name, parameter name and a placeholder for the
evaluation result ready to be used by com_alter(mod) functions,
in linked lists modtlist (model) or devtlist (device instance).
(done function inp_parse_temper()).
3) After the circuit structure has been established, generate
the parse trees. We can do it only then because pointers to
ckt->CKTtemp and others are stored in the trees.
(done in function inp_parse_temper_trees()).
4) Evaluation of the parse trees is requested by calling function
inp_evaluate_temper(). The B Source parser is invoked here.
ckt->CKTtemp is used to replace the 'temper' token by the actual
circuit temperature. The evaluation results are added to the
wordlist, com_alter(mod) is called to set the new parameters
to the model parameters or device instance parameters.
*/
static int inp_parse_temper(struct card *card, struct pt_temper **modtlist_p,
struct pt_temper **devtlist_p)
{
int error = 0;
struct pt_temper *modtlist = NULL;
struct pt_temper *devtlist = NULL;
/* skip title line */
card = card->nextcard;
for (; card; card = card->nextcard) {
char *curr_line = card->line;
/* exclude some elements */
if (strchr("*vbiegfh", curr_line[0]))
continue;
/* exclude all dot commands except .model */
if (curr_line[0] == '.' && !prefix(".model", curr_line))
continue;
/* exclude lines not containing 'temper' */
if (!strstr(curr_line, "temper"))
continue;
bool is_model = prefix(".model", curr_line);
/* skip ".model" */
if (is_model)
curr_line = nexttok(curr_line);
/* now start processing of the remaining lines containing 'temper' */
char *name = gettok(&curr_line);
char *t = curr_line;
while ((t = search_identifier(t, "temper", curr_line)) != NULL) {
struct pt_temper *alter = TMALLOC(struct pt_temper, 1);
char *eq_ptr = find_back_assignment(t, curr_line);
if (!eq_ptr) {
t = t + 1;
continue;
}
/* go back over param name */
char *end_param = skip_back_ws(eq_ptr, curr_line);
char *beg_param = eq_ptr;
while (beg_param > curr_line && !isspace_c(beg_param[-1]) && beg_param[-1] != '(')
beg_param--;
/* find end of expression string */
char *beg_expr = skip_ws(eq_ptr + 1);
char *end_expr = find_assignment(beg_expr);
if (end_expr) {
end_expr = skip_back_ws(end_expr, curr_line);
end_expr = skip_back_non_ws(end_expr, curr_line);
} else {
end_expr = strchr(beg_expr, '\0');
}
end_expr = skip_back_ws(end_expr, curr_line);
/* overwrite this parameter assignment with ' '
* the backend will use a default
* later, after evaluation, "alter" the parameter
*/
alter->expression = copy_substring(beg_expr, end_expr);
/* to be filled in by evaluation function */
alter->wlend = wl_cons(NULL, NULL);
/* create wordlist suitable for com_altermod */
alter->wl =
wl_cons(copy(name),
wl_cons(copy_substring(beg_param, end_param),
wl_cons(copy("="),
alter->wlend)));
memset(beg_param, ' ', (size_t) (end_expr - beg_param));
/* fill in the linked parse tree list */
if (is_model) {
alter->next = modtlist;
modtlist = alter;
} else {
alter->next = devtlist;
devtlist = alter;
}
t = end_expr;
}
tfree(name);
}
*modtlist_p = modtlist;
*devtlist_p = devtlist;
return error;
}
static void
inp_parse_temper_trees(struct circ *circ)
{
struct pt_temper *d;
for(d = circ->devtlist; d; d = d->next) {
char *expression = d->expression;
INPgetTree(&expression, &d->pt, circ->ci_ckt, NULL);
}
for(d = circ->modtlist; d; d = d->next) {
char *expression = d->expression;
INPgetTree(&expression, &d->pt, circ->ci_ckt, NULL);
}
}
void
rem_tlist(struct pt_temper *p)
{
while (p) {
struct pt_temper *next_p = p->next;
tfree(p->expression);
wl_free(p->wl);
INPfreeTree((IFparseTree *) p->pt);
tfree(p);
p = next_p;
}
}
void inp_evaluate_temper(struct circ *circ)
{
struct pt_temper *d;
double result;
for(d = circ->devtlist; d; d = d->next) {
IFeval((IFparseTree *) d->pt, 1e-12, &result, NULL, NULL);
if (d->wlend->wl_word)
tfree(d->wlend->wl_word);
d->wlend->wl_word = tprintf("%g", result);
com_alter(d->wl);
}
/* Step through the nodes of the linked list at circ->modtlist */
for(d = circ->modtlist; d; d = d->next) {
char *name = d->wl->wl_word;
INPretrieve(&name, circ->ci_symtab);
/* only evaluate models which have been entered into the
hash table ckt->MODnameHash */
if (ft_sim->findModel (circ->ci_ckt, name) == NULL) {
continue;
}
IFeval((IFparseTree *) d->pt, 1e-12, &result, NULL, NULL);
if (d->wlend->wl_word)
tfree(d->wlend->wl_word);
d->wlend->wl_word = tprintf("%g", result);
com_altermod(d->wl);
}
} /* end of funtion inp_evaluate_temper */
/*
* Enable current measurements by the user
* if 'option savecurrents' is set by the user.
* We have to check for this option here prematurely
* because options will be processed later.
* Then append a
* .save all
* statement to 'wl' if no other 'save' has been given so far.
* Then scan the deck for known devices and append
* .save @q1[ib]
* statements to 'wl' for all of their current vectors.
*/
static wordlist *
inp_savecurrents(struct card *deck, struct card *options, wordlist *wl, wordlist *controls)
{
wordlist *p;
/* check if option 'savecurrents' is set */
for (; options; options = options->nextcard)
if (strstr(options->line, "savecurrents"))
break;
if (!options)
return wl;
/* search for 'save' command in the .control section */
for (p = controls; p; p = p->wl_next)
if(prefix("save", p->wl_word))
break;
/* search for '.save' in the 'wl' list */
if (!p)
for (p = wl; p; p = p->wl_next)
if(prefix(".save", p->wl_word))
break;
/* if not found, then add '.save all' */
if (!p) {
p = wl_cons(copy(".save all"), NULL);
}
else {
p = NULL;
}
/* Scan the deck for devices with their terminals.
* We currently serve bipolars, resistors, MOS1, capacitors, inductors,
* controlled current sources. Others may follow.
*/
for (deck = deck->nextcard; deck; deck = deck->nextcard) {
char *newline, *devname, *devline = deck->line;
switch (devline[0]) {
case 'm':
devname = gettok(&devline);
newline = tprintf(".save @%s[id] @%s[is] @%s[ig] @%s[ib]",
devname, devname, devname, devname);
break;
case 'j':
devname = gettok(&devline);
newline = tprintf(".save @%s[id] @%s[is] @%s[ig] @%s[igd]",
devname, devname, devname, devname);
break;
case 'q':
devname = gettok(&devline);
newline = tprintf(".save @%s[ic] @%s[ie] @%s[ib] @%s[is]",
devname, devname, devname, devname);
break;
case 'd':
devname = gettok(&devline);
newline = tprintf(".save @%s[id]", devname);
break;
case 'r':
case 'c':
case 'l':
case 'b':
case 'f':
case 'g':
case 'w':
case 's':
devname = gettok(&devline);
newline = tprintf(".save @%s[i]", devname);
break;
case 'i':
devname = gettok(&devline);
newline = tprintf(".save @%s[current]", devname);
break;
default:
continue;
}
p = wl_cons(newline, p);
tfree(devname);
}
return wl_append(wl, wl_reverse(p));
}
static double
agauss(double nominal_val, double abs_variation, double sigma)
{
double stdvar;
stdvar = abs_variation / sigma;
return (nominal_val + stdvar * gauss1());
}
static double
gauss(double nominal_val, double rel_variation, double sigma)
{
double stdvar;
stdvar = nominal_val * rel_variation / sigma;
return (nominal_val + stdvar * gauss1());
}
static double
unif(double nominal_val, double rel_variation)
{
return (nominal_val + nominal_val * rel_variation * drand());
}
static double
aunif(double nominal_val, double abs_variation)
{
return (nominal_val + abs_variation * drand());
}
static double
limit(double nominal_val, double abs_variation)
{
return (nominal_val + (drand() > 0 ? abs_variation : -1. * abs_variation));
}
/* Second step to enable functions agauss, gauss, aunif, unif, limit
* in professional parameter decks:
* agauss has been preserved by replacement operation of .func
* (function inp_fix_agauss_in_param() in inpcom.c).
* After subcircuit expansion, agauss may be still existing in b-lines,
* however agauss does not exist in the B source parser, and it would
* not make sense in adding it there, because in each time step a different
* return from agauss would result.
* So we have to do the following in each B-line:
* check for agauss(x,y,z), and replace it by a suitable return value
* of agauss()
* agauss in .param lines has been treated already
*/
static void
eval_agauss(struct card *deck, char *fcn)
{
struct card *card;
double x, y, z, val;
int skip_control = 0;
card = deck;
for (; card; card = card->nextcard) {
char *ap, *curr_line = card->line;
/* exclude any command inside .control ... .endc */
if (ciprefix(".control", curr_line)) {
skip_control++;
continue;
}
else if (ciprefix(".endc", curr_line)) {
skip_control--;
continue;
}
else if (skip_control > 0) {
continue;
}
if (*curr_line != 'b')
continue;
while ((ap = search_identifier(curr_line, fcn, curr_line)) != NULL) {
char *lparen, *begstr, *contstr = NULL, *new_line, *midstr;
char *tmp1str, *tmp2str, *delstr;
int nerror;
begstr = copy_substring(curr_line, ap);
lparen = strchr(ap, '(');
tmp1str = midstr = gettok_char(&lparen, ')', FALSE, TRUE);
if (lparen + 1)
contstr = copy(lparen + 1);
tmp1str++; /* skip '(' */
/* find the parameters */
delstr = tmp2str = gettok(&tmp1str);
x = INPevaluate(&tmp2str, &nerror, 1);
tfree(delstr);
delstr = tmp2str = gettok(&tmp1str);
y = INPevaluate(&tmp2str, &nerror, 1);
tfree(delstr);
if (cieq(fcn, "agauss")) {
delstr = tmp2str = gettok(&tmp1str);
z = INPevaluate(&tmp2str, &nerror, 1);
tfree(delstr);
val = agauss(x, y, z);
}
else if (cieq(fcn, "gauss")) {
delstr = tmp2str = gettok(&tmp1str);
z = INPevaluate(&tmp2str, &nerror, 1);
tfree(delstr);
val = gauss(x, y, z);
}
else if (cieq(fcn, "aunif")) {
val = aunif(x, y);
}
else if (cieq(fcn, "unif")) {
val = unif(x, y);
}
else if (cieq(fcn, "limit")) {
val = limit(x, y);
}
else {
fprintf(cp_err, "ERROR: Unknown function %s, cannot evaluate\n", fcn);
tfree(begstr);
tfree(contstr);
tfree(midstr);
return;
}
new_line = tprintf("%s%g%s", begstr, val, contstr);
tfree(card->line);
curr_line = card->line = new_line;
tfree(begstr);
tfree(contstr);
tfree(midstr);
}
}
}
struct mlist {
struct card* mod;
struct card* prevmod;
struct card* prevcard;
char* mname;
float wmin;
float wmax;
float lmin;
float lmax;
struct mlist* nextm;
bool used;
bool checked;
};
/* Finally get rid of unused MOS models */
static void rem_unused_mos_models(struct card* deck) {
struct card *tmpc, *tmppc = NULL;
struct mlist* modellist = NULL, *tmplist;
double scale;
if (!cp_getvar("scale", CP_REAL, &scale, 0))
scale = 1;
/* the old way to remove unused models */
struct nscope* root = inp_add_levels(deck);
comment_out_unused_subckt_models(deck);
inp_rem_unused_models(root, deck);
/* remove unused binning models */
for (tmpc = deck; tmpc; tmppc = tmpc, tmpc = tmpc->nextcard) {
char* curr_line;
char* nline = curr_line = tmpc->line;
if (ciprefix(".model", nline)) {
float fwmin, fwmax, flmin, flmax;
char* wmin = strstr(curr_line, " wmin=");
if (wmin) {
int err;
wmin = wmin + 6;
wmin = skip_ws(wmin);
fwmin = (float)INPevaluate(&wmin, &err, 0);
if (err) {
continue;
}
}
else {
continue;
}
char* wmax = strstr(curr_line, " wmax=");
if (wmax) {
int err;
wmax = wmax + 6;
wmax = skip_ws(wmax);
fwmax = (float)INPevaluate(&wmax, &err, 0);
if (err) {
continue;
}
}
else {
continue;
}
char* lmin = strstr(curr_line, " lmin=");
if (lmin) {
int err;
lmin = lmin + 6;
lmin = skip_ws(lmin);
flmin = (float)INPevaluate(&lmin, &err, 0);
if (err) {
continue;
}
}
else {
continue;
}
char* lmax = strstr(curr_line, " lmax=");
if (lmax) {
int err;
lmax = lmax + 6;
lmax = skip_ws(lmax);
flmax = (float)INPevaluate(&lmax, &err, 0);
if (err) {
continue;
}
}
else {
continue;
}
nline = nexttok(nline);
char* modname = gettok(&nline);
struct mlist* newm = TMALLOC(struct mlist, 1);
newm->mname = modname;
newm->mod = tmpc;
newm->prevmod = tmppc;
newm->wmin = newm->wmax = newm->lmin = newm->lmax = 0.;
newm->nextm = NULL;
newm->used = FALSE;
newm->checked = FALSE;
newm->lmax = flmax;
newm->lmin = flmin;
newm->wmax = fwmax;
newm->wmin = fwmin;
if (!modellist) {
modellist = newm;
}
else {
struct mlist* tmpl = modellist;
modellist = newm;
modellist->nextm = tmpl;
}
modellist->prevcard = tmppc;
}
}
for (tmpc = deck; tmpc; tmpc = tmpc->nextcard) {
char* curr_line = tmpc->line;
/* We only look for MOS devices and extract W, L, nf, and wnflag */
if (*curr_line == 'm') {
float w = 0., l = 0., nf = 1., wnf = 1.;
int wnflag = 0;
char* wstr = strstr(curr_line, " w=");
if (wstr) {
int err;
wstr = wstr + 3;
wstr = skip_ws(wstr);
w = (float)INPevaluate(&wstr, &err, 0);
if (err) {
continue;
}
}
char* lstr = strstr(curr_line, " l=");
if (lstr) {
int err;
lstr = lstr + 3;
lstr = skip_ws(lstr);
l = (float)INPevaluate(&lstr, &err, 0);
if (err) {
continue;
}
}
char* nfstr = strstr(curr_line, " nf=");
if (nfstr) {
int err;
nfstr = nfstr + 4;
nfstr = skip_ws(nfstr);
nf = (float)INPevaluate(&nfstr, &err, 0);
if (err) {
continue;
}
}
char* wnstr = strstr(curr_line, " wnflag=");
if (wnstr) {
int err;
wnstr = wnstr + 8;
wnstr = skip_ws(wnstr);
wnf = (float)INPevaluate(&wnstr, &err, 0);
if (err) {
continue;
}
}
if (!cp_getvar("wnflag", CP_NUM, &wnflag, 0)) {
if (newcompat.spe || newcompat.hs)
wnflag = 1;
else
wnflag = 0;
}
nf = (float)wnflag * wnf > 0.5f ? nf : 1.f;
w = w / nf;
/* what is the device's model name? */
char* mname = nexttok(curr_line);
int nonodes = 4; /* FIXME: this is a hack! How to really detect the number of nodes? */
int jj;
for (jj = 0; jj < nonodes; jj++) {
mname = nexttok(mname);
}
mname = gettok(&mname);
/* We now check all models */
for (tmplist = modellist; tmplist; tmplist = tmplist->nextm) {
if (strstr(tmplist->mname, mname)) {
float ls = l * (float)scale;
float ws = w * (float)scale;
if (tmplist->lmin <= ls && tmplist->lmax >= ls && tmplist->wmin <= ws && tmplist->wmax >= ws)
tmplist->used = TRUE;
else
tmplist->checked = TRUE;
}
else {
tmplist->checked = TRUE;
}
}
tfree(mname);
}
}
/* Delete the models that have been checked, but are unused */
for (tmplist = modellist; tmplist; tmplist = tmplist->nextm) {
if (tmplist->checked && !tmplist->used) {
if (tmplist->prevcard == NULL) {
struct card* tmpcard = tmplist->mod;
tmplist->mod = tmplist->mod->nextcard;
line_free_x(tmpcard, FALSE);
}
else {
struct card* tmpcard = tmplist->prevcard;
tmpcard->nextcard = tmplist->mod->nextcard;
line_free_x(tmplist->mod, FALSE);
}
}
}
/* Remove modellist */
while (modellist) {
struct mlist* tlist = modellist->nextm;
tfree(modellist->mname);
tfree(modellist);
modellist = tlist;
}
}