Copyright 1990 Regents of the University of California. All rights reserved.
Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
Modified: 2000 AlansFixes
**********/
* encapsulated string assembly in translate() and finishLine()
* this string facility (bxx_buffer) mainly abstracts away buffer allocation.
* this fixes a buffer overflow in finishLine, caused by lengthy descriptions
* of the kind:
* B1 1 2 I=v(1)+v(2)+v(3)+...
* Larice, 22nd Aug 2009
*----------------------------------------------------------------------------*/
* Added changes supplied by by H.Tanaka with some tidy up of comments, debug
* statements, and variables. This fixes a problem with nested .subsck elements
* that accessed .model lines. Code not ideal, but it seems to work okay.
* Also took opportunity to tidy a few other items (unused variables etc.), plus
* fix a few spelling errors in the comments, and a memory leak.
* SJB 25th March 2005
*----------------------------------------------------------------------------*/
* re-written by SDB during 4.2003 to enable SPICE2 POLY statements to be processed
* properly. This is particularly important for dependent sources, whose argument
* list changes when POLY is used.
* Major changes include:
* -- Added lots of comments which (hopefully) elucidate the steps taken
* by the program during its processing.
* -- Re-wrote translate, which does the processing of each card.
* Please direct comments/questions/complaints to Stuart Brorson:
* mailto:sdb@cloud9.net
*-----------------------------------------------------------------------------*/
* Expand subcircuits. This is very spice-dependent. Bug fixes by Norbert
* Jeske on 10/5/85.
*/
* Expand all subcircuits in the deck. This handles imbedded .subckt
* definitions. The variables substart, subend, and subinvoke can be used
* to redefine the controls used. The syntax is invariant though.
* NOTE: the deck must be passed without the title line.
* What we do is as follows: first make one pass through the circuit
* and collect all of the subcircuits. Then, whenever a line that starts
* with 'x' is found, copy the subcircuit associated with that name and
* splice it in. A few of the problems: the nodes in the spliced-in
* stuff must be unique, so when we copy it, append "subcktname:" to
* each node. If we are in a nested subcircuit, use foo:bar:...:node.
* Then we have to systematically change all references to the renamed
* nodes. On top of that, we have to know how many args BJT's have,
* so we have to keep track of model names.
*======================================================================*/
#include "ngspice/ngspice.h"
#include "ngspice/cpdefs.h"
#include "ngspice/ftedefs.h"
#include "ngspice/fteinp.h"
#include "ngspice/stringskip.h"
#include "ngspice/compatmode.h"
#include "ngspice/hash.h"
#include <stdarg.h>
#ifdef XSPICE
#include "ngspice/mifproto.h"
#endif
#include "subckt.h"
#include "variable.h"
#include "numparam/numpaif.h"
extern void line_free_x(struct card *deck, bool recurse);
extern int get_number_terminals(char* c);
extern void tprint(struct card* deck);
#define line_free(line, flag) \
do { \
line_free_x(line, flag); \
line = NULL; \
} while(0)
struct subs;
static struct card *doit(struct card *deck, wordlist *modnames);
static int translate(struct card *deck, char *formal, int flen, char *actual,
char *scname, const char *subname, struct subs *subs,
wordlist const *modnames);
struct bxx_buffer;
static void finishLine(struct bxx_buffer *dst, char *src, char *scname);
static int settrans(char *formal, int flen, char *actual, const char *subname);
static char *gettrans(const char *name, const char *name_end, bool *isglobal);
static int numnodes(const char *line, struct subs *subs, wordlist const *modnames);
static int numdevs(char *s);
static wordlist *modtranslate(struct card *deck, char *subname, wordlist *new_modnames);
static void devmodtranslate(struct card *deck, char *subname, wordlist * const orig_modnames);
static int inp_numnodes(char c);
* For now its use is limited to avoid double entries in global_nodes[] */
static NGHASHPTR glonodes = NULL;
#define DUMMYDATA ((void *)42)
* table is used in settrans and gettrans -- it holds the netnames used
* in the .subckt definition (t_old), and in the subcircuit invocation
* (t_new). The table ends when t_old is NULL.
*--------------------------------------------------------------------*/
static struct tab {
char *t_old;
char *t_new;
} *table;
* subs is the linked list which holds the .subckt definitions
* found during processing.
*--------------------------------------------------------------------*/
struct subs {
char *su_name;
char *su_args;
int su_numargs;
struct card *su_def;
struct subs *su_next;
};
* list of translated names (i.e. after subckt expansion)
*/
static bool use_numparams = FALSE;
static char start[32], sbend[32], invoke[32], model[32];
static void
collect_global_nodes(struct card *c)
{
int num_global_nodes;
glonodes = nghash_init(NGHASH_MIN_SIZE);
nghash_insert(glonodes, "0", DUMMYDATA);
#ifdef XSPICE
nghash_insert(glonodes, "null", DUMMYDATA);
#endif
num_global_nodes = 2;
for (; c; c = c->nextcard)
if (ciprefix(".global", c->line)) {
char *s = c->line;
s = nexttok(s);
while (*s) {
char *t = skip_non_ws(s);
char *gnode = copy_substring(s, t);
if (gnode && *gnode != '\0' && nghash_find(glonodes, gnode) == NULL) {
nghash_insert(glonodes, gnode, DUMMYDATA);
}
tfree(gnode);
s = skip_ws(t);
}
num_global_nodes++;
#ifdef TRACE
if (num_global_nodes == 3)
fprintf(stderr, "***Global node option has been found.***\n");
fprintf(stderr, "***Global node no.%d is %s.***\n", num_global_nodes, c->line);
#endif
c->line[0] = '*';
}
}
static void
free_global_nodes(void)
{
nghash_free(glonodes, NULL, NULL);
}
inp_subcktexpand is the top level function which translates
.subckts into mainlined code. Note that there are several things
we need to do: 1. Find all .subckt definitions & stick them
into a list. 2. Find all subcircuit invocations (refdes X)
and replace them with the .subckt definition stored earlier.
3. Do parameter substitution.
The algorithm is as follows:
1. Define some aliases for .subckt, .ends, etc.
2. First numparam pass: substitute paramterized tokens by
intermediate values 1000000001 etc.
3. Make a list global_nodes[] of global nodes
4. Clean up parens around netnames
5. Call doit, which does the actual translation.
6. Second numparam pass: Do final substitution
7. Check the results & return.
inp_subcktexpand takes as argument a pointer to deck, and
it returns a pointer to the same deck after the new subcircuits
are spliced in.
-------------------------------------------------------------------*/
struct card *
inp_subcktexpand(struct card *deck) {
struct card *c;
wordlist *modnames = NULL;
if (!cp_getvar("substart", CP_STRING, start, sizeof(start)))
strcpy(start, ".subckt");
if (!cp_getvar("subend", CP_STRING, sbend, sizeof(sbend)))
strcpy(sbend, ".ends");
if (!cp_getvar("subinvoke", CP_STRING, invoke, sizeof(invoke)))
strcpy(invoke, "x");
if (!cp_getvar("modelcard", CP_STRING, model, sizeof(model)))
strcpy(model, ".model");
if (!cp_getvar("modelline", CP_STRING, model, sizeof(model)))
strcpy(model, ".model");
use_numparams = TRUE;
if (use_numparams) {
#ifdef TRACE
fprintf(stderr, "Numparams is processing this deck:\n");
for (c = deck; c; c = c->nextcard) {
if (ciprefix("*", c->line))
continue;
fprintf(stderr, "%3d:%s\n", c->linenum, c->line);
}
#endif
nupa_signal(NUPADECKCOPY);
for (c = deck; c; c = c->nextcard) {
if (ciprefix(".subckt", c->line)) {
nupa_scan(c);
}
}
for (c = deck; c; c = c->nextcard) {
if (*(c->line) == '*') {
continue;
}
c->line = nupa_copy(c);
}
#ifdef TRACE
fprintf(stderr, "Numparams transformed deck:\n");
for (c = deck; c; c = c->nextcard) {
if (ciprefix("*", c->line))
continue;
fprintf(stderr, "%3d:%s\n", c->linenum, c->line);
}
#endif
}
* Stick all the model names into the doubly-linked wordlist modnames.
*/
{
int nest = 0;
for (c = deck; c; c = c->nextcard) {
if (ciprefix(".subckt", c->line))
nest++;
else if (ciprefix(".ends", c->line))
nest--;
else if (nest > 0)
continue;
if (ciprefix(model, c->line)) {
char *s = nexttok(c->line);
modnames = wl_cons(gettok(&s), modnames);
}
}
}
#ifdef TRACE
{
wordlist *w;
printf("Models found:\n");
for (w = modnames; w; w = w->wl_next)
printf("%s\n", w->wl_word);
}
#endif
collect_global_nodes(deck);
for (c = deck; c; c = c->nextcard) {
char *s = c->line;
if (*s == '*')
continue;
if (ciprefix(start, s)) {
#ifdef TRACE
printf("In inp_subcktexpand, found a .subckt: %s\n", s);
#endif
while (*s && *s != '(')
s++;
if (*s == '(') {
int level = 0;
do {
if (*s == '(' && level++ == 0) {
*s = ' ';
}
if (*s == ')' && --level == 0) {
*s = ' ';
break;
}
} while(*s++);
}
} else if (*s == '.') {
continue;
} else {
s = skip_non_ws(s);
s = skip_ws(s);
if (*s == '(') {
int level = 0;
do {
if (*s == '(' && level++ == 0) {
*s = ' ';
}
if (*s == ')' && --level == 0) {
*s = ' ';
break;
}
} while(*s++);
}
}
}
#ifdef TRACE
printf("In inp_subcktexpand, about to call doit.\n");
#endif
deck = doit(deck, modnames);
free_global_nodes();
wl_free(modnames);
if (deck) {
dynMaxckt = 0;
for (c = deck; c; c = c->nextcard)
dynMaxckt++;
}
for (c = deck; c; c = c->nextcard)
if (ciprefix(invoke, c->line)) {
fprintf(cp_err, "Error: unknown subckt: %s\n", c->line);
fprintf(cp_err, " in line no. %d from file %s\n", c->linenum_orig, c->linesource);
if (use_numparams)
nupa_signal(NUPAEVALDONE);
return NULL;
}
if (use_numparams) {
nupa_signal(NUPASUBDONE);
for (c = deck; c; c = c->nextcard)
if (ciprefix(".meas", c->line) && strstr(c->line, "param")) {
;
} else {
nupa_eval(c);
}
#ifdef TRACE
fprintf(stderr, "Numparams converted deck:\n");
for (c = deck; c; c = c->nextcard) {
if (ciprefix("*", c->line))
continue;
fprintf(stderr, "%3d:%s\n", c->linenum, c->line);
}
#endif
nupa_copy_inst_dico();
nupa_signal(NUPAEVALDONE);
}
return (deck);
}
static struct card *
find_ends(struct card *l)
{
int nest = 1;
while (l->nextcard) {
if (ciprefix(sbend, l->nextcard->line))
nest--;
else if (ciprefix(start, l->nextcard->line))
nest++;
if (!nest)
break;
l = l->nextcard;
}
return l;
}
#define MAXNEST 21
static struct card *
doit(struct card *deck, wordlist *modnames) {
struct subs *sss = NULL;
int numpasses = MAXNEST;
bool gotone;
int error;
struct subs *subs = NULL;
wordlist *xmodnames = modnames;
#ifdef TRACE
{
struct card *c;
printf("In doit, about to start first pass through deck.\n");
for (c = deck; c; c = c->nextcard) {
if (ciprefix("*", c->line))
continue;
printf(" %s\n", c->line);
}
}
#endif
{
struct card *c = deck;
struct card *prev_of_c = NULL;
while (c) {
if (ciprefix(sbend, c->line)) {
fprintf(cp_err, "Error: misplaced %s line: %s\n", sbend,
c->line);
fprintf(cp_err, " line no. %d from file %s \n",
c->linenum_orig, c->linesource);
return (NULL);
}
if (ciprefix(start, c->line)) {
struct card *prev_of_ends = find_ends(c);
struct card *ends = prev_of_ends->nextcard;
if (!ends) {
fprintf(cp_err, "Error: no %s line.\n", sbend);
fprintf(cp_err, " in file %s \n",
c->linesource);
return (NULL);
}
{
char *s = c->line;
sss = TMALLOC(struct subs, 1);
s = nexttok(s);
sss->su_name = gettok(&s);
sss->su_args = copy(s);
sss->su_def = c->nextcard;
sss->su_numargs = 0;
for (;;) {
s = skip_ws(s);
if (*s == '\0')
break;
s = skip_non_ws(s);
sss->su_numargs ++;
}
}
sss->su_next = subs;
subs = sss;
line_free_x(c, FALSE);
c = ends->nextcard;
if (prev_of_c)
prev_of_c->nextcard = c;
else
deck = c;
if (use_numparams == FALSE) {
line_free_x(ends, FALSE);
prev_of_ends->nextcard = NULL;
} else {
ends->line[0] = '*';
ends->nextcard = NULL;
}
} else {
prev_of_c = c;
c = c->nextcard;
}
}
}
* all .subckt defs found, including this one
*/
if (!subs)
return (deck);
for (sss = subs; sss; sss = sss->su_next)
if ((sss->su_def = doit(sss->su_def, modnames)) == NULL)
return (NULL);
#ifdef TRACE
{
struct card *c;
printf("In doit, about to start second pass through deck.\n");
for (c = deck; c; c = c->nextcard) {
if (ciprefix("*", c->line))
continue;
printf(" %s\n", c->line);
}
}
#endif
double scale;
if (!cp_getvar("scale", CP_REAL, &scale, 0))
scale = 1;
error = 0;
do {
struct card *c = deck;
struct card *prev_of_c = NULL;
gotone = FALSE;
for (; c; prev_of_c = c, c = c->nextcard) {
if (ciprefix(invoke, c->line)) {
char *tofree, *tofree2, *s, *t;
char *scname;
gotone = TRUE;
t = tofree = s = copy(c->line);
* e.g. if invocation is Xreference, *scname = reference
*/
tofree2 = scname = gettok(&s);
while ((*scname == ' ') || (*scname == '\t') || (*scname == ':'))
scname++;
* the name of the model invoked
*/
while (*s)
s++;
s--;
while ((*s == ' ') || (*s == '\t'))
*s-- = '\0';
while ((*s != ' ') && (*s != '\t'))
s--;
s++;
for (sss = subs; sss; sss = sss->su_next)
if (eq(sss->su_name, s))
break;
* and scname points to the netnames
* involved.
*/
* instance of a subckt that is defined above at higher level.
*/
if (sss) {
struct card *su_deck = inp_deckcopy(sss->su_def);
We try to reduce the models to the one really used.
Otherwise su_deck is full of unused binning models.*/
if ((newcompat.hs || newcompat.spe) && c->w > 0 && c->l > 0) {
struct card* new_deck = su_deck;
struct card* prev = NULL;
while (su_deck) {
if (!ciprefix(".model", su_deck->line)) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
char* curr_line = su_deck->line;
float fwmin, fwmax, flmin, flmax;
char *wmin = strstr(curr_line, " wmin=");
if (wmin) {
int err;
wmin = wmin + 6;
fwmin = (float)INPevaluate(&wmin, &err, 0);
if (err) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
}
else {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
char *wmax = strstr(curr_line, " wmax=");
if (wmax) {
int err;
wmax = wmax + 6;
fwmax = (float)INPevaluate(&wmax, &err, 0);
if (err) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
}
else {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
char* lmin = strstr(curr_line, " lmin=");
if (lmin) {
int err;
lmin = lmin + 6;
flmin = (float)INPevaluate(&lmin, &err, 0);
if (err) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
}
else {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
char* lmax = strstr(curr_line, " lmax=");
if (lmax) {
int err;
lmax = lmax + 6;
flmax = (float)INPevaluate(&lmax, &err, 0);
if (err) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
}
else {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
float csl = (float)scale * c->l;
float csw = (float)scale * c->w / c->nf;
if (csl >= flmin && csl < flmax && csw >= fwmin && csw < fwmax) {
prev = su_deck;
su_deck = su_deck->nextcard;
continue;
}
else {
struct card* tmpcard = su_deck->nextcard;
line_free_x(prev->nextcard, FALSE);
su_deck = prev->nextcard = tmpcard;
}
}
su_deck = new_deck;
}
if (!su_deck) {
fprintf(stderr, "\nError: Could not find a model for device %s in subcircuit %s\n",
scname, sss->su_name);
controlled_exit(1);
}
struct card *rest_of_c = c->nextcard;
* macro definition.
*/
modnames = modtranslate(su_deck, scname, modnames);
t = nexttok(t);
* translation.
*/
if (!translate(su_deck, sss->su_args, sss->su_numargs, t, scname, sss->su_name, subs, modnames))
error = 1;
if (use_numparams == FALSE) {
line_free_x(c, FALSE);
if (prev_of_c)
prev_of_c->nextcard = su_deck;
else
deck = su_deck;
} else {
c->line[0] = '*';
c->nextcard = su_deck;
}
c = su_deck;
while (c->nextcard)
c = c->nextcard;
c->nextcard = rest_of_c;
}
tfree(tofree);
tfree(tofree2);
}
}
} while (!error && numpasses-- && gotone);
if (!numpasses) {
fprintf(cp_err, "Error: infinite subckt recursion\n");
error = 1;
}
#ifdef TRACE
{
struct card *c = deck;
printf("Converted deck\n");
for (; c; c = c->nextcard) {
if (ciprefix("*", c->line))
continue;
printf("%s\n", c->line);
}
}
{
wordlist *w = modnames;
printf("Models:\n");
for (; w; w = w->wl_next)
printf("%s\n", w->wl_word);
}
#endif
wl_delete_slice(modnames, xmodnames);
if (error)
return NULL;
while (subs) {
struct subs *rest = subs->su_next;
tfree(subs->su_name);
tfree(subs->su_args);
line_free(subs->su_def, TRUE);
tfree(subs);
subs = rest;
}
return (deck);
}
struct card * inp_deckcopy(struct card *deck) {
struct card *d = NULL, *nd = NULL;
while (deck) {
if (nd) {
d->nextcard = TMALLOC(struct card, 1);
d = d->nextcard;
} else {
nd = d = TMALLOC(struct card, 1);
}
d->linenum = deck->linenum;
d->linenum_orig = deck->linenum_orig;
d->compmod = deck->compmod;
d->linesource = deck->linesource;
d->w = deck->w;
d->l = deck->l;
d->nf = deck->nf;
d->line = copy(deck->line);
if (deck->error)
d->error = copy(deck->error);
d->actualLine = inp_deckcopy(deck->actualLine);
deck = deck->nextcard;
}
return (nd);
}
* Copy a deck, without the ->actualLine lines, without comment lines, and
* without .control section(s).
* First line is always copied (except being .control).
*/
struct card *inp_deckcopy_oc(struct card * deck)
{
struct card *d = NULL, *nd = NULL;
int skip_control = 0, i = 0;
while (deck) {
if (ciprefix(".control", deck->line)) {
skip_control++;
deck = deck->nextcard;
continue;
}
else if (ciprefix(".endc", deck->line)) {
skip_control--;
deck = deck->nextcard;
continue;
}
else if (skip_control > 0) {
deck = deck->nextcard;
continue;
}
if (nd) {
d = d->nextcard = TMALLOC(struct card, 1);
}
else {
nd = d = TMALLOC(struct card, 1);
}
d->w = deck->w;
d->l = deck->l;
d->nf = deck->nf;
d->linenum_orig = deck->linenum_orig;
d->compmod = deck->compmod;
d->linesource = deck->linesource;
d->linenum = i++;
d->line = copy(deck->line);
if (deck->error) {
d->error = copy(deck->error);
}
d->actualLine = NULL;
deck = deck->nextcard;
while (deck && *(deck->line) == '*') {
deck = deck->nextcard;
}
}
return nd;
}
* Copy a deck, without the ->actualLine lines, without comment lines, and
* without .control section(s).
* Keep the line numbers.
*/
struct card* inp_deckcopy_ln(struct card* deck)
{
struct card* d = NULL, * nd = NULL;
int skip_control = 0;
while (deck) {
if (ciprefix(".control", deck->line)) {
skip_control++;
deck = deck->nextcard;
continue;
}
else if (ciprefix(".endc", deck->line)) {
skip_control--;
deck = deck->nextcard;
continue;
}
else if (skip_control > 0) {
deck = deck->nextcard;
continue;
}
else if (*(deck->line) == '*') {
deck = deck->nextcard;
continue;
}
if (nd) {
d = d->nextcard = TMALLOC(struct card, 1);
}
else {
nd = d = TMALLOC(struct card, 1);
}
d->w = deck->w;
d->l = deck->l;
d->nf = deck->nf;
d->linenum_orig = deck->linenum_orig;
d->linesource = deck->linesource;
d->linenum = deck->linenum;
d->compmod = deck->compmod;
d->line = copy(deck->line);
if (deck->error) {
d->error = copy(deck->error);
}
d->actualLine = NULL;
deck = deck->nextcard;
}
return nd;
}
* struct bxx_buffer,
* a string assembly facility.
*
* usage:
*
* struct bxx_buffer thing;
* bxx_init(&thing);
* ...
* while (...) {
* bxx_rewind(&thing);
* ...
* bxx_putc(&thing, ...)
* bxx_printf(&thing, ...)
* bxx_put_cstring(&thing, ...)
* bxx_put_substring(&thing, ...)
* ...
* strcpy(bxx_buffer(&thing)
* }
* ..
* bxx_free(&thing)
*
* main aspect:
* reallocates/extends its buffer itself.
*
* note:
* during asssembly the internal buffer is
* not necessarily '\0' terminated.
* but will be when bxx_buffer() is invoked
*/
struct bxx_buffer {
char *dst;
char *limit;
char *buffer;
};
static const int bxx_chunksize = 1024;
static void
bxx_init(struct bxx_buffer *t)
{
t->buffer = TMALLOC(char, bxx_chunksize);
t->dst = t->buffer;
t->limit = t->buffer + bxx_chunksize;
}
static void
bxx_free(struct bxx_buffer *t)
{
tfree(t->buffer);
}
static void
bxx_rewind(struct bxx_buffer *t)
{
t->dst = t->buffer;
}
static void
bxx_extend(struct bxx_buffer *t, int howmuch)
{
int pos = (int)(t->dst - t->buffer);
int len = (int)(t->limit - t->buffer);
howmuch += (bxx_chunksize - 1);
howmuch &= ~(bxx_chunksize - 1);
len += howmuch;
t->buffer = TREALLOC(char, t->buffer, len);
t->dst = t->buffer + pos;
t->limit = t->buffer + len;
}
static void
bxx_printf(struct bxx_buffer *t, const char *fmt, ...)
{
va_list ap;
for (;;) {
int ret;
int size = (int)(t->limit - t->dst);
va_start(ap, fmt);
ret = vsnprintf(t->dst, (size_t) size, fmt, ap);
va_end(ap);
if (ret == -1) {
bxx_extend(t, bxx_chunksize);
} else if (ret >= size) {
bxx_extend(t, ret - size + 1);
} else {
t->dst += ret;
break;
}
}
va_end(ap);
}
static inline char
bxx_putc(struct bxx_buffer *t, char c)
{
if (t->dst >= t->limit)
bxx_extend(t, 1);
return *(t->dst)++ = c;
}
static void
bxx_put_cstring(struct bxx_buffer *t, const char *cstring)
{
while (*cstring)
bxx_putc(t, *cstring++);
}
static void
bxx_put_substring(struct bxx_buffer *t, const char *str, const char *end)
{
while (str < end)
bxx_putc(t, *str++);
}
static char *
bxx_buffer(struct bxx_buffer *t)
{
if ((t->dst == t->buffer) || (t->dst[-1] != '\0'))
bxx_putc(t, '\0');
return t->buffer;
}
* Translate all of the device names and node names in the .subckt deck. They are
* pre-pended with subname:, unless they are in the formal list, in which case
* they are replaced with the corresponding entry in the actual list.
* The one special case is node 0 -- this is always ground and we don't
* touch it.
*
* Variable name meanings:
* *deck = pointer to subcircuit definition (lcc) (struct card)
* formal = copy of the .subckt definition line (e.g. ".subckt subcircuitname 1 2 3") (string)
* actual = copy of the .subcircuit invocation line (e.g. "Xexample 4 5 6 subcircuitname") (string)
* scname = refdes (- first letter) used at invocation (e.g. "example") (string)
* subname = copy of the subcircuit name
*-------------------------------------------------------------------------------------------*/
static void
translate_node_name(struct bxx_buffer *buffer, const char *scname, const char *name, const char *name_e)
{
const char *t;
bool isglobal;
if (!name_e)
name_e = strchr(name, '\0');
t = gettrans(name, name_e, &isglobal);
if (t) {
bxx_put_cstring(buffer, t);
if(isglobal)
tfree(t);
} else {
bxx_put_cstring(buffer, scname);
bxx_putc(buffer, '.');
bxx_put_substring(buffer, name, name_e);
}
}
static void
translate_inst_name(struct bxx_buffer *buffer, const char *scname, const char *name, const char *name_e)
{
if (!name_e)
name_e = strchr(name, '\0');
if (tolower_c(*name) != 'x') {
bxx_putc(buffer, *name);
bxx_putc(buffer, '.');
}
bxx_put_cstring(buffer, scname);
bxx_putc(buffer, '.');
bxx_put_substring(buffer, name, name_e);
}
static int
translate(struct card *deck, char *formal, int flen, char *actual, char *scname, const char *subname, struct subs *subs, wordlist const *modnames)
{
struct card *c;
struct bxx_buffer buffer;
char *next_name, *name, *t, *nametofree, *paren_ptr;
int nnodes, i, dim;
int rtn = 0;
#ifdef XSPICE
bool got_vnam = FALSE;
#endif
bxx_init(&buffer);
i = settrans(formal, flen, actual, subname);
if (i < 0) {
fprintf(stderr,
"Too few parameters for subcircuit type \"%s\" (instance: x%s)\n",
subname, scname);
goto quit;
} else if (i > 0) {
fprintf(stderr,
"Too many parameters for subcircuit type \"%s\" (instance: x%s)\n",
subname, scname);
goto quit;
}
for (c = deck; c; c = c->nextcard) {
char *s = c->line;
char dev_type = tolower_c(s[0]);
bxx_rewind(&buffer);
#ifdef TRACE
printf("\nIn translate, examining line (dev_type: %c, subname: %s, instance: %s) %s \n", dev_type, subname, scname, s);
#endif
switch (dev_type) {
case '.':
if (ciprefix(".save", s)) {
while ((paren_ptr = strchr(s, '(')) != NULL) {
bool curr = FALSE;
char* comma_ptr = NULL;
if (ciprefix(" i(", paren_ptr - 2))
curr = TRUE;
name = paren_ptr + 1;
if ((paren_ptr = strchr(name, ')')) == NULL) {
fprintf(cp_err, "Error: missing closing ')' for .save statement %s\n", c->line);
fprintf(cp_err, " line no. %d from file %s\n", c->linenum_orig, c->linesource);
goto quit;
}
comma_ptr = strchr(s, ',');
bxx_put_substring(&buffer, s, name);
if (curr) {
translate_inst_name(&buffer, scname, name, paren_ptr);
s = paren_ptr;
}
else if (comma_ptr && comma_ptr < paren_ptr) {
translate_node_name(&buffer, scname, name, comma_ptr);
bxx_putc(&buffer, ',');
name = comma_ptr + 1;
translate_node_name(&buffer, scname, name, paren_ptr);
s = paren_ptr;
}
else {
translate_node_name(&buffer, scname, name, paren_ptr);
s = paren_ptr;
}
}
bxx_put_cstring(&buffer, s);
break;
}
else if (ciprefix(".ic", s) || ciprefix(".nodeset", s)) {
while ((paren_ptr = strchr(s, '(')) != NULL) {
name = paren_ptr + 1;
if ((paren_ptr = strchr(name, ')')) == NULL) {
fprintf(cp_err, "Error: missing closing ')' for .ic|.nodeset statement %s\n", c->line);
fprintf(cp_err, " line no. %d from file %s\n", c->linenum_orig, c->linesource);
goto quit;
}
bxx_put_substring(&buffer, s, name);
translate_node_name(&buffer, scname, name, paren_ptr);
s = paren_ptr;
}
bxx_put_cstring(&buffer, s);
break;
} else {
continue;
}
case '\0':
case '*':
case '$':
continue;
#ifdef XSPICE
case 'a':
name = MIFgettok(&s);
translate_inst_name(&buffer, scname, name, NULL);
bxx_putc(&buffer, ' ');
next_name = MIFgettok(&s);
for (;;) {
if (name)
tfree(name);
name = next_name;
next_name = MIFgettok(&s);
if (next_name == NULL)
break;
switch (*name) {
case '[':
case ']':
case '~':
bxx_put_cstring(&buffer, name);
break;
case '%':
bxx_putc(&buffer, '%');
if (name)
tfree(name);
name = next_name;
if (eq(name, "vnam"))
got_vnam = TRUE;
next_name = MIFgettok(&s);
bxx_put_cstring(&buffer, name);
break;
default:
if (got_vnam) {
translate_inst_name(&buffer, scname, name, NULL);
got_vnam = FALSE;
}
else {
translate_node_name(&buffer, scname, name, NULL);
}
break;
}
bxx_putc(&buffer, ' ');
}
if (name) {
bxx_put_cstring(&buffer, name);
tfree(name);
}
break;
#endif
* This is a new section, added by SDB to handle POLYs in sources. Significant
* changes were made in here.
* 4.21.2003 -- SDB. mailto:sdb@cloud9.net
*/
case 'e':
case 'f':
case 'g':
case 'h':
name = gettok(&s);
if (!name)
continue;
if (!*name) {
tfree(name);
continue;
}
* and stick the translated name into buffer.
*/
translate_inst_name(&buffer, scname, name, NULL);
tfree(name);
bxx_putc(&buffer, ' ');
nnodes = numnodes(c->line, subs, modnames);
while (--nnodes >= 0) {
name = gettok_node(&s);
if (name == NULL) {
fprintf(cp_err, "Error: too few nodes: %s\n",
c->line);
fprintf(cp_err, " line no. %d from file %s\n", c->linenum_orig, c->linesource);
goto quit;
}
translate_node_name(&buffer, scname, name, NULL);
tfree(name);
bxx_putc(&buffer, ' ');
}
t = s;
next_name = gettok_noparens(&t);
if ((strcmp(next_name, "POLY") == 0) ||
(strcmp(next_name, "poly") == 0)) {
#ifdef TRACE
printf("In translate, looking at e, f, g, h found poly\n");
#endif
if (get_l_paren(&s) == 1) {
fprintf(cp_err, "Error: no left paren after POLY %s\n",
c->line);
fprintf(cp_err, " line no. %d from file %s\n", c->linenum_orig, c->linesource);
tfree(next_name);
goto quit;
}
nametofree = gettok_noparens(&s);
dim = atoi(nametofree);
tfree(nametofree);
if (get_r_paren(&s) == 1) {
fprintf(cp_err, "Error: no right paren after POLY %s\n",
c->line);
fprintf(cp_err, " line no. %d from file %s\n", c->linenum_orig, c->linesource);
tfree(next_name);
goto quit;
}
bxx_printf(&buffer, "POLY( %d ) ", dim);
}
else
dim = 1;
tfree(next_name);
nnodes = dim * numdevs(c->line);
while (--nnodes >= 0) {
name = gettok_node(&s);
if (name == NULL) {
fprintf(cp_err, "Error: too few devs: %s\n", c->line);
fprintf(cp_err, " line no. %d from file %s\n", c->linenum_orig, c->linesource);
goto quit;
}
if ((dev_type == 'f') || (dev_type == 'h'))
translate_inst_name(&buffer, scname, name, NULL);
else
translate_node_name(&buffer, scname, name, NULL);
tfree(name);
bxx_putc(&buffer, ' ');
}
finishLine(&buffer, s, scname);
break;
default:
name = gettok_node(&s);
if (!name)
continue;
if (!*name) {
tfree(name);
continue;
}
translate_inst_name(&buffer, scname, name, NULL);
tfree(name);
bxx_putc(&buffer, ' ');
if (!modnames && *(c->line) == 'm')
nnodes = get_number_terminals(c->line);
else if (*(c->line) == 'n')
nnodes = get_number_terminals(c->line);
else
nnodes = numnodes(c->line, subs, modnames);
while (--nnodes >= 0) {
name = gettok_node(&s);
if (name == NULL) {
fprintf(cp_err, "Error: too few nodes: %s\n", c->line);
fprintf(cp_err, " line no. %d from file %s\n", c->linenum_orig, c->linesource);
goto quit;
}
translate_node_name(&buffer, scname, name, NULL);
tfree(name);
bxx_putc(&buffer, ' ');
}
* This may be superfluous because we handle dependent
* source devices above . . . .
*/
nnodes = numdevs(c->line);
while (--nnodes >= 0) {
name = gettok_node(&s);
if (name == NULL) {
fprintf(cp_err, "Error: too few devs: %s\n", c->line);
fprintf(cp_err, " line no. %d from file %s\n", c->linenum_orig, c->linesource);
goto quit;
}
translate_inst_name(&buffer, scname, name, NULL);
tfree(name);
bxx_putc(&buffer, ' ');
}
* this involves adding the component value to the buffer.
* We also scan through the line for v(something) and
* i(something)...
*/
finishLine(&buffer, s, scname);
break;
}
tfree(c->line);
c->line = copy(bxx_buffer(&buffer));
#ifdef TRACE
printf("In translate, translated line = %s \n", c->line);
#endif
}
rtn = 1;
quit:
for (i = 0; ; i++) {
if (!table[i].t_old && !table[i].t_new)
break;
FREE(table[i].t_old);
FREE(table[i].t_new);
}
FREE(table);
table = (struct tab *)NULL;
bxx_free(&buffer);
return rtn;
}
* finishLine now doesn't handle current or voltage sources.
* Therefore, it just writes out the final netnames, if required.
* Changes made by SDB on 4.29.2003.
*-------------------------------------------------------------------*/
static void
finishLine(struct bxx_buffer *t, char *src, char *scname)
{
char *buf, *buf_end, which;
char *s;
int lastwasalpha;
lastwasalpha = 0;
while (*src) {
* this string.
*/
if (((*src != 'v') && (*src != 'V') &&
(*src != 'i') && (*src != 'I')) ||
lastwasalpha) {
lastwasalpha = isalpha_c(*src);
bxx_putc(t, *src++);
continue;
}
which = *src;
s = skip_ws(src + 1);
if (*s != '(') {
lastwasalpha = isalpha_c(*src);
bxx_putc(t, *src++);
continue;
}
src = skip_ws(s + 1);
lastwasalpha = 0;
bxx_putc(t, which);
bxx_putc(t, '(');
for (buf = src; *src && !isspace_c(*src) && *src != ',' && *src != ')'; )
src++;
buf_end = src;
if ((which == 'v') || (which == 'V')) {
translate_node_name(t, scname, buf, buf_end);
while (*src && (isspace_c(*src) || *src == ','))
src++;
if (*src && *src != ')') {
for (buf = src; *src && !isspace_c(*src) && (*src != ')'); )
src++;
bxx_putc(t, ',');
translate_node_name(t, scname, buf, buf_end = src);
}
} else {
* i(instance_name) --> i(instance_name[0].subckt.instance_name)
*/
translate_inst_name(t, scname, buf, buf_end);
}
}
}
* settrans builds the table which holds the old and new netnames.
* it also compares the number of nets present in the .subckt definition against
* the number of nets present in the subcircuit invocation. It returns 0 if they
* match, otherwise, it returns an error.
*
* Variable definitions:
* formal = copy of the .subckt definition line (e.g. ".subckt subcircuitname 1 2 3") (string)
* actual = copy of the .subcircuit invocation line (e.g. "Xexample 4 5 6 subcircuitname") (string)
* subname = copy of the subcircuit name
*------------------------------------------------------------------------------*/
static int
settrans(char *formal, int flen, char *actual, const char *subname)
{
int i;
flen++;
table = TMALLOC(struct tab, flen + 1);
memset(table, 0, (size_t)(flen + 1) * sizeof(struct tab));
for (i = 0; i < flen; i++) {
table[i].t_old = gettok(&formal);
table[i].t_new = gettok(&actual);
if (table[i].t_new == NULL) {
return -1;
} else if (table[i].t_old == NULL) {
if (eq(table[i].t_new, subname))
break;
else
return 1;
}
}
return 0;
}
* the substring itself is required to be free of a '\0'
*/
static int
eq_substr(const char *str, const char *end, const char *cstring)
{
while (str < end)
if (*str++ != *cstring++)
return 0;
return (*cstring == '\0');
}
* gettrans returns the name of the top level net if it is in the list,
* otherwise it returns NULL.
*------------------------------------------------------------------------------*/
static char *
gettrans(const char *name, const char *name_end, bool *isglobal)
{
int i;
*isglobal = FALSE;
if (!name_end)
name_end = strchr(name, '\0');
char* newgl = copy_substring(name, name_end);
if (nghash_find(glonodes, newgl)) {
*isglobal = TRUE;
return newgl;
}
else
tfree(newgl);
for (i = 0; table[i].t_old; i++)
if (eq_substr(name, name_end, table[i].t_old)) {
*isglobal = FALSE;
return table[i].t_new;
}
return (NULL);
}
static int
numnodes(const char *line, struct subs *subs, wordlist const *modnames)
{
char c;
int n;
line = skip_ws(line);
c = tolower_c(*line);
if (c == 'x') {
const char *xname_e = skip_back_ws(strchr(line, '\0'), line);
const char *xname = skip_back_non_ws(xname_e, line);
for (; subs; subs = subs->su_next)
if (eq_substr(xname, xname_e, subs->su_name))
return subs->su_numargs;
* number of nodes not known so far.
* lets count the nodes ourselves,
* assuming `buf' looks like this:
* xname n1 n2 ... nn subname
*/
{
int nodes = -2;
while (*line) {
nodes++;
line = skip_ws(skip_non_ws(line));
}
return (nodes);
}
}
if (ft_skywaterpdk && c == 'm')
return 4;
n = inp_numnodes(c);
if ((c == 'm') || (c == 'p') || (c == 'q') || (c == 'd')) {
char *s = nexttok(line);
int gotit = 0;
int i = 0;
while ((i <= n) && (*s) && !gotit) {
char *t = gettok_node(&s);
const wordlist *wl;
for (wl = modnames; wl; wl = wl->wl_next)
if (model_name_match(t, wl->wl_word)) {
gotit = 1;
break;
}
i++;
tfree(t);
}
if ((i < 4) && ((c == 'm') || (c == 'q'))) {
fprintf(cp_err, "Error: too few nodes for MOS or BJT: %s\n", line);
return (0);
}
if ((i < 5) && (c == 'p')) {
fprintf(cp_err, "Error: too few nodes for CPL: %s\n", line);
return (0);
}
return (i-1);
} else {
return (n);
}
}
* This function returns the number of controlling voltage sources
* (for F, H) or controlling nodes (for G, E) attached to a dependent
* source.
*-------------------------------------------------------------------*/
static int
numdevs(char *s)
{
s = skip_ws(s);
switch (*s) {
case 'K':
case 'k':
return (2);
case 'G':
case 'g':
case 'E':
case 'e':
return (2);
case 'F':
case 'f':
case 'H':
case 'h':
case 'W':
case 'w':
return (1);
default:
return (0);
}
}
* modtranslate -- translates .model lines found in subckt definitions.
* Calling arguments are:
* *c = pointer to the .subckt definition (linked list)
* *subname = pointer to the subcircuit name used at the subcircuit invocation (string)
* modtranslate returns the list of model names which have been translated
*----------------------------------------------------------------------*/
static wordlist *
modtranslate(struct card *c, char *subname, wordlist *new_modnames)
{
wordlist *orig_modnames = NULL;
struct card *lcc = c;
for (; c; c = c->nextcard)
if (ciprefix(".model", c->line)) {
char *model_name, *new_model_name;
char *t = c->line;
#ifdef TRACE
printf("modtranslate(), translating:\n"
" \"%s\" -->\n", t);
#endif
t = nexttok(t);
model_name = gettok(&t);
new_model_name = tprintf("%s:%s", subname, model_name);
orig_modnames = wl_cons(model_name, orig_modnames);
new_modnames = wl_cons(new_model_name, new_modnames);
t = tprintf(".model %s %s", new_model_name, t);
tfree(c->line);
c->line = t;
#ifdef TRACE
printf(" \"%s\"\n", t);
printf(" mapped modelname \"%s\" --> \"%s\"\n",
model_name, new_model_name);
#endif
}
if (orig_modnames) {
devmodtranslate(lcc, subname, orig_modnames);
wl_free(orig_modnames);
}
return new_modnames;
}
* Devmodtranslate scans through the deck, and translates the
* name of the model in a line held in a .subckt. For example:
* before: .subckt U1 . . . .
* Q1 c b e 2N3904
* after: Q1 c b e U1:2N3904
*-------------------------------------------------------------------*/
static void
translate_mod_name(struct bxx_buffer *buffer, char *modname, char *subname, struct wordlist *orig_modnames)
{
* Note that we compare against orig_modnames,
* which is the list of untranslated names of models.
*/
wordlist *wlsub = wl_find(modname, orig_modnames);
if (!wlsub)
bxx_printf(buffer, "%s", modname);
else
bxx_printf(buffer, "%s:%s", subname, modname);
}
static void
devmodtranslate(struct card *s, char *subname, wordlist * const orig_modnames)
{
int found;
struct bxx_buffer buffer;
bxx_init(&buffer);
for (; s; s = s->nextcard) {
char *t, c, *name, *next_name;
wordlist *wlsub;
bxx_rewind(&buffer);
t = s->line;
#ifdef TRACE
printf("In devmodtranslate, examining line %s.\n", t);
#endif
t = skip_ws(t);
c = *t;
if (isupper_c(c))
c = tolower_c(c);
switch (c) {
#ifdef XSPICE
case 'a':
* The algorithm is simple. We don't know how many nodes or sources are attached,
* but the name of the model is always last. Therefore, just iterate through all
* tokens until the last one is reached. Then translate it.
*/
#ifdef TRACE
printf("In devmodtranslate, found codemodel, line= %s\n", t);
#endif
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
next_name = gettok(&t);
for (;;) {
name = next_name;
next_name = gettok(&t);
if (next_name == NULL) {
* name holds the model name. Therefore, break */
break;
} else {
bxx_printf(&buffer, "%s ", name);
tfree(name);
}
}
translate_mod_name(&buffer, name, subname, orig_modnames);
tfree(name);
bxx_putc(&buffer, ' ');
#ifdef TRACE
printf("In devmodtranslate, translated codemodel line= %s\n", buffer);
#endif
bxx_put_cstring(&buffer, t);
tfree(s->line);
s->line = copy(bxx_buffer(&buffer));
break;
#endif
case 'r':
case 'c':
case 'l':
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
if (*t) {
name = gettok(&t);
translate_mod_name(&buffer, name, subname, orig_modnames);
tfree(name);
bxx_putc(&buffer, ' ');
}
if (*t) {
name = gettok(&t);
translate_mod_name(&buffer, name, subname, orig_modnames);
tfree(name);
bxx_putc(&buffer, ' ');
}
bxx_put_cstring(&buffer, t);
tfree(s->line);
s->line = copy(bxx_buffer(&buffer));
break;
case 'd':
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
if (name == NULL) {
name = copy("");
} else {
for (;;) {
wlsub = wl_find(name, orig_modnames);
if (wlsub) {
break;
} else {
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok(&t);
if (name == NULL) {
name = copy("");
break;
}
}
}
}
translate_mod_name(&buffer, name, subname, orig_modnames);
tfree(name);
bxx_putc(&buffer, ' ');
bxx_put_cstring(&buffer, t);
tfree(s->line);
s->line = copy(bxx_buffer(&buffer));
break;
#ifdef OSDI
case 'n':
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
if (name == NULL) {
name = copy("");
}
else {
for (;;) {
wlsub = wl_find(name, orig_modnames);
if (wlsub) {
break;
}
else {
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok(&t);
if (name == NULL) {
name = copy("");
break;
}
}
}
}
translate_mod_name(&buffer, name, subname, orig_modnames);
tfree(name);
bxx_putc(&buffer, ' ');
bxx_put_cstring(&buffer, t);
tfree(s->line);
s->line = copy(bxx_buffer(&buffer));
break;
#endif
case 'u':
case 'w':
case 'j':
case 'z':
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok(&t);
translate_mod_name(&buffer, name, subname, orig_modnames);
tfree(name);
bxx_putc(&buffer, ' ');
bxx_put_cstring(&buffer, t);
tfree(s->line);
s->line = copy(bxx_buffer(&buffer));
break;
case 'o':
case 's':
case 'y':
to enable parsing lines like "S1 10 11 (80,51) SLATCH1"
which occur in real Analog Devices SPICE models.
*/
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok(&t);
translate_mod_name(&buffer, name, subname, orig_modnames);
bxx_putc(&buffer, ' ');
bxx_put_cstring(&buffer, t);
tfree(s->line);
s->line = copy(bxx_buffer(&buffer));
tfree(name);
break;
case 'm':
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
if (!name) {
break;
}
found = 0;
while (!found) {
for (wlsub = orig_modnames; wlsub; wlsub = wlsub->wl_next)
if (model_name_match(name, wlsub->wl_word)) {
found = 1;
break;
}
if (!found) {
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
if (name == NULL) {
name = copy("");
break;
}
}
}
if (!found)
bxx_printf(&buffer, "%s", name);
else
bxx_printf(&buffer, "%s:%s", subname, name);
bxx_putc(&buffer, ' ');
bxx_put_cstring(&buffer, t);
tfree(s->line);
s->line = copy(bxx_buffer(&buffer));
tfree(name);
break;
case 'q':
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok_node(&t);
if (name == NULL) {
name = copy("");
} else {
for (;;) {
wlsub = wl_find(name, orig_modnames);
if (wlsub) {
break;
} else {
bxx_printf(&buffer, "%s ", name);
tfree(name);
name = gettok(&t);
if (name == NULL) {
name = copy("");
break;
}
}
}
}
translate_mod_name(&buffer, name, subname, orig_modnames);
tfree(name);
bxx_putc(&buffer, ' ');
bxx_put_cstring(&buffer, t);
tfree(s->line);
s->line = copy(bxx_buffer(&buffer));
break;
case 'p':
name = gettok(&t);
bxx_printf(&buffer, "%s ", name);
tfree(name);
next_name = gettok(&t);
for (;;) {
name = next_name;
next_name = gettok(&t);
if (!next_name || strstr(next_name, "len")) {
* name holds the model name. Therefore, break */
break;
} else {
bxx_printf(&buffer, "%s ", name);
tfree(name);
}
}
translate_mod_name(&buffer, name, subname, orig_modnames);
tfree(name);
bxx_putc(&buffer, ' ');
bxx_put_cstring(&buffer, t);
tfree(s->line);
s->line = copy(bxx_buffer(&buffer));
break;
default:
break;
}
}
bxx_free(&buffer);
}
* inp_numnodes returns the maximum number of nodes (netnames) attached
* to the component.
* This is a spice-dependent thing. It should probably go somewhere
* else, but... Note that we pretend that dependent sources and mutual
* inductors have more nodes than they really do...
*----------------------------------------------------------------------*/
static int
inp_numnodes(char c)
{
if (isupper_c(c))
c = tolower_c(c);
switch (c) {
case ' ':
case '\t':
case '.':
case 'x':
case '*':
case '$':
return (0);
case 'b':
return (2);
case 'c':
return (2);
case 'd':
return (3);
case 'e':
return (2);
case 'f':
return (2);
case 'g':
return (2);
case 'h':
return (2);
case 'i':
return (2);
case 'j':
return (3);
case 'k':
return (0);
case 'l':
return (2);
case 'm':
return (7);
case 'o':
return (4);
case 'p':
return (18);
case 'q':
return (5);
case 'r':
return (2);
case 's':
return (4);
case 't':
return (4);
case 'u':
return (3);
case 'v':
return (2);
case 'w':
return (2);
case 'y':
return (4);
case 'z':
return (3);
default:
fprintf(cp_err, "Warning: unknown device type: %c\n", c);
return (2);
}
}