1 /***************************************************************************
2 JSPICE3 adaptation of Spice3e2 - 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  * Stuff for dealing with spice input decks and command scripts, and
10  * the listing routines.
11  */
12 
13 #include "spice.h"
14 #ifdef HAVE_STAT
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #endif
18 #include "ftedefs.h"
19 #include "inpdefs.h"
20 #include "fteinp.h"
21 #include "spfteext.h"
22 
23 #ifdef __STDC__
24 static char *upper(char*);
25 static void spsource(FILE*,bool,bool,char*);
26 static void decksource(struct line*,bool,bool,char*);
27 static bool is_ckt(struct line*);
28 static wordlist *get_speccmds(struct line*);
29 static wordlist *get_controls(struct line*,bool);
30 static void varsub(char**);
31 static wordlist *wl_separate(char*);
32 static char *flatten(wordlist*);
33 #else
34 static char *upper();
35 static void spsource();
36 static void decksource();
37 static bool is_ckt();
38 static wordlist *get_speccmds();
39 static wordlist *get_controls();
40 static void varsub();
41 static wordlist *wl_separate();
42 static char *flatten();
43 #endif
44 
45 
46 /* Do a listing. Use is listing [expanded] [logical] [physical] [deck] */
47 
48 void
com_listing(wl)49 com_listing(wl)
50 
51 wordlist *wl;
52 {
53     int type = LS_LOGICAL;
54     bool expand = false;
55     char *s;
56 
57     if (ft_curckt) {
58         while (wl) {
59             s = wl->wl_word;
60             switch (*s) {
61                 case 'l':
62                 case 'L':
63                     type = LS_LOGICAL;
64                     break;
65                 case 'p':
66                 case 'P':
67                     type = LS_PHYSICAL;
68                     break;
69                 case 'd':
70                 case 'D':
71                     type = LS_DECK;
72                     break;
73                 case 'e':
74                 case 'E':
75                     expand = true;
76                     break;
77                 default:
78                     fprintf(cp_err,
79                     "Error: bad listing type %s\n", s);
80             }
81             wl = wl->wl_next;
82         }
83         inp_list(cp_out, expand ? ft_curckt->ci_deck :
84                 ft_curckt->ci_origdeck, ft_curckt->ci_options,
85                 type);
86     }
87     else
88         fprintf(cp_err, "Error: no circuit loaded.\n");
89     return;
90 }
91 
92 
93 static char *
upper(string)94 upper(string)
95 
96 register char *string;
97 {
98 
99     static char buf[BSIZE_SP];
100     register char *s;
101 
102     if (string) {
103         strncpy(buf, string, BSIZE_SP - 1);
104         buf[BSIZE_SP - 1] = 0;
105         inp_casefix(buf);
106     }
107     else {
108         strcpy(buf, "<null>");
109     }
110 
111     return buf;
112 
113 }
114 
115 
116 /* Provide an input listing on the specified file of the given
117  * card deck.  The listing should be of either LS_PHYSICAL or LS_LOGICAL
118  * or LS_DECK lines as specified by the type parameter.
119  */
120 
121 void
inp_list(file,deck,extras,type)122 inp_list(file, deck, extras, type)
123 
124 FILE *file;
125 struct line *deck, *extras;
126 int type;
127 {
128     struct line *here;
129     struct line *there;
130     struct line *tmp, *next;
131     bool renumber;
132     bool useout = (file == cp_out);
133     int i = 1;
134     extern char *kw_renumber;
135 
136     if (useout)
137         out_init();
138 
139     if (type != LS_DECK)
140         if (useout) {
141             out_printf("\t%s\n", ft_curckt->ci_name);
142             if (ft_curckt->ci_contblk)
143                 out_printf("\tBound codeblock: %s\n\n",ft_curckt->ci_contblk);
144             else
145                 out_send("\n");
146         }
147         else {
148             fprintf(cp_out, "\t%s\n", ft_curckt->ci_name);
149             if (ft_curckt->ci_contblk)
150                 fprintf(cp_out,
151                     "\tBound codeblock: %s\n\n",ft_curckt->ci_contblk);
152             else
153                 fputc('\n',cp_out);
154         }
155 
156 
157     (void) cp_getvar(kw_renumber, VT_BOOL, (char *) &renumber);
158     if (type == LS_LOGICAL) {
159 top1:
160         for (here = deck; here; here = here->li_next) {
161             if (renumber)
162                 here->li_linenum = i;
163             i++;
164             if (ciprefix(".end", here->li_line) &&
165                     !isalpha(here->li_line[4]))
166                 continue;
167             if (*here->li_line != '*') {
168                 if (useout) {
169                     out_printf("%6d : %s\n",
170                             here->li_linenum,
171                             upper(here->li_line));
172                 }
173                 else
174                     fprintf(file, "%6d : %s\n",
175                             here->li_linenum,
176                             upper(here->li_line));
177                 if (here->li_error) {
178                     if (useout) {
179                         out_printf("%s\n",
180                                 here->li_error);
181                     }
182                     else
183                         fprintf(file, "%s\n",
184                             here->li_error, file);
185                 }
186             }
187         }
188         if (extras) {
189             deck = extras;
190             extras = NULL;
191             goto top1;
192         }
193         if (useout) {
194             out_printf("%6d : .end\n", i);
195         }
196         else
197             fprintf(file, "%6d : .end\n", i);
198     }
199     else if ((type == LS_PHYSICAL) || (type == LS_DECK)) {
200 top2:
201         for (here = deck; here; here = here->li_next) {
202             if ((here->li_actual == NULL) || (here == deck)) {
203                 if (renumber)
204                     here->li_linenum = i;
205                 i++;
206                 if (ciprefix(".end", here->li_line) &&
207                         !isalpha(here->li_line[4]))
208                     continue;
209                 if (type == LS_PHYSICAL) {
210                     if (useout) {
211                         out_printf("%6d : %s\n",
212                             here->li_linenum,
213                             upper(here->li_line));
214                     }
215                     else
216                         fprintf(file, "%6d : %s\n",
217                             here->li_linenum,
218                             upper(here->li_line));
219                 }
220                 else {
221                     if (useout)
222                         out_printf("%s\n",
223                             upper(here->li_line));
224                     else
225                         fprintf(file, "%s\n",
226                             upper(here->li_line));
227                 }
228                 if (here->li_error && (type == LS_PHYSICAL)) {
229                     if (useout)
230                         out_printf("%s\n",
231                             here->li_error);
232                     else
233                         fprintf(file, "%s\n",
234                             here->li_error);
235                 }
236             }
237             else {
238                 for (there = here->li_actual; there;
239                         there = there->li_next) {
240                     there->li_linenum = i++;
241                     if (ciprefix(".end", here->li_line) &&
242                             isalpha(here->li_line[4]))
243                         continue;
244                     if (type == LS_PHYSICAL) {
245                         if (useout) {
246                             out_printf("%6d : %s\n",
247                                 there->li_linenum,
248                                 upper(there->li_line));
249                         }
250                         else
251                             fprintf(file, "%6d : %s\n",
252                                 there->li_linenum,
253                                 upper(there->li_line));
254                     }
255                     else {
256                         if (useout)
257                             out_printf("%s\n",
258                                 upper(there->li_line));
259                         else
260                             fprintf(file, "%s\n",
261                                 upper(there->li_line));
262                     }
263                     if (there->li_error &&
264                             (type == LS_PHYSICAL)) {
265                         if (useout)
266                             out_printf("%s\n",
267                             there->li_error);
268                         else
269                             fprintf(file, "%s\n",
270                             there->li_error);
271                     }
272                 }
273                 here->li_linenum = i;
274             }
275         }
276         if (extras) {
277             deck = extras;
278             extras = NULL;
279             goto top2;
280         }
281         if (type == LS_PHYSICAL) {
282             if (useout) {
283                 out_printf("%6d : .end\n", i);
284             }
285             else
286                 fprintf(file, "%6d : .end\n", i);
287         }
288         else {
289             if (useout)
290                 out_printf(".end\n");
291             else
292                 fprintf(file, ".end\n");
293         }
294     }
295     else
296         fprintf(cp_err, "inp_list: Internal Error: bad type %d\n",type);
297     return;
298 }
299 
300 
301 /* The routine to source a spice input deck. We read the deck in, take out
302  * the front-end commands, and create a CKT structure. Also we filter out
303  * the following cards: .save, .width, .four, .print, and .plot, to perform
304  * after the run is over.
305  */
306 
307 void
inp_spsource(fp,comfile,filename)308 inp_spsource(fp, comfile, filename)
309 
310 FILE *fp;
311 bool comfile;
312 char *filename;
313 {
314     spsource(fp, comfile, comfile, filename);
315 }
316 
317 
318 static void
spsource(fp,nospice,nocmds,filename)319 spsource(fp, nospice, nocmds, filename)
320 
321 FILE *fp;
322 bool nospice, nocmds;
323 char *filename;
324 {
325     struct line *deck;
326     wordlist wl;
327     char *buf;
328 
329     if ((buf = readline(fp)) == NULL)
330         return;
331     if (prefix("(Symbo",buf)) {
332         /* file contains symbolic layout information */
333         txfree(buf);
334         while ((buf = readline(fp)) != NULL) {
335             if (eq(buf,"E\n")) {
336                 txfree(buf);
337                 break;
338             }
339             txfree(buf);
340         }
341         if ((buf = readline(fp)) == NULL)
342             return;
343     }
344     if (prefix(".check",buf)) {
345         /* operating range analysis file */
346         wl.wl_word = filename;
347         wl.wl_next = wl.wl_prev = NULL;
348         txfree(buf);
349         ft_check(&wl,fp);
350         return;
351     }
352 #ifdef HAVE_STIM
353     if (prefix(".stim",buf)) {
354         /* stimulus description file */
355         txfree(buf);
356         MB_run(fp,1,filename);
357         return;
358     }
359 #endif
360 
361     inp_readall(fp,&deck,buf);
362     if (deck)
363         decksource(deck, nospice, nocmds, filename);
364 }
365 
366 void
inp_decksource(deck,comfile,filename)367 inp_decksource(deck, comfile, filename)
368 
369 struct line *deck;
370 bool comfile;
371 char *filename;
372 {
373     decksource(deck,comfile,comfile,filename);
374 }
375 
376 
377 static void
decksource(deck,nospice,nocmds,filename)378 decksource(deck, nospice, nocmds, filename)
379 
380 /* Source a deck.  If nospice is true, execute the commands in the deck
381  * without saving the spice text, if any.  If nocmds is true, save the
382  * spice text but discard the commands.  If both flags are true, then
383  * execute all commands up until a .endc line, the deck is assumed to
384  * come from an init file.  If neither flag is true, source the spice
385  * text and execute the commands.
386  */
387 struct line *deck;
388 bool nospice, nocmds;
389 char *filename;
390 {
391     wordlist *wl, *controls;
392     FILE *lastin, *lastout, *lasterr;
393 
394     if (!deck)
395         return;
396 
397     controls = get_controls(deck,nospice && nocmds);
398     if (!nospice && is_ckt(deck)) {
399         out_init();
400         if (!ft_servermode && deck->li_line && *deck->li_line)
401             out_printf("\nCircuit: %s\n\n", deck->li_line);
402         if (inp_spdeck(deck,filename)) {
403             wl_free(controls);
404             return;
405         }
406     }
407     else
408         inp_deckfree(deck);
409 
410     /* Now do the commands */
411     if (controls) {
412         if (!nocmds || nospice) {
413 
414             lastin = cp_curin;
415             lastout = cp_curout;
416             lasterr = cp_curerr;
417             cp_curin = cp_in;
418             cp_curout = cp_out;
419             cp_curerr = cp_err;
420             cp_pushcontrol();
421 
422             for (wl = controls; wl; wl = wl->wl_next)
423                 (void) cp_evloop(wl->wl_word);
424 
425             cp_popcontrol();
426             cp_curin = lastin;
427             cp_curout = lastout;
428             cp_curerr = lasterr;
429         }
430         wl_free(controls);
431     }
432     return;
433 }
434 
435 
436 static bool
is_ckt(deck)437 is_ckt(deck)
438 
439 /* return true if there is a circuit */
440 struct line *deck;
441 {
442     struct line *l;
443     char *s;
444 
445     for (l = deck->li_next; l; l = l->li_next) {
446         s = l->li_line;
447         if (!s) {
448             continue;
449         }
450         while (isspace(*s))
451             s++;
452         if (!*s || *s == '*' || *s == '#') {
453             continue;
454         }
455         /* found something real */
456         return (true);
457     }
458     return (false);
459 }
460 
461 
462 bool
inp_spdeck(deck,filename)463 inp_spdeck(deck,filename)
464 
465 struct line *deck;
466 char *filename;
467 {
468     struct line *dd, *realdeck, *options;
469     wordlist *end;
470     char *tt;
471     bool nosubckts = false;
472     extern char *kw_nosubckt;
473 
474     realdeck = inp_deckcopy(deck);
475     tt = copy(deck->li_line);
476 
477     /* substitute for shell variables in spice text */
478     for (dd = deck->li_next; dd; dd = dd->li_next) {
479         if (*dd->li_line == '*') continue;
480         if (strchr(dd->li_line,'$'))
481             varsub(&dd->li_line);
482     }
483     end = get_speccmds(deck);
484     options = inp_getopts(deck);
485 
486     if (deck->li_next) {
487 
488         /* Now expand subcircuit macros. Note that we have to
489          * fix the case before we do this but after we
490          * deal with the commands.
491          */
492         if (!cp_getvar(kw_nosubckt, VT_BOOL, (char *)&nosubckts)) {
493             dd = inp_subcktexpand(deck->li_next);
494             if (dd == (struct line *)NULL) {
495                 inp_deckfree(deck);
496                 inp_deckfree(realdeck);
497                 inp_deckfree(options);
498                 wl_free(end);
499                 tfree(tt);
500                 fprintf(cp_err,
501             "Error during subcircuit expansion, circuit not loaded.\n");
502                 return (true);;
503             }
504             deck->li_next = dd;
505         }
506         deck->li_actual = realdeck;
507         inp_dodeck(deck, tt, end, false, options, copy(filename));
508     }
509     else {
510         fprintf(cp_err, "Error: no lines in input\n");
511         tfree(tt);
512         wl_free(end);
513         inp_deckfree(options);
514         inp_deckfree(realdeck);
515         inp_deckfree(deck);
516         return (true);
517     }
518     return (false);
519 }
520 
521 
522 static wordlist *
get_speccmds(deck)523 get_speccmds(deck)
524 
525 struct line *deck;
526 {
527     struct line *dd, *ld;
528     wordlist *wl, *end = NULL;
529     char *s;
530 
531     ld = deck;
532     for (dd = deck->li_next; dd; dd = ld->li_next) {
533         if (dd->li_line[0] == '*') {
534             ld = dd;
535             continue;
536         }
537         s = dd->li_line;
538         while (isspace(*s)) s++;
539         inp_casefix(dd->li_line);
540 
541         if (prefix(".width",s) || prefix(".four",s) ||
542                 prefix(".plot",s) || prefix(".print",s) ||
543                 prefix(".save",s)) {
544             if (end) {
545                 end->wl_next = alloc(struct wordlist);
546                 end->wl_next->wl_prev = end;
547                 end = end->wl_next;
548             }
549             else
550                 wl = end = alloc(struct wordlist);
551             end->wl_word = copy(dd->li_line);
552             ld->li_next = dd->li_next;
553             txfree(dd->li_line);
554             txfree((char*)dd);
555         }
556         else
557             ld = dd;
558     }
559     return (end);
560 }
561 
562 
563 static wordlist *
get_controls(deck,allcmds)564 get_controls(deck,allcmds)
565 
566 /* strip the control lines out of the deck and return as a wordlist */
567 struct line *deck;
568 bool allcmds;
569 {
570     bool commands = allcmds;
571     struct line *dd, *ld;
572     char *s;
573     wordlist *wl, *controls = NULL;
574 
575     ld = deck;
576     for (dd = deck->li_next; dd; dd = ld->li_next) {
577         if (ciprefix(".control", dd->li_line)) {
578             ld->li_next = dd->li_next;
579             txfree(dd->li_line);
580             txfree((char*)dd);
581             if (commands && !allcmds)
582                 fprintf(cp_err,
583                     "Warning: redundant .control card\n");
584             commands = true;
585         }
586         else if (ciprefix(".endc", dd->li_line)) {
587             ld->li_next = dd->li_next;
588             txfree(dd->li_line);
589             txfree((char*)dd);
590             if (commands)
591                 commands = false;
592             else
593                 fprintf(cp_err,
594                 "Warning: misplaced .endc card\n");
595         }
596         else if (!*dd->li_line ||
597             (commands && (*dd->li_line == '#' || *dd->li_line == '*'))) {
598             /* So blank lines in com files don't get
599              * considered as circuits.
600              * Also kill comments in commands.
601              */
602             ld->li_next = dd->li_next;
603             txfree(dd->li_line);
604             txfree((char*)dd);
605         }
606         else if (commands || prefix("*#", dd->li_line)) {
607             wl = alloc(struct wordlist);
608             if (controls) {
609                 wl->wl_next = controls;
610                 controls->wl_prev = wl;
611                 controls = wl;
612             }
613             else
614                 controls = wl;
615             if (prefix("*#", dd->li_line))
616                 wl->wl_word = copy(dd->li_line + 2);
617             else
618                 wl->wl_word = copy(dd->li_line);
619             if (commands) {
620                 for (;;) {
621                     /* look for lines concatenated with \ */
622                     s = wl->wl_word + strlen(wl->wl_word) - 1;
623                     while (isspace(*s)) s--;
624                     if (*s == '\\') {
625                         *s = '\0';
626                         ld->li_next = dd->li_next;
627                         txfree(dd->li_line);
628                         txfree((char*)dd);
629                         dd = ld->li_next;
630                         s = tmalloc(
631                             strlen(wl->wl_word) + strlen(dd->li_line) + 1);
632                         strcpy(s,wl->wl_word);
633                         strcat(s,dd->li_line);
634                         txfree(wl->wl_word);
635                         wl->wl_word = s;
636                         continue;
637                     }
638                     break;
639                 }
640             }
641             ld->li_next = dd->li_next;
642             txfree(dd->li_line);
643             txfree((char*)dd);
644         }
645         else
646             ld = dd;
647     }
648     if (!controls)
649         return (NULL);
650     return (wl_reverse(controls));
651 }
652 
653 
654 /* New Feature: substitute for $variables in the spice deck as it is
655  * read.  The variables must have been defined previously, and (presently)
656  * not in the same deck, i.e., not in control statements of the same file.
657  */
658 
659 
660 static void
varsub(str)661 varsub(str)
662 
663 /* replace the $variables */
664 char **str;
665 {
666     wordlist *wl;
667 
668     wl = wl_separate(*str);
669     cp_variablesubst(&wl);
670     txfree(*str);
671     *str = flatten(wl);
672     wl_free(wl);
673 }
674 
675 
676 #define VALIDCHARS "_<#?@.()[]&"
677 
678 
679 static wordlist *
wl_separate(str)680 wl_separate(str)
681 
682 /* separate out the $variables into a linked wordlist */
683 char *str;
684 {
685     char *c, *d;
686     wordlist *wl, *wl0 = NULL;
687     int lev1, lev2;
688 
689     c = str;
690     while (isspace(*c)) c++;
691 
692     while ((d = strchr(c,'$')) != NULL) {
693         if (d != c) {
694             if (wl0 == NULL) {
695                 wl = wl0 = alloc(wordlist);
696             }
697             else {
698                 wl->wl_next = alloc(wordlist);
699                 wl->wl_next->wl_prev = wl;
700                 wl = wl->wl_next;
701             }
702             wl->wl_word = tmalloc((d-c+1)*sizeof(char));
703             strncpy(wl->wl_word,c,d-c);
704             wl->wl_word[d-c] = '\0';
705             c = wl->wl_word + (d-c-1);
706             while(isspace(*c)) *c-- = '\0';
707         }
708         c = d;
709         if (*++d == '\0') break;
710         if (*++d == '\0') break;
711         if (wl0 == NULL) {
712             wl = wl0 = alloc(wordlist);
713         }
714         else {
715             wl->wl_next = alloc(wordlist);
716             wl->wl_next->wl_prev = wl;
717             wl = wl->wl_next;
718         }
719         if (*d &&
720             (isalphanum(*d) || strchr(VALIDCHARS, *d) || *d == cp_dol)) {
721             d++;
722         }
723         lev1 = lev2 = 0;
724         for ( ; *d; d++) {
725             if (isalphanum(*d))
726                 continue;
727             if (*d == '[') {
728                 lev1++;
729                 continue;
730             }
731             if (*d == '(') {
732                 lev2++;
733                 continue;
734             }
735             if (*d == ']') {
736                 if (lev1) lev1--;
737                 continue;
738             }
739             if (*d == ')') {
740                 if (lev2) lev2--;
741                 continue;
742             }
743             if (strchr(VALIDCHARS, *d))
744                 continue;
745             if (lev1 || lev2)
746                 continue;
747             break;
748         }
749         wl->wl_word = tmalloc((d-c+1)*sizeof(char));
750         strncpy(wl->wl_word,c,d-c);
751         wl->wl_word[d-c] = '\0';
752         while (*d && isspace(*d)) d++;
753         c = d;
754     }
755     if (*c) {
756         if (wl0 == NULL) {
757             wl = wl0 = alloc(wordlist);
758         }
759         else {
760             wl->wl_next = alloc(wordlist);
761             wl->wl_next->wl_prev = wl;
762             wl = wl->wl_next;
763         }
764         wl->wl_word = copy(c);
765     }
766     return (wl0);
767 }
768 
769 
770 static char *
flatten(wl)771 flatten(wl)
772 
773 /* like wl_flatten(), but join tokens with leading '%' */
774 wordlist *wl;
775 {
776     wordlist *ww;
777     int len;
778     char *str, *s;
779 
780     len = 1;
781     for (ww = wl; ww; ww = ww->wl_next)
782         len += strlen(ww->wl_word) + 1;
783     str = tmalloc(len);
784     *str = '\0';
785     for (ww = wl; ww; ww = ww->wl_next) {
786         s = ww->wl_word;
787         if (*s == '%') {
788             while (*s == '%') s++;
789             strcat(str,s);
790             continue;
791         }
792         if (ww != wl)
793             strcat(str," ");
794         strcat(str,s);
795     }
796     return (str);
797 }
798 
799 
800 /* This routine is cut in half here because com_rset has to do what follows
801  * also. End is the list of commands we execute when the job is finished:
802  * we only bother with this if we might be running in batch mode, since
803  * it isn't much use otherwise.
804  */
805 
806 void
inp_dodeck(deck,tt,end,reuse,options,filename)807 inp_dodeck(deck, tt, end, reuse, options, filename)
808 
809 struct line *deck;
810 char *tt;
811 wordlist *end;
812 bool reuse;
813 struct line *options;
814 char *filename;
815 {
816     struct circ *ct;
817     struct line *dd;
818     char *ckt, *s;
819     INPtables *tab;
820     struct variable *eev = NULL;
821     wordlist *wl;
822     bool noparse, ii;
823     extern char *kw_noparse;
824 
825     /* First throw away any old error messages there might be and fix
826      * the case of the lines.
827      */
828     for (dd = deck; dd; dd = dd->li_next) {
829         if (dd->li_error) {
830             tfree(dd->li_error);
831             dd->li_error = NULL;
832         }
833     }
834     for (dd = options; dd; dd = dd->li_next) {
835         if (dd->li_error) {
836             tfree(dd->li_error);
837             dd->li_error = NULL;
838         }
839     }
840     if (reuse) {
841         ct = ft_curckt;
842     }
843     else {
844         if (ft_curckt) {
845             ft_curckt->ci_devices = cp_kwswitch(CT_DEVNAMES,
846                     (char *) NULL);
847             ft_curckt->ci_nodes = cp_kwswitch(CT_NODENAMES,
848                     (char *) NULL);
849         }
850         ft_curckt = ct = alloc(struct circ);
851     }
852     (void) cp_getvar(kw_noparse, VT_BOOL, (char *) &noparse);
853     if (!noparse)
854         ckt = if_inpdeck(deck, (char**)&tab);
855     else
856         ckt = NULL;
857 
858     for (dd = deck; dd; dd = dd->li_next)
859         if (dd->li_error) {
860             if (cp_out != stdout)
861                 fprintf(cp_err, "Warning, line %d : %s\n%s\n",
862                     dd->li_linenum, dd->li_line, dd->li_error);
863             else
864                 out_printf("Warning, line %d : %s\n%s\n",
865                     dd->li_linenum, dd->li_line, dd->li_error);
866         }
867 
868     /* Add this circuit to the circuit list. If reuse is true then
869      * use the ft_curckt structure.
870      */
871 
872     if (!reuse) {
873         for (dd = deck->li_next; dd; dd = dd->li_next)
874             if_setndnames(dd->li_line);
875 
876         /* Be sure that ci_devices and ci_nodes are valid */
877         ft_curckt->ci_devices = cp_kwswitch(CT_DEVNAMES,
878                 (char *) NULL);
879         (void) cp_kwswitch(CT_DEVNAMES, ft_curckt->ci_devices);
880         ft_curckt->ci_nodes = cp_kwswitch(CT_NODENAMES, (char *) NULL);
881         (void) cp_kwswitch(CT_NODENAMES, ft_curckt->ci_nodes);
882         ft_newcirc(ct);
883         /* ft_setccirc(); */ ft_curckt = ct;
884     }
885     ct->ci_name = tt;
886     ct->ci_deck = deck;
887     ct->ci_options = options;
888     if (deck->li_actual)
889         ct->ci_origdeck = deck->li_actual;
890     else
891         ct->ci_origdeck = ct->ci_deck;
892     ct->ci_ckt = ckt;
893     ct->ci_symtab = (char*)tab;
894     ct->ci_inprogress = false;
895     ct->ci_runonce = false;
896     ct->ci_commands = end;
897     if (filename)
898         ct->ci_filename = filename;
899     else
900         tfree(ct->ci_filename);
901 
902     if (!noparse) {
903         for (; options; options = options->li_next) {
904             INP2dot(ct->ci_ckt,(INPtables*)ct->ci_symtab,
905                 (card*)options,NULL,NULL);
906             for (s = options->li_line; *s && !isspace(*s); s++)
907                 ;
908             ii = cp_interactive;
909             cp_interactive = false;
910             wl = cp_lexer(s);
911             cp_interactive = ii;
912             if (!wl || !wl->wl_word || !*wl->wl_word)
913                 continue;
914             if (eev)
915                 eev->va_next = cp_setparse(wl);
916             else
917                 ct->ci_vars = eev = cp_setparse(wl);
918             wl_free(wl);
919             while (eev->va_next)
920                 eev = eev->va_next;
921 
922             if (options->li_error)
923                 out_printf("Warning, line %d : %s\n%s\n",
924                     options->li_linenum, options->li_line, options->li_error);
925         }
926     }
927 
928     cp_addkword(CT_CKTNAMES, tt);
929     return;
930 }
931 
932 
933 /* Edit and re-load the current input deck.  In this version, the previous
934  * circuit (before editing) is kept, but is no longer associated with the
935  * original file name, unless "-r" option is given. "-n" supresses source
936  * after edit;
937  */
938 
939 void
com_edit(wl)940 com_edit(wl)
941 
942 wordlist *wl;
943 {
944 
945     char *filename;
946     FILE *fp;
947     bool permfile, nosource = false, reuse = false;
948     struct circ *old;
949     extern char *kw_editor;
950     char ed[BSIZE_SP];
951     int rval;
952 
953     if (SCEDactive()) {
954         ShowPrompt("Exit sced to edit.");
955         return;
956     }
957 
958 top:
959     if (wl) {
960 
961         /* supress sourcing if "-n" given */
962         /* reuse ckt structure if "-r" given */
963         if (eq(wl->wl_word,"-n")) {
964             nosource = true;
965             wl = wl->wl_next;
966             goto top;
967         }
968         if (eq(wl->wl_word,"-r")) {
969             reuse = true;
970             wl = wl->wl_next;
971             goto top;
972         }
973         if (wl->wl_next && eq(wl->wl_next->wl_word,"-n"))
974             nosource = true;
975         if (wl->wl_next && eq(wl->wl_next->wl_word,"-r"))
976             reuse = true;
977         filename = wl->wl_word;
978         permfile = true;
979     }
980     else {
981         if (ft_curckt && ft_curckt->ci_filename) {
982             /* know the circuit and file */
983             filename = ft_curckt->ci_filename;
984             permfile = true;
985         }
986         else {
987             /* work with temp file */
988             filename = smktemp("sp");
989             permfile = false;
990         }
991         if (ft_curckt && !ft_curckt->ci_filename) {
992             /* Circuit came from file which was subsequently modified.
993              * Dump circuit listing into temp file.
994              */
995             if (!(fp = fopen(filename, "w"))) {
996                 perror(filename);
997                 return;
998             }
999             inp_list(fp, ft_curckt->ci_origdeck ? ft_curckt->ci_origdeck :
1000                 ft_curckt->ci_deck, ft_curckt->ci_options, LS_DECK);
1001             fprintf(cp_err,
1002         "Warning: editing a temporary file -- circuit not saved\n");
1003             (void) fclose(fp);
1004         }
1005         else if (!ft_curckt) {
1006             /* No current circuit, user must create (using temp file) */
1007             if (!(fp = fopen(filename, "w"))) {
1008                 perror(filename);
1009                 return;
1010             }
1011             fprintf(fp, "SPICE 3 test deck\n");
1012             (void) fclose(fp);
1013         }
1014     }
1015 
1016     rval = cp_getvar(kw_editor, VT_STRING, ed);
1017     if ((!rval || cieq(ed, "xeditor")) && !xeditor(filename)) {
1018         if (!permfile) {
1019             (void) unlink(filename);
1020             txfree(filename);
1021         }
1022         return;
1023     }
1024     else {
1025 
1026 #ifdef HAVE_STAT
1027         /* supress source if file is not written */
1028         int i, j;
1029         struct stat st1, st2;
1030 
1031         if (permfile) {
1032             i = stat(filename,&st1);
1033             if (inp_edit(filename))
1034                 return;
1035             j = stat(filename,&st2);
1036             if (j == -1)
1037                 return;
1038             if (!i && st1.st_mtime == st2.st_mtime) {
1039                 /* file was not modified */
1040                 for (old = ft_circuits; old; old = old->ci_next) {
1041                     if (old->ci_filename && eq(old->ci_filename,filename))
1042                         /* already sourced */
1043                         return;
1044                 }
1045             }
1046         }
1047         else
1048 #endif
1049         if (inp_edit(filename)) return;
1050     }
1051     if (!nosource)
1052         inp_srcedit(filename, permfile, reuse);
1053 }
1054 
1055 
1056 void
inp_srcedit(filename,permfile,reuse)1057 inp_srcedit(filename, permfile, reuse)
1058 
1059 char *filename;
1060 bool permfile, reuse;
1061 {
1062     FILE *fp;
1063     struct circ *old;
1064     bool inter;
1065 
1066     inter = cp_interactive;
1067     cp_interactive = false;
1068     if (!(fp = inp_pathopen(filename, "r"))) {
1069         perror(filename);
1070         goto ret;
1071     }
1072     if (reuse) if_cktclear();
1073     inp_spsource(fp, false, permfile ? filename : (char *) NULL);
1074     (void) fclose(fp);
1075 
1076     if (!permfile) {
1077         (void) unlink(filename);
1078         txfree(filename);
1079     }
1080 
1081     /* Check for other loaded circuits from the same file.  If found,
1082      * free and null the filename entry, as the file has been edited
1083      * and therefore does not correspond to the saved circuit struct.
1084      */
1085     if (permfile)
1086         for (old = ft_circuits; old; old = old->ci_next) {
1087             if (old != ft_curckt && old->ci_filename &&
1088                 eq(old->ci_filename,filename))
1089                 tfree(old->ci_filename);
1090         }
1091 ret:
1092     cp_interactive = inter;
1093     return;
1094 }
1095 
1096 
1097 /* SCED interface */
1098 void
com_sced(wl)1099 com_sced(wl)
1100 
1101 wordlist *wl;
1102 {
1103     FILE *fp,*errfp,*outfp,*errtmp,*outtmp;
1104     struct circ *old;
1105     bool inter, nosource = false, reuse = false;
1106     char *errmsg = "Error: no symbolic representation found.\n";
1107     char *filename,*errfile,*outfile;
1108     int i;
1109 
1110     inter = cp_interactive;
1111     cp_interactive = false;
1112 top:
1113     if (wl) {
1114 
1115         /* supress sourcing if "-n" given */
1116         /* reuse ckt structure if "-r" given */
1117         if (eq(wl->wl_word,"-n")) {
1118             nosource = true;
1119             wl = wl->wl_next;
1120             goto top;
1121         }
1122         if (eq(wl->wl_word,"-r")) {
1123             reuse = true;
1124             wl = wl->wl_next;
1125             goto top;
1126         }
1127         if (wl->wl_next && eq(wl->wl_next->wl_word,"-n"))
1128             nosource = true;
1129         if (wl->wl_next && eq(wl->wl_next->wl_word,"-r"))
1130             reuse = true;
1131         filename = wl->wl_word;
1132     }
1133     else {
1134         if (ft_curckt && ft_curckt->ci_filename) {
1135             /* know the circuit and symbolic file */
1136             filename = ft_curckt->ci_filename;
1137         }
1138         else {
1139             if (ft_curckt) {
1140                 fprintf(cp_err,errmsg);
1141                 goto ret;
1142             }
1143             else {
1144                 filename = "noname";
1145             }
1146         }
1147     }
1148     errfile = smktemp("er");
1149     outfile = smktemp("ot");
1150 
1151     errfp = fopen(errfile,"w+");
1152     outfp = fopen(outfile,"w");
1153     if (errfp == NULL || outfp == NULL) {
1154         fprintf(cp_err,"Error: redirection failed.\n");
1155         return;
1156     }
1157     errtmp = cp_err;
1158     outtmp = cp_out;
1159     cp_err = cp_curerr = errfp;
1160     cp_out = cp_curout = outfp;
1161 
1162 
1163 #ifdef HAVE_STAT
1164 /* supress source if file is not written */
1165 {
1166     int k, j;
1167     struct stat st1, st2;
1168     char *f1;
1169 
1170     f1 = filename;
1171     k = stat(filename,&st1);
1172     i = sced(&filename);
1173     if (i == 1) {
1174         j = stat(filename,&st2);
1175         if (j == -1)
1176             i = 2;
1177         if (!k && eq(f1,filename) && st1.st_mtime == st2.st_mtime) {
1178             /* file was not modified */
1179             for (old = ft_circuits; old; old = old->ci_next) {
1180                 if (old->ci_filename && eq(old->ci_filename,filename)) {
1181                     /* already sourced */
1182                     i = 2;
1183                     break;
1184                 }
1185             }
1186         }
1187     }
1188 }
1189 #else
1190     i = sced(&filename);
1191 #endif
1192     cp_curout = outtmp;
1193     cp_curerr = errtmp;
1194     cp_ioreset();
1195     unlink(errfile);
1196     unlink(outfile);
1197     tfree(errfile);
1198     tfree(outfile);
1199 
1200     if (i == 3) {
1201         fprintf(cp_err,errmsg);
1202         goto ret;
1203     }
1204     if (i == 2 || nosource)
1205         goto ret;
1206 
1207     if (i == 1) {
1208         if (!(fp = inp_pathopen(filename, "r"))) {
1209             perror(filename);
1210             goto ret;
1211         }
1212         if (reuse) if_cktclear();
1213         fprintf(cp_err,"Sourcing the spice text from %s\n",filename);
1214         fprintf(cp_err,
1215 "Warning: this text may not accurately reflect subcircuit changes\n\
1216 made after %s was saved in sced\n",filename);
1217         inp_spsource(fp,false,filename);
1218         (void) fclose(fp);
1219     }
1220 
1221     /* Check for other loaded circuits from the same file.  If found,
1222      * free and null the filename entry, as the file has been edited
1223      * and therefore does not correspond to the saved circuit struct.
1224      */
1225     for (old = ft_circuits; old; old = old->ci_next) {
1226         if (old != ft_curckt)
1227             if (old->ci_filename && eq(old->ci_filename,filename))
1228                 tfree(old->ci_filename);
1229     }
1230 ret:
1231     cp_interactive = inter;
1232     return;
1233 }
1234 
1235 
1236 void
com_source(wl)1237 com_source(wl)
1238 
1239 wordlist *wl;
1240 {
1241     FILE *fp, *tp;
1242     char buf[BSIZE_SP];
1243     bool inter;
1244     char *tempfile = NULL, *fname;
1245     wordlist *owl, *ww, *wn;
1246     int i;
1247     bool nospice = false, nocmds = false, reuse = false;
1248 
1249     owl = wl;
1250     wl = wl_copy(wl);
1251     ww = wl;
1252     for (; wl; wl = wn) {
1253         wn = wl->wl_next;
1254         if (*wl->wl_word != '-')
1255             continue;
1256         if (strchr(wl->wl_word,'n'))
1257             nospice = true;
1258         else if (strchr(wl->wl_word,'c'))
1259             nocmds = true;
1260         else if (strchr(wl->wl_word,'r'))
1261             reuse = true;
1262         else
1263             continue;
1264         if (wl->wl_prev)
1265             wl->wl_prev->wl_next = wl->wl_next;
1266         if (wl->wl_next)
1267             wl->wl_next->wl_prev = wl->wl_prev;
1268         txfree(wl->wl_word);
1269         if (ww == wl)
1270             ww = wl->wl_next;
1271         txfree((char*)wl);
1272     }
1273     wl = ww;
1274 
1275     inter = cp_interactive;
1276     cp_interactive = false;
1277     if (wl && wl->wl_next) {
1278         /* There are several files -- put them into a temp file  */
1279         tempfile = smktemp("sp");
1280         if (!(fp = inp_pathopen(tempfile, "w+"))) {
1281             perror(tempfile);
1282             goto done;
1283         }
1284         while (wl) {
1285             if (!(tp = inp_pathopen(wl->wl_word, "r"))) {
1286                 perror(wl->wl_word);
1287                 (void) fclose(fp);
1288                 goto done;
1289             }
1290             while ((i = fread(buf, 1, BSIZE_SP, tp)) > 0)
1291                 (void) fwrite(buf, 1, i, fp);
1292             (void) fclose(tp);
1293             wl = wl->wl_next;
1294         }
1295         (void) fseek(fp, (long) 0, 0);
1296     }
1297     else {
1298         if (wl) {
1299             if (!(fp = inp_pathopen(wl->wl_word, "r"))) {
1300                 perror(wl->wl_word);
1301                 goto done;
1302             }
1303         }
1304         else
1305             fp = stdin;
1306     }
1307     if (reuse && !nospice)
1308         if_cktclear();
1309 
1310     if (tempfile)
1311         fname = NULL;
1312     else {
1313         if (wl)
1314             fname = wl->wl_word;
1315         else
1316             fname = NULL;
1317     }
1318 
1319     /* Don't print the title if this is a .spiceinit file. */
1320     if (substring(".spiceinit", owl->wl_word) ||
1321             substring("spice.rc", owl->wl_word))
1322         spsource(fp, true, true, fname);
1323     else
1324         spsource(fp,nospice,nocmds,fname);
1325     if (fp != stdin)
1326         (void) fclose(fp);
1327 
1328 done:
1329     cp_interactive = inter;
1330 
1331     if (tempfile) {
1332         (void) unlink(tempfile);
1333         txfree(tempfile);
1334     }
1335     wl_free(ww);
1336     return;
1337 }
1338 
1339 
1340 void
inp_source(file)1341 inp_source(file)
1342 
1343 char *file;
1344 {
1345     wordlist *wl;
1346 
1347     wl = cp_lexer(file);
1348     com_source(wl);
1349     wl_free(wl);
1350     return;
1351 }
1352