From 629d1f5462a6f05e74b17fd1b22e30115d85be00 Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Wed, 26 Feb 2025 09:04:42 +0000 Subject: [PATCH] Fix a bug where a node name is mis-identified as the model for an OSDI device and remove a limit on node count. Problem was reported by user Sam in ngspice-users. --- src/frontend/inpcom.c | 28 ++++----- src/spicelib/parser/inp2n.c | 109 +++++++++++++++++++++--------------- 2 files changed, 73 insertions(+), 64 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 73fd4aa3e..c60a16d6c 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -5248,9 +5248,7 @@ int get_number_terminals(char *c) return i - 2; break; } - case 'p': /* recognition of up to 100 cpl nodes */ - /* find the last token in the line*/ - + case 'p': /* Recognition of up to 100 cpl nodes */ for (i = j = 0; (i < 100) && (*c != '\0'); ++i) { inst = gettok_instance(&c); if (strchr(inst, '=')) @@ -5326,27 +5324,21 @@ int get_number_terminals(char *c) break; } #ifdef OSDI - case 'n': /* Recognize an unknown number of nodes by stopping at tokens with '=' */ - { - char* cc, * ccfree; - - cc = copy(c); - /* required to make m= 1 a single token m=1 */ - ccfree = cc = inp_remove_ws(cc); + case 'n': + /* Find the last non-parameter token in the line. */ - /* Find the first token with "off", "tnodeout", "thermal" or "=" */ - for (i = 0; (i < 20) && (*cc != '\0'); ++i) { - inst = gettok_instance(&cc); - if (i > 2 && (strchr(inst, '='))) { - txfree(inst); + for (i = 0; *c != '\0' && *c != '='; ++i) { + inst = gettok_instance(&c); + if (strchr(inst, '=')) { + tfree(inst); break; } - txfree(inst); + tfree(inst); } - tfree(ccfree); + if (*c == '=') + --i; // Counted a parameter name. return i - 2; break; - } #endif default: return 0; diff --git a/src/spicelib/parser/inp2n.c b/src/spicelib/parser/inp2n.c index f02c33bf9..526455024 100644 --- a/src/spicelib/parser/inp2n.c +++ b/src/spicelib/parser/inp2n.c @@ -22,79 +22,96 @@ void INP2N(CKTcircuit *ckt, INPtables *tab, struct card *current) { * [IC=,,] */ - int type; /* the type the model says it is */ - char *line; /* the part of the current line left to parse */ - char *name; /* the resistor's name */ - // limit to at most 20 nodes - const int max_i = 20; - CKTnode *node[20]; - int error; /* error code temporary */ - int numnodes; /* flag indicating 4 or 5 (or 6 or 7) nodes */ - GENinstance *fast; /* pointer to the actual instance */ - int waslead; /* flag to indicate that funny unlabeled number was found */ - double leadval; /* actual value of unlabeled number */ - INPmodel *thismodel; /* pointer to model description for user's model */ - GENmodel *mdfast; /* pointer to the actual model */ - int i; + int type; /* Model type. */ + char *line; /* Unparsed part of the current line. */ + char *name; /* Device instance name. */ + int error; /* Temporary error code. */ + int numnodes; /* Flag indicating 4 or 5 (or 6 or 7) nodes. */ + GENinstance *fast; /* Pointer to the actual instance. */ + int waslead; /* Funny unlabeled number was found. */ + double leadval; /* Value of unlabeled number. */ + INPmodel *thismodel; /* Pointer to model description for user's model. */ + GENmodel *mdfast; /* Pointer to the actual model. */ + IFdevice *dev; + CKTnode *node; + char *c, *token = NULL, *prev = NULL, *pprev = NULL; + int i; line = current->line; - INPgetNetTok(&line, &name, 1); INPinsert(&name, tab); - for (i = 0;; i++) { - char *token; - INPgetNetTok(&line, &token, 1); + /* Find the last non-parameter token in the line. */ - /* We have single terminal Verilog-A modules */ - if (i >= 1) { - txfree(INPgetMod(ckt, token, &thismodel, tab)); + c = line; + for (i = 0; *c != '\0'; ++i) { + tfree(pprev); + pprev = prev; + prev = token; + token = gettok_instance(&c); + if (strchr(token, '=')) + break; + } + if (*c) { + tfree(token); // A parameter or starts with '='. + if (*c == '=') { + /* Now prev points to a parameter pprev is the model. */ + + --i; + token = pprev; + tfree(prev); + } else { + token = prev; + tfree(pprev); + } + } - /* /1* check if using model binning -- pass in line since need 'l' and 'w' *1/ */ - /* if (!thismodel) */ - /* txfree(INPgetModBin(ckt, token, &thismodel, tab, line)); */ + /* We have single terminal Verilog-A modules */ - if (thismodel) { - INPinsert(&token, tab); - break; + if (i >= 2) { + c = INPgetMod(ckt, token, &thismodel, tab); + if (c) { + LITERR(c); + tfree(c); + tfree(token); + return; } - } - if (i >= max_i) { + } + tfree(token); + if (i < 2 || !thismodel) { LITERR("could not find a valid modelname"); return; - } - INPtermInsert(ckt, &token, tab, &node[i]); } - type = thismodel->INPmodType; mdfast = thismodel->INPmodfast; - IFdevice *dev = ft_sim->devices[type]; + dev = ft_sim->devices[type]; if (!dev->registry_entry) { LITERR("incorrect model type! Expected OSDI device"); return; } - if (i == 0) { - LITERR("not enough nodes"); - return; - } - - if (i > *dev->terms) { + numnodes = i - 1; + if (numnodes > *dev->terms) { LITERR("too many nodes connected to instance"); return; } - numnodes = i; - IFC(newInstance, (ckt, mdfast, &fast, name)); - for (i = 0; i < *dev->terms; i++) - if (i < numnodes) - IFC(bindNode, (ckt, fast, i + 1, node[i])); - else - GENnode(fast)[i] = -1; + /* Rescan to process nodes. */ + for (i = 0; i < *dev->terms; i++) { + if (i < numnodes) { + token = gettok_instance(&line); + INPtermInsert(ckt, &token, tab, &node); // Consumes token + IFC(bindNode, (ckt, fast, i + 1, node)); + } else { + GENnode(fast)[i] = -1; + } + } + token = gettok_instance(&line); // Eat model name. + tfree(token); PARSECALL((&line, ckt, type, fast, &leadval, &waslead, tab)); if (waslead) LITERR(" error: no unlabeled parameter permitted on osdi devices\n");