1 /***************************************************************************
2 JSPICE3 adaptation of Spice3f2 - Copyright (c) Stephen R. Whiteley 1992
3 Copyright 1990 Regents of the University of California.  All rights reserved.
4 Authors: 1985 Wayne A. Christopher
5          1992 Stephen R. Whiteley
6 ****************************************************************************/
7 
8 /*
9  * Interface routines. These are specific to spice. The only changes to FTE
10  * that should be needed to make FTE work with a different simulator is
11  * to rewrite this file. What each routine is expected to do can be
12  * found in the programmer's manual. This file should be the only one
13  * that includes spice header files.
14  */
15 
16 #include "spice.h"
17 #include "ftedefs.h"
18 #include "ftedebug.h"
19 #include "spfteext.h"
20 #include "inpdefs.h"
21 #include "fteinp.h"
22 #include "jobdefs.h"
23 #include "iferrmsg.h"
24 #include "util.h"
25 
26 #ifdef __STDC__
27 static int get_anal(char*);
28 static void set_options(void);
29 static struct variable *parmtovar(IFvalue*,IFparm*);
30 static IFparm * parmlookup(IFdevice*,GENinstance*,GENmodel*,char*);
31 static IFvalue *doask(char*,int,GENinstance*,GENmodel*,IFparm*,int);
32 static int finddev(char*,char*,GENERIC**,GENERIC**);
33 #else
34 static int get_anal();
35 static void set_options();
36 static struct variable *parmtovar();
37 static IFparm * parmlookup();
38 static IFvalue *doask();
39 static int finddev();
40 #endif
41 
42 
43 char *
if_inpdeck(deck,tab)44 if_inpdeck(deck, tab)
45 
46 /* Input a single deck, and return a pointer to the circuit. */
47 struct line *deck;
48 char **tab;
49 {
50     GENERIC *ckt;
51     int err, i, j;
52     struct line *ll;
53     IFuid taskUid;
54     IFuid optUid;
55     int which;
56 
57     for (i = 0, ll = deck; ll; ll = ll->li_next)
58         i++;
59     *tab = (char *)INPtabInit(i);
60     ft_curckt->ci_symtab = *tab;
61     if ((err = (*(ft_sim->newCircuit))(&ckt)) != OK) {
62         ft_sperror(err, "CKTinit");
63         return (NULL);
64     }
65     err =
66         IFnewUid(ckt,&taskUid,(IFuid)NULL,"default",UID_TASK,(GENERIC**)NULL);
67     if (err) {
68         ft_sperror(err,"newUid");
69         return (NULL);
70     }
71     err =
72         (*(ft_sim->newTask))(ckt,(GENERIC**)&(ft_curckt->ci_defTask),taskUid);
73     if (err) {
74         ft_sperror(err,"newTask");
75         return (NULL);
76     }
77     which = get_anal("options");
78     if (which != -1) {
79         err = IFnewUid(ckt,&optUid,(IFuid)NULL,"options",UID_ANALYSIS,
80                 (GENERIC**)NULL);
81         if (err) {
82             ft_sperror(err,"newUid");
83             return (NULL);
84         }
85         err = (*(ft_sim->newAnalysis))(ckt,which,optUid,
86                 (GENERIC**)&(ft_curckt->ci_defOpt),
87                 (GENERIC*)ft_curckt->ci_defTask);
88         if (err) {
89             ft_sperror(err,"createOptions");
90             return (NULL);
91         }
92         ft_curckt->ci_curOpt = ft_curckt->ci_defOpt;
93     }
94     ft_curckt->ci_curTask = ft_curckt->ci_defTask;
95     INPpas1((GENERIC *) ckt, (card *) deck->li_next,(INPtables *)*tab);
96     INPpas2((GENERIC *) ckt, (card *) deck->li_next,
97             (INPtables *) *tab,ft_curckt->ci_defTask);
98     INPkillMods();
99     return (ckt);
100 }
101 
102 
103 int
if_run(what,args)104 if_run(what, args)
105 
106 /* Do a run of the circuit, of the given type. Type "resume" is special --
107  * it means to resume whatever simulation that was in progress. The
108  * return value of this routine is 0 if the exit was ok, and 1 if there was
109  * a reason to interrupt the circuit (interrupt typed at the keyboard,
110  * error in the simulation, etc).
111  * args may be the entire command line,
112  * e.g. "tran 1 10 20 uic",
113  * or only the arguments that follow "what".
114  */
115 char *what;
116 wordlist *args;
117 {
118     GENERIC *ckt = (GENERIC *)ft_curckt->ci_ckt;
119     INPtables *tab = (INPtables *)ft_curckt->ci_symtab;
120     int err;
121     struct line deck;
122     char buf[BSIZE_SP], *tmp;
123     int j;
124     int which;
125     IFuid specUid,optUid;
126 
127     /* First parse the line... */
128     if (eq(what,"sens") || eq(what, "tran") || eq(what, "ac") ||
129         eq(what, "dc")  || eq(what, "op")   || eq(what, "pz") ||
130         eq(what,"disto")|| eq(what,"tf")    || eq(what, "noise")) {
131 
132         if (args) {
133             tmp = wl_flatten(args);
134             if (ciprefix(what,tmp))
135                 (void) sprintf(buf, ".%s", tmp);
136             else
137                 (void) sprintf(buf, ".%s %s", what, tmp);
138             txfree(tmp);
139         }
140         else
141             (void) sprintf(buf, ".%s", what);
142 
143         deck.li_next = deck.li_actual = NULL;
144         deck.li_error = NULL;
145         deck.li_linenum = 0;
146         deck.li_line = buf;
147 
148         if (ft_curckt->ci_specTask) {
149             err = (*(ft_sim->deleteTask))(ckt, ft_curckt->ci_specTask);
150             if (err) {
151                 ft_sperror(err,"deleteTask");
152                 return (2);
153             }
154         }
155         err = IFnewUid(ckt,&specUid,(IFuid)NULL,"special",
156                 UID_TASK,(GENERIC**)NULL);
157         if (err) {
158             ft_sperror(err,"newUid");
159             return (2);
160         }
161         err = (*(ft_sim->newTask))(ckt,
162                 (GENERIC**)&(ft_curckt->ci_specTask),specUid);
163         if (err) {
164             ft_sperror(err,"newTask");
165             return (2);
166         }
167         which = get_anal("options");
168         if (which != -1) {
169             err = IFnewUid(ckt,&optUid,(IFuid)NULL,"options",
170                     UID_ANALYSIS,(GENERIC**)NULL);
171             if (err) {
172                 ft_sperror(err,"newUid");
173                 return (2);
174             }
175             err = (*(ft_sim->newAnalysis))(ckt,which,optUid,
176                     (GENERIC**)&(ft_curckt->ci_specOpt),
177                     (GENERIC*)ft_curckt->ci_specTask);
178             if (err) {
179                 ft_sperror(err,"createOptions");
180                 return (2);
181             }
182             ft_curckt->ci_curOpt = ft_curckt->ci_specOpt;
183             set_options();
184         }
185         ft_curckt->ci_curTask = ft_curckt->ci_specTask;
186 
187         INPpas2(ckt, (card *) &deck, tab, ft_curckt->ci_specTask);
188         if (deck.li_error) {
189             /* INP produdes an E_EXISTS error here... Don't
190              * make this fatal...
191              */
192             fprintf(cp_err, "Warning: %s\n", deck.li_error);
193         }
194     }
195     if ( eq(what,"run") ) {
196         ft_curckt->ci_curTask = ft_curckt->ci_defTask;
197         ft_curckt->ci_curOpt = ft_curckt->ci_defOpt;
198     }
199 
200     if (  (eq(what, "tran"))  ||
201           (eq(what, "ac"))    ||
202           (eq(what, "dc"))    ||
203           (eq(what, "op"))    ||
204           (eq(what, "pz"))    ||
205           (eq(what, "disto")) ||
206           (eq(what, "sens"))  ||
207           (eq(what, "noise")) ||
208           (eq(what, "tf"))    ||
209           (eq(what, "run")) )  {
210         if ((err =
211                 (*(ft_sim->doAnalyses))(ckt, 1, ft_curckt->ci_curTask)) != OK) {
212             if (err == E_PAUSE)
213                  return (1);
214             else {
215                  ft_sperror(err, "doAnalyses");
216                  return (2);
217             }
218         }
219     }
220     else if (eq(what, "resume")) {
221         if ((err =
222                 (*(ft_sim->doAnalyses))(ckt, 0, ft_curckt->ci_curTask)) != OK) {
223             if (err == E_PAUSE)
224                 return (1);
225             else {
226                 ft_sperror(err, "doAnalyses");
227                 return (2);
228             }
229         }
230     }
231     else {
232         fprintf(cp_err, "if_run: Internal Error: bad run type %s\n", what);
233             return (2);
234     }
235     return (0);
236 }
237 
238 
239 static int
get_anal(string)240 get_anal(string)
241 char *string;
242 {
243     int i;
244 
245     for (i = 0; i < ft_sim->numAnalyses; i++) {
246         if (strcmp(ft_sim->analyses[i]->name,string) == 0) {
247             return (i);
248         }
249     }
250     return (-1);
251 }
252 
253 
254 static void
set_options()255 set_options()
256 
257 /* Set the options for the "special" task.  If analysis is initiated with
258  * the "run" command, we use the options found in the .options line only,
259  * which have already been set (so this routine need not be called).
260  * Otherise if an analysis is given, a new task is created.  This routine
261  * is called to first set the options found in the .options line, then
262  * (laboriously) set any options that were entered with the "set" command.
263  * The options set with the "set" command override those set in the
264  * .options line.  Note that if "run" is used, the "set" options are
265  * ignored.
266  */
267 {
268     int i, which, type;
269     char *name, *vv;
270     double f;
271         struct variable *var;
272     struct line *opt;
273 
274 
275     for (opt = ft_curckt->ci_options; opt; opt = opt->li_next) {
276         txfree(opt->li_error);
277         opt->li_error = NULL;
278         INP2dot(ft_curckt->ci_ckt,(INPtables*)ft_curckt->ci_symtab,
279             (card*)opt,NULL,NULL);
280     }
281 
282     /* set the "set" variables */
283     which = get_anal("options");
284     if (which == -1) return;
285 
286     for (i = 0; i < ft_sim->analyses[which]->numParms; i++) {
287         type = ft_sim->analyses[which]->analysisParms[i].dataType;
288         if (type & IF_SET) {
289 
290             name = ft_sim->analyses[which]->analysisParms[i].keyword;
291 
292             if (type & IF_FLAG) {
293                 if (cp_getvar(name,VT_BOOL,(char*)&f))
294                     if_option(ft_curckt->ci_ckt, name, VT_BOOL, (char*)&f);
295             }
296             else if (type & IF_INTEGER) {
297                 if (cp_getvar(name,VT_NUM,(char*)&f))
298                     if_option(ft_curckt->ci_ckt, name, VT_NUM, (char*)&f);
299             }
300             else if (type & IF_REAL) {
301                 if (cp_getvar(name,VT_REAL,(char*)&f))
302                     if_option(ft_curckt->ci_ckt, name, VT_REAL, (char*)&f);
303             }
304             else if (type & IF_STRING) {
305                 if (cp_getvar(name,VT_STRING,(char*)&f))
306                     if_option(ft_curckt->ci_ckt, name, VT_STRING, (char*)&f);
307             }
308         }
309     }
310 }
311 
312 static char *unsupported[] = {
313     "itl3",
314     "itl5",
315     "lvltim",
316     "maxord",
317     "method",
318     NULL
319 } ;
320 
321 static char *obsolete[] = {
322     "limpts",
323     "limtim",
324     "lvlcod",
325     "nomod",
326     NULL
327 } ;
328 
329 
330 void
if_option(ckt,name,type,value)331 if_option(ckt, name, type, value)
332 
333 /* Set an option in the circuit. Arguments are option name, type, and
334  * value (the last a char *), suitable for casting to whatever needed...
335  */
336 char *ckt;
337 char *name;
338 int type;
339 char *value;
340 {
341     IFvalue pval;
342     int err, i;
343     GENERIC *cc = (GENERIC *) ckt;
344     char **vv;
345     int which;
346 
347     if (eq(name, "acct")) {
348         ft_acctprint = true;
349             return;
350     }
351     else if (eq(name, "list")) {
352         ft_listprint = true;
353             return;
354     }
355     else if (eq(name, "node")) {
356         ft_nodesprint = true;
357             return;
358     }
359     else if (eq(name, "opts")) {
360         ft_optsprint = true;
361             return;
362     }
363     else if (eq(name, "nopage")) {
364             ft_nopage = true;
365             return;
366     }
367 
368     which = get_anal("options");
369     if (which == -1) {
370         fprintf(cp_err,"Warning:  .options card unsupported\n");
371         return;
372     }
373 
374     for (i = 0; i < ft_sim->analyses[which]->numParms; i++)
375         if (eq(ft_sim->analyses[which]->analysisParms[i].keyword, name) &&
376             (ft_sim->analyses[which]->analysisParms[i].dataType & IF_SET))
377             break;
378     if (i == ft_sim->analyses[which]->numParms) {
379         /* See if this is unsupported or obsolete. */
380         for (vv = unsupported; *vv; vv++)
381             if (eq(name, *vv)) {
382                 fprintf(cp_err,
383             "Warning: option %s is currently unsupported.\n", name);
384                 return;
385             }
386         for (vv = obsolete; *vv; vv++)
387             if (eq(name, *vv)) {
388                 fprintf(cp_err,
389                 "Warning: option %s is obsolete.\n", name);
390                 return;
391             }
392         return;
393     }
394 
395     switch (ft_sim->analyses[which]->analysisParms[i].dataType & IF_VARTYPES) {
396         case IF_REAL:
397             if (type == VT_REAL)
398                 pval.rValue = *((double *) value);
399             else if (type == VT_NUM)
400                 pval.rValue = *((int *) value);
401             else
402                 goto badtype;
403             break;
404         case IF_INTEGER:
405             if (type == VT_NUM)
406                 pval.iValue = *((int *) value);
407             else if (type == VT_REAL)
408                 pval.iValue = *((double *) value);
409             else
410                 goto badtype;
411             break;
412         case IF_STRING:
413             if (type == VT_STRING)
414                 pval.sValue = copy(value);
415             else
416                 goto badtype;
417             break;
418         case IF_FLAG:
419             /* Do nothing. */
420             pval.iValue = 1;
421             break;
422         default:
423             fprintf(cp_err,
424             "if_option: Internal Error: bad option type %d.\n",
425                     ft_sim->analyses[which]->analysisParms[i].dataType);
426     }
427 
428     if ((err = (*(ft_sim->setAnalysisParm))(cc, (GENERIC *)ft_curckt->ci_curOpt,
429             ft_sim->analyses[which]->analysisParms[i].id, &pval,
430             (IFvalue *)NULL)) != OK)
431         ft_sperror(err, "setAnalysisParm(options)");
432 
433     return;
434 
435 badtype:
436     fprintf(cp_err, "Error: bad type given for option %s --\n", name);
437     fprintf(cp_err, "\ttype given was ");
438     switch (type) {
439         case VT_BOOL:   fputs("boolean", cp_err); break;
440         case VT_NUM:    fputs("integer", cp_err); break;
441         case VT_REAL:   fputs("real", cp_err); break;
442         case VT_STRING: fputs("string", cp_err); break;
443         case VT_LIST:   fputs("list", cp_err); break;
444         default:        fputs("something strange", cp_err); break;
445     }
446     fprintf(cp_err, ", type expected was ");
447     switch(ft_sim->analyses[which]->analysisParms[i].dataType & IF_VARTYPES) {
448         case IF_REAL:   fputs("real.\n", cp_err); break;
449         case IF_INTEGER:fputs("integer.\n", cp_err); break;
450         case IF_STRING: fputs("string.\n", cp_err); break;
451         case IF_FLAG:   fputs("flag.\n", cp_err); break;
452         default:        fputs("something strange.\n", cp_err); break;
453     }
454     if (type == VT_BOOL)
455 fputs("\t(Note that you must use an = to separate option name and value.)\n",
456                     cp_err);
457     return;
458 }
459 
460 
461 void
if_cktfree(ckt,tab)462 if_cktfree(ckt, tab)
463 
464 char *ckt;
465 char *tab;
466 {
467     GENERIC *cc = (GENERIC *) ckt;
468 
469     (*(ft_sim->deleteCircuit))(cc);
470     INPtabEnd((INPtables *) tab);
471     return;
472 }
473 
474 
475 void
if_cktclear()476 if_cktclear()
477 
478 /* Delete the current circuit.
479  * This blows away all the peripheral stuff, unlike if_cktfree() which only
480  * frees the ckt structure and input tables.
481  */
482 {
483     struct circ *cc;
484 
485     if (!ft_curckt) return;
486     tfree(ft_curckt->ci_name);
487     inp_deckfree(ft_curckt->ci_deck);
488     inp_deckfree(ft_curckt->ci_options);
489     wl_free(ft_curckt->ci_commands);
490 
491     (*(ft_sim->deleteTask))(ft_curckt->ci_ckt,ft_curckt->ci_specTask);
492     (*(ft_sim->deleteTask))(ft_curckt->ci_ckt,ft_curckt->ci_defTask);
493     va_free(ft_curckt->ci_vars);
494 
495     tfree(ft_curckt->ci_filename);
496 
497     if (ft_curckt->ci_contblk) {
498         for (cc = ft_circuits; cc; cc = cc->ci_next) {
499             if (cc == ft_curckt)
500                 continue;
501             if (cc->ci_contblk && eq(cc->ci_contblk,ft_curckt->ci_contblk))
502                 break;
503         }
504         if (!cc) {
505             if (prefix("#_",ft_curckt->ci_contblk)) {
506                 cp_freeblock(ft_curckt->ci_contblk);
507                 ft_curckt->ci_contblk[1] = '#';
508                 cp_freeblock(ft_curckt->ci_contblk);
509             }
510         }
511         txfree(ft_curckt->ci_contblk);
512     }
513 
514     if_cktfree(ft_curckt->ci_ckt, (char*)ft_curckt->ci_symtab);
515     ft_curckt->ci_devices = cp_kwswitch(CT_DEVNAMES,(char *)NULL);
516     ft_curckt->ci_nodes = cp_kwswitch(CT_NODENAMES,(char *)NULL);
517     cp_ccfreetrie(ft_curckt->ci_devices);
518     cp_ccfreetrie(ft_curckt->ci_nodes);
519 
520     if (ft_curckt == ft_circuits) {
521         tfree(ft_curckt);
522         ft_circuits = ft_curckt = ft_circuits->ci_next;
523     }
524     else {
525         for (cc = ft_circuits; cc; cc = cc->ci_next)
526             if (cc->ci_next == ft_curckt)
527                 break;
528 
529         if (!cc) {
530             fprintf(cp_err,"Internal Error: cktfree -- not in list\n");
531             goto xxx;
532         }
533         cc->ci_next = ft_curckt->ci_next;
534         tfree(ft_curckt);
535         ft_curckt = cc;
536     }
537 xxx:
538     if (ft_curckt) {
539         (void) cp_kwswitch(CT_DEVNAMES, ft_curckt->ci_devices);
540         (void) cp_kwswitch(CT_NODENAMES, ft_curckt->ci_nodes);
541     }
542 }
543 
544 /* Return a string describing an error code. */
545 
546 
547 /* BLOW THIS AWAY.... */
548 
549 char *
if_errstring(code)550 if_errstring(code)
551 
552 int code;
553 {
554     return (INPerror(code));
555 }
556 
557 
558 struct variable *
spif_getparam(ckt,name,param,ind,keyword)559 spif_getparam(ckt, name, param, ind, keyword)
560 
561 /* Get a parameter value from the circuit. If name is left unspecified,
562  * we want a circuit parameter.  If keywords is not null, a list of
563  * matching keywords is built.
564  */
565 char *ckt;
566 char **name;
567 char *param;
568 int ind;
569 wordlist **keyword;
570 {
571     struct variable *vv, *tv;
572     IFvalue *pv;
573     IFparm *opt;
574     int typecode, i, num;
575     GENinstance *dev = (GENinstance *)NULL;
576     GENmodel *mod = (GENmodel *)NULL;
577     IFdevice *device;
578     wordlist *ww;
579 
580     if (param && eq(param, "all")) {
581         INPinsert(name,(INPtables *)ft_curckt->ci_symtab);
582         typecode = finddev(ckt, *name,(GENERIC**) &dev,(GENERIC**) &mod);
583         if (typecode == -1) {
584             fprintf(cp_err,
585                 "Error: no such device or model name %s\n", *name);
586             return (NULL);
587         }
588         device = ft_sim->devices[typecode];
589         vv = tv = (struct variable*)NULL;
590         if (dev) {
591             num = *(device->numInstanceParms);
592             opt = device->instanceParms;
593         }
594         else {
595             num = *(device->numModelParms);
596             opt = device->modelParms;
597         }
598         if (opt) {
599             for (i = 0; i < num; i++, opt++) {
600                 if (!(opt->dataType & IF_ASK)) continue;
601                 pv = doask(ckt, typecode, dev, mod, opt, ind);
602                 if (pv) {
603                     if (tv) {
604                         if (keyword) {
605                              ww->wl_next = alloc(struct wordlist);
606                              ww = ww->wl_next;
607                              ww->wl_word = copy(opt->keyword);
608                         }
609                         tv->va_next = parmtovar(pv,opt);
610                         tv = tv->va_next;
611                     }
612                     else {
613                         if (keyword) {
614                             *keyword = ww = alloc(struct wordlist);
615                             ww->wl_word = copy(opt->keyword);
616                         }
617                         vv = tv = parmtovar(pv,opt);
618                     }
619                 }
620                 else
621                     fprintf(cp_err,
622                         "Internal Error: no parameter '%s' on device '%s'\n",
623                         device->instanceParms[i].keyword,
624                         device->name);
625             }
626             return (vv);
627         }
628         else
629             return (NULL);
630     }
631     else if (param) {
632         INPinsert(name,(INPtables *)ft_curckt->ci_symtab);
633         typecode = finddev(ckt, *name, (GENERIC**)&dev, (GENERIC**)&mod);
634         if (typecode == -1) {
635             fprintf(cp_err,
636                 "Error: no such device or model name %s\n", *name);
637             return (NULL);
638         }
639         device = ft_sim->devices[typecode];
640         opt = parmlookup(device, dev, mod, param);
641         if (!opt) {
642             fprintf(cp_err, "Error: no such parameter %s.\n", param);
643             return (NULL);
644         }
645         pv = doask(ckt, typecode, dev, mod, opt, ind);
646         if (pv) {
647             if (keyword) {
648                 *keyword = alloc(struct wordlist);
649                 (*keyword)->wl_word = copy(opt->keyword);
650             }
651             vv = parmtovar(pv, opt);
652         }
653         return (vv);
654     }
655     else
656         return (if_getstat(ckt, *name, keyword));
657 }
658 
659 
660 /* ARGSUSED */
661 void
if_setparam(ckt,name,param,val)662 if_setparam(ckt, name, param, val)
663 
664 char *ckt;
665 char *name;
666 char *param;
667 struct variable *val;
668 {
669 }
670 
671 
672 static struct variable *
parmtovar(pv,opt)673 parmtovar(pv, opt)
674 
675 IFvalue *pv;
676 IFparm *opt;
677 {
678     struct variable *tv, *vv = alloc(struct variable);
679     int i = 0;
680 
681     switch (opt->dataType & IF_VARTYPES) {
682         case IF_INTEGER:
683             vv->va_type = VT_NUM;
684             vv->va_num = pv->iValue;
685             break;
686         case IF_REAL:
687         case IF_COMPLEX:
688             vv->va_type = VT_REAL;
689             vv->va_real = pv->rValue;
690             break;
691         case IF_STRING:
692             vv->va_type = VT_STRING;
693             vv->va_string = copy(pv->sValue);
694             break;
695         case IF_INSTANCE:
696             vv->va_type = VT_STRING;
697             vv->va_string = copy((char*)pv->uValue);
698             break;
699         case IF_FLAG:
700             vv->va_type = VT_BOOL;
701             vv->va_bool = pv->iValue ? true : false;
702             break;
703         case IF_REALVEC:
704             vv->va_type = VT_LIST;
705             tv = NULL;
706             for (i = 0; i < pv->v.numValue; i++) {
707                 if (!tv)
708                     tv = vv->va_vlist = alloc(struct variable);
709                 else {
710                     tv->va_next = alloc(struct variable);
711                     tv = tv->va_next;
712                 }
713                 tv->va_type = VT_REAL;
714                 tv->va_real = (pv->v.vec.rVec)[i];
715             }
716             break;
717         case IF_PARSETREE:
718             vv->va_type = VT_STRING;
719             vv->va_string = NULL;
720             if (pv->tValue)
721                 vv->va_string = copy(pv->tValue->line);
722             break;
723         default:
724             fprintf(cp_err,
725             "parmtovar: Internal Error: bad PARM type %d.\n",
726                     opt->dataType);
727             tfree(vv);
728             return (NULL);
729     }
730 
731     /* It's not clear whether we want the keyword or the desc here... */
732     vv->va_name = copy(opt->description);
733     return (vv);
734 }
735 
736 
737 static IFparm *
parmlookup(device,dev,mod,param)738 parmlookup(device, dev, mod, param)
739 
740 /* Extract the IFparm structure from the device.  A match is assumed if
741  * param is a case insensitive match of the IFdevice keyword.  If inptr
742  * is not NULL, the devices are checked first.  *inptr is NULLed if
743  * no matching device is found, and a model is returned if there is a
744  * match.
745  */
746 IFdevice *device;
747 GENinstance *dev;
748 GENmodel *mod;
749 char *param;
750 {
751     int i;
752 
753     if (dev) {
754         for (i = 0; i < *(device->numInstanceParms); i++) {
755             if ((device->instanceParms[i].dataType & IF_ASK) &&
756                     cieq(param,device->instanceParms[i].keyword))
757                 return (&device->instanceParms[i]);
758         }
759     }
760     else if (mod) {
761         if (device->modelParms == NULL)
762             return (NULL);
763         for (i = 0; i < *(device->numModelParms); i++)
764             if ((device->modelParms[i].dataType & IF_ASK) &&
765                     cieq(param,device->modelParms[i].keyword))
766                 return (&device->modelParms[i]);
767     }
768     return (NULL);
769 }
770 
771 
772 /* ARGSUSED */
773 static IFvalue *
doask(ckt,typecode,dev,mod,opt,ind)774 doask(ckt, typecode, dev, mod, opt, ind)
775 
776 /* Perform the CKTask call. We have both 'fast' and 'modfast', so the other
777  * parameters aren't necessary.
778  */
779 char *ckt;
780 GENinstance *dev;
781 GENmodel *mod;
782 IFparm *opt;
783 int ind;
784 {
785     static IFvalue pv;
786     int err;
787 
788     pv.rValue = 0.0;
789     pv.iValue = ind;    /* Sometimes this will be junk and ignored... */
790 
791     if (dev)
792         err = (*(ft_sim->askInstanceQuest))((GENERIC *)ckt, (GENERIC *)dev,
793                 opt->id, &pv, (IFvalue *)NULL);
794     else
795         err = (*(ft_sim->askModelQuest))((GENERIC*)ckt, (GENERIC*) mod,
796                 opt->id, &pv, (IFvalue *)NULL);
797     if (err != OK) {
798         ft_sperror(err, "doask");
799         return (NULL);
800     }
801 
802     return (&pv);
803 }
804 
805 
806 static int
finddev(ck,name,devptr,modptr)807 finddev(ck, name, devptr, modptr)
808 
809 /* Get pointers to a device, its model, and its type number given the name.
810  * If there is no such device, try to find a model with that name.
811  */
812 char *ck;
813 char *name;
814 GENERIC **devptr;
815 GENERIC **modptr;
816 {
817     int err;
818     int type = -1;
819 
820     err =
821         (*(ft_sim->findInstance))((GENERIC *)ck,&type,devptr,name,NULL,NULL);
822     if (err == OK)
823         return (type);
824 
825     type = -1;
826     *devptr = (GENERIC *)NULL;
827 
828     err = (*(ft_sim->findModel))((GENERIC *)ck,&type,modptr,name);
829     if (err == OK)
830         return(type);
831 
832     *modptr = (GENERIC *)NULL;
833     return (-1);
834 }
835 
836 
837 /* Extract the node and device names from the line and add them to the command
838  * completion structure.  This is probably not a good thing to do if it
839  * takes too much time.
840  */
841 
842 void
if_setndnames(line)843 if_setndnames(line)
844 
845 char *line;
846 {
847     int i;
848     char buf[BSIZE_SP];
849 
850     while (isspace(*line))
851         line++;
852 
853     if (!*line || (*line == '*'))
854         return;
855     if (*line == '.') {
856         line++;
857         if (prefix("model",line)) {
858             copytok(buf,&line);
859             copytok(buf,&line);
860             cp_addkword(CT_DEVNAMES, buf);
861         }
862         return;
863     }
864     copytok(buf,&line);
865     cp_addkword(CT_DEVNAMES, buf);
866 
867 /* inefficient and not very useful */
868 /*
869     if (!(i = inp_numnodes(*buf)))
870         return;
871     if ((*buf == 'q') || (*buf == 'Q'))
872         i = 3;
873 
874     while (i-- > 0) {
875         if (copytok(buf,&line))
876             cp_addkword(CT_NODENAMES, buf);
877     }
878     return;
879 */
880 }
881 
882 
883 struct variable *
if_getstat(ckt,name,keyword)884 if_getstat(ckt, name, keyword)
885 
886 /* Get the statistic called 'name'.  If this is NULL get all statistics
887  * available.  If keyword is not null, it returns a wordlist of actual
888  * keyword matches.  A match is assumed if name is a case insensitive
889  * prefix of the keyword.
890  */
891 char *ckt;
892 char *name;
893 wordlist **keyword;
894 {
895     int i;
896     struct variable *v, *vars;
897     IFvalue parm;
898     int which;
899     wordlist *ww;
900 
901     which = get_anal("options");
902     if (which == -1) {
903         fprintf(cp_err,"Warning:  statistics unsupported\n");
904         return(NULL);
905     }
906 
907     if (name) {
908         for (i = 0; i < ft_sim->analyses[which]->numParms; i++)
909             if (ciprefix(name,
910                 ft_sim->analyses[which]->analysisParms[i].keyword))
911                 break;
912         if (i == ft_sim->analyses[which]->numParms)
913             return (NULL);
914         if ((*(ft_sim->askAnalysisQuest))(ckt, ft_curckt->ci_curTask,
915                 ft_sim->analyses[which]->analysisParms[i].id, &parm,
916                 (IFvalue *)NULL) == -1) {
917             goto error;
918         }
919         if (keyword) {
920             *keyword = alloc(struct wordlist);
921             (*keyword)->wl_word = copy(
922                 ft_sim->analyses[which]->analysisParms[i].keyword);
923         }
924         return (parmtovar(&parm,
925             &(ft_sim->analyses[which]->analysisParms[i])));
926     }
927     else {
928         for (i = 0, vars = v = NULL;
929                 i < ft_sim->analyses[which]->numParms; i++) {
930             if (!(ft_sim->analyses[which]->analysisParms[i].dataType&IF_ASK)) {
931                 continue;
932             }
933             if ((*(ft_sim->askAnalysisQuest))(ckt, ft_curckt->ci_curTask,
934                     ft_sim->analyses[which]->analysisParms[i].id,
935                     &parm, (IFvalue *)NULL) == -1) {
936                 goto error;
937             }
938             if (v) {
939                 if (keyword) {
940                     ww->wl_next = alloc(struct wordlist);
941                     ww = ww->wl_next;
942                     ww->wl_word = copy(
943                         ft_sim->analyses[which]->analysisParms[i].keyword);
944                 }
945                 v->va_next = parmtovar(&parm,
946                         &(ft_sim->analyses[which]->analysisParms[i]));
947                 v = v->va_next;
948             }
949             else {
950                 if (keyword) {
951                     *keyword = ww = alloc(struct wordlist);
952                     ww->wl_word = copy(
953                         ft_sim->analyses[which]->analysisParms[i].keyword);
954                 }
955                 vars = v = parmtovar(&parm,
956                     &(ft_sim->analyses[which]->analysisParms[i]));
957             }
958         }
959         return (vars);
960     }
961 error:
962         fprintf(cp_err,"if_getstat: Internal Error: can't get %s.\n",name);
963         return NULL;
964 }
965 
966 
967 wordlist *
GetAnalysisFromDeck()968 GetAnalysisFromDeck()
969 {
970     wordlist *tl1 = NULL;
971     int numjobs;
972 
973     if (!(*ft_sim->findAnalysis)(ft_curckt->ci_ckt,&numjobs,
974         (GENERIC**)NULL,(IFuid)NULL,ft_curckt->ci_defTask,(IFuid)NULL)) {
975 
976         if (numjobs != 1)
977             return (NULL);
978 
979         /* one analysis specified in file */
980         ft_curckt->ci_curTask = ft_curckt->ci_defTask;
981         ft_curckt->ci_curOpt = ft_curckt->ci_defOpt;
982         tl1 = alloc(wordlist);
983         tl1->wl_word = copy("run");
984     }
985     return (tl1);
986 }
987 
988 
989 int
InProgress()990 InProgress()
991 {
992     return (ft_curckt && ft_curckt->ci_inprogress);
993 }
994 
995 
996 int
IsIplot()997 IsIplot()
998 {
999     struct dbcomm *d;
1000     extern struct dbcomm *dbiplot;
1001 
1002     for (d = dbiplot; d; d = d->db_next) {
1003         if (d->db_type == DB_IPLOT) return (true);
1004         if (d->db_type == DB_IPLOTALL) return (true);
1005     }
1006     return (false);
1007 }
1008