Browse Source

Applied Steven Borley patch for numparam and frontend. See Changelog.

pre-master-46
pnenzi 21 years ago
parent
commit
985d977197
  1. 39
      ChangeLog
  2. 2
      src/frontend/device.c
  3. 2
      src/frontend/inp.c
  4. 102
      src/frontend/inpcom.c
  5. 2
      src/frontend/numparam/general.h
  6. 12
      src/frontend/numparam/numparam.h
  7. 16
      src/frontend/numparam/nupatest.c
  8. 43
      src/frontend/numparam/spicenum.c
  9. 18
      src/frontend/numparam/washprog.c
  10. 54
      src/frontend/numparam/xpressn.c
  11. 17
      src/frontend/plotting/x11.c
  12. 10
      src/frontend/resource.c
  13. 12
      src/frontend/spiceif.c
  14. 78
      src/frontend/subckt.c

39
ChangeLog

@ -1,3 +1,42 @@
2005-04-25 Paolo Nenzi <p.nenzi@ieee.org>
* src/frontend/numparam/{general.h, numparam.h, nupatest.c,
spicenum.c, washprog.c, xpressn.c}: Applied patch from Steven
Borley <steven.borley@virgin.net>. The patch removed the numparam
support for end-of-line comments and moved it into frontend code.
Line buffer for numparam is increased from 250 to 1000 to accomodate
large model cards. Fixed compilation on Cygwin and MacOSX.
* src/frontend/subckt.c: numparams, once compiled in, must be
enabled, during interactive session, using "set numparams". Since
library is experimental and under continuos testing, it is not
yet intended for general use and support is provided for interactive
sessions only (patch from Steven Borley).
* src/frontend/{device.c, spiceif.c}: fixed case sensitivity problems
in key-word identification (patch from Steven Borley).
* src/frontend/inp.c: Fix a bug where the wrong argument to 'listing'
would error, but still produce a listing (patch fron Steven Borley).
* src/frontend/inpcom.c: From Steven Borley <steven.borley@virgin.net>
patch:
End-of-line comments have been fixed and can be used on a line
that is followed by a continuation line. This functionality
has been moved from the numparams library and in to the front
end code. All the following can be used to start a comment:
; for compatibility with PSpice
$ for compatibility with HSpice
// like C++, and as per numparams
-- as per numparams
This was a full re-write of this code, not a conversion of the
numparams code. It is not dependent on the numparams library.
* src/frontend/resource.c: Fixed a compile bug under window & cygwin
(patch from Steven Borley).
* src/frontend/plotting/x11.c: Applied patch from Steven Borley.
2005-04-16 Paolo Nenzi <p.nenzi@ieee.org> 2005-04-16 Paolo Nenzi <p.nenzi@ieee.org>
* src/frontend/{subckt.c, inpcom.c}, src/frontend/numparam/{general.h, * src/frontend/{subckt.c, inpcom.c}, src/frontend/numparam/{general.h,

2
src/frontend/device.c

@ -663,7 +663,7 @@ devexpand(char *name)
wl = tw; wl = tw;
wl->wl_word = devices->wl_word; wl->wl_word = devices->wl_word;
} }
} else if (eq(name, "all")) {
} else if (cieq(name, "all")) {
wl = cp_cctowl(ft_curckt->ci_devices); wl = cp_cctowl(ft_curckt->ci_devices);
} else { } else {
wl = alloc(struct wordlist); wl = alloc(struct wordlist);

2
src/frontend/inp.c

@ -37,7 +37,6 @@ Author: 1985 Wayne A. Christopher
static char * upper(register char *string); static char * upper(register char *string);
static bool doedit(char *filename); static bool doedit(char *filename);
/* Do a listing. Use is listing [expanded] [logical] [physical] [deck] */ /* Do a listing. Use is listing [expanded] [logical] [physical] [deck] */
@ -73,6 +72,7 @@ com_listing(wordlist *wl)
default: default:
fprintf(cp_err, fprintf(cp_err,
"Error: bad listing type %s\n", s); "Error: bad listing type %s\n", s);
return; /* SJB - don't go on after an error */
} }
wl = wl->wl_next; wl = wl->wl_next;
} }

102
src/frontend/inpcom.c

@ -6,7 +6,21 @@ Author: 1985 Wayne A. Christopher
/* /*
* For dealing with spice input decks and command scripts * For dealing with spice input decks and command scripts
*/ */
/*
* SJB 21 April 2005
* Added support for end-of-line comments that begin with any of the following:
* ';' (for PSpice compatability)
* '$' (for HSpice compatability)
* '//' (like in c++ and as per the numparam code)
* '--' (as per the numparam code)
* Any following text to the end of the line is ignored.
* Comments on a contunuation line (i.e. line begining with '+') are allowed
* and are removed before lines are stitched.
* Lines that contain only an end-of-line comment with or withou leading white
* space are also allowed.
*/
/* /*
* SJB 22 May 2001 * SJB 22 May 2001
* Fixed memory leaks in inp_readall() when first(?) line of input begins with a '@'. * Fixed memory leaks in inp_readall() when first(?) line of input begins with a '@'.
@ -35,6 +49,11 @@ Author: 1985 Wayne A. Christopher
/* SJB - Uncomment this line for debug tracing */ /* SJB - Uncomment this line for debug tracing */
/*#define TRACE */ /*#define TRACE */
/* static declarations */
static char * readline(FILE *fd);
static void inp_stripcomments_deck(struct line *deck);
static void inp_stripcomments_line(char * s);
/*-------------------------------------------------------------------------* /*-------------------------------------------------------------------------*
* This routine reads a line (of arbitrary length), up to a '\n' or 'EOF' * * This routine reads a line (of arbitrary length), up to a '\n' or 'EOF' *
* and returns a pointer to the resulting null terminated string. * * and returns a pointer to the resulting null terminated string. *
@ -305,10 +324,17 @@ inp_readall(FILE *fp, struct line **data)
/* tfree(buffer); */ /* tfree(buffer); */
/* Now clean up li: remove comments & stitch together continuation lines. */
/* Now clean up li: remove comments & stitch together continuation lines. */
working = cc->li_next; /* cc points to head of deck. Start with the working = cc->li_next; /* cc points to head of deck. Start with the
next card. */ next card. */
/* sjb - strip or convert end-of-line comments.
This must be cone before stitching continuation lines.
If the line only contains an end-of-line comment then it is converted
into a normal comment with a '*' at the start. This will then get
stripped in the following code. */
inp_stripcomments_deck(working);
while (working) { while (working) {
for (s = working->li_line; (c = *s) && c <= ' '; s++) for (s = working->li_line; (c = *s) && c <= ' '; s++)
; ;
@ -319,7 +345,7 @@ inp_readall(FILE *fp, struct line **data)
#endif #endif
switch (c) { switch (c) {
case '#':
case '#':
case '$': case '$':
case '*': case '*':
case '\0': case '\0':
@ -402,3 +428,73 @@ inp_casefix(char *string)
return; return;
#endif #endif
} }
/* Strip all end-of-line comments from a deck */
static void
inp_stripcomments_deck(struct line *deck)
{
struct line *c=deck;
while( c!=NULL) {
inp_stripcomments_line(c->li_line);
c= c->li_next;
}
}
/* Strip end of line comment from a string and remove trailing white space
supports comments that begin with single characters ';' or '$'
or double characters '//' or '--'
If there is only white space before the end-of-line comment the
the whole line is converted to a normal comment line (i.e. one that
begins with a '*').
BUG: comment characters in side of string literals are not ignored. */
static void
inp_stripcomments_line(char * s)
{
char c = ' '; /* anything other than a comment character */
char * d = s;
if(*s=='\0') return; /* empty line */
/* look for comment */
while((c=*d)!='\0') {
d++;
if( (*d==';') || (*d=='$') ) {
break;
} else if( (*d==c) && ((c=='/') || (c=='-'))) {
*d--; /* move d back to first comment character */
break;
}
}
/* d now points to the first comment character of the null at the string end */
/* check for special case of comment at start of line */
if(d==s) {
*s = '*'; /* turn into normal comment */
return;
}
if(d>s) {
d--;
/* d now points to character just before comment */
/* eat white space at end of line */
while(d>=s) {
if( (*d!=' ') && (*d!='\t' ) )
break;
d--;
}
d++;
/* d now points to the first white space character before the
end-of-line or end-of-line comment, or it points to the first
end-of-line comment character, or to the begining of the line */
}
/* Check for special case of comment at start of line
with or without preceeding white space */
if(d<=s) {
*s = '*'; /* turn the whole line into normal comment */
return;
}
*d='\0'; /* terminate line in new location */
}

2
src/frontend/numparam/general.h

@ -85,7 +85,7 @@ Type(Pfile, FILE AT)
#ifdef __STDIO_H /* Turbo C */ #ifdef __STDIO_H /* Turbo C */
Type(Pfile, FILE AT) Type(Pfile, FILE AT)
#else #else
Type(Pfile, Pointer)
Type(Pfile, FILE*) /* sjb - was Pointer, now FILE* */
#endif #endif
#endif #endif

12
src/frontend/numparam/numparam.h

@ -9,7 +9,8 @@
extern char * nupa_copy(char *s, int linenum); extern char * nupa_copy(char *s, int linenum);
extern int nupa_eval(char *s, int linenum); extern int nupa_eval(char *s, int linenum);
extern int nupa_signal(int sig, char *info);
extern int nupa_signal(int sig, char *info);
extern void nupa_scan(char * s, int linenum); // sjb
/***** numparam internals ********/ /***** numparam internals ********/
@ -23,7 +24,12 @@ Cconst(Comment,'*') /*Spice Comment lines*/
Cconst(Pspice,'{') /*Pspice expression */ Cconst(Pspice,'{') /*Pspice expression */
Cconst(Maxdico,200) /*size of symbol table*/ Cconst(Maxdico,200) /*size of symbol table*/
Cconst(Llen,250) /* maximum composite input line length */
/* Composite line length
This used to be 250 characters, but this is too easy to exceed with a
.model line, especially when spread over several continuation
lines with much white space. I hope 1000 will be enough. */
Cconst(Llen,1000)
typedef char str20 [24]; typedef char str20 [24];
typedef char str80 [84]; typedef char str80 [84];
@ -42,7 +48,7 @@ Record(entry)
Pchar sbbase; /* string buffer base address if any */ Pchar sbbase; /* string buffer base address if any */
EndRec(entry) EndRec(entry)
Record(fumas) /*funtion,macro,string*/
Record(fumas) /*function,macro,string*/
Word start /*,stop*/ ; /*buffer index or location */ Word start /*,stop*/ ; /*buffer index or location */
EndRec(fumas) EndRec(fumas)

16
src/frontend/numparam/nupatest.c

@ -57,7 +57,7 @@ Func short runscript( tdico *dico, Pchar prefix,
/* return value: number of lines included */ /* return value: number of lines included */
Begin Begin
short i,j, idef, nnest, nline, dn, myipx; short i,j, idef, nnest, nline, dn, myipx;
Str(250, subpfx); /* subckt prefix */
Strbig(Llen, subpfx); /* subckt prefix */
Str(80, subname); Str(80, subname);
char c; char c;
Bool done= False; Bool done= False;
@ -116,7 +116,7 @@ Proc gluepluslines( short imax)
/* general sweep to eliminate continuation lines */ /* general sweep to eliminate continuation lines */
Begin Begin
short i,j,k, ls, p; short i,j,k, ls, p;
Str(250,s);
Strbig(Llen,s);
i=1; i=1;
While i<= imax Do While i<= imax Do
If (buff[i][0]=='+') And (i>1) Then If (buff[i][0]=='+') And (i>1) Then
@ -141,7 +141,7 @@ Begin
Done Done
EndProc EndProc
#if 0
#if 0 // sjb - this is in mystring.c
Proc rs(Pchar s) /* 78 coumn limit */ Proc rs(Pchar s) /* 78 coumn limit */
Begin Begin
short i; short i;
@ -185,7 +185,7 @@ EndProc
Proc wordinsert(Pchar s, Pchar w, short i) Proc wordinsert(Pchar s, Pchar w, short i)
/* insert w before s[i] */ /* insert w before s[i] */
Begin Begin
Str(250,t);
Strbig(Llen,t);
short ls=length(s); short ls=length(s);
pscopy(t,s,i+1,ls); pscopy(s,s,1,i); pscopy(t,s,i+1,ls); pscopy(s,s,1,i);
sadd(s,w); sadd(s,t); sadd(s,w); sadd(s,t);
@ -194,7 +194,7 @@ EndProc
Func short worddelete(Pchar s, short i) Func short worddelete(Pchar s, short i)
/* delete word starting at s[i] */ /* delete word starting at s[i] */
Begin Begin
Str(250,t);
Strbig(Llen,t);
short ls= length(s); short ls= length(s);
short j=i; short j=i;
While (j<ls) And (s[j]>' ') Do Inc(j) Done While (j<ls) And (s[j]>' ') Do Inc(j) Done
@ -327,7 +327,7 @@ Proc getnodelist(Pchar form, Pchar act, Pchar s, tdico *dic, short k)
/* the line s contains the actual node parameters, between 1st & last word */ /* the line s contains the actual node parameters, between 1st & last word */
Begin Begin
short j,ls, idef; short j,ls, idef;
Str(80,u); Str(250,t);
Str(80,u); Strbig(Llen,t);
ccopy(act,' '); ccopy(form,' '); ccopy(act,' '); ccopy(form,' ');
j=0; ls= length(s); j=0; ls= length(s);
j= getnextword(s,u,j); j= getnextword(s,u,j);
@ -358,9 +358,9 @@ Proc nupa_test(Pchar fname, char mode)
Begin Begin
Pfile tf, fout; Pfile tf, fout;
tdico * dic; /* dictionary data pointer */ tdico * dic; /* dictionary data pointer */
Str(250,s);
Strbig(Llen,s);
Str(80, prefix); Str(80, prefix);
/* Str(250, formals); Str(250,actuals); */
/* Strbig(Llen, formals); Strbig(Llen,actuals); */
Darray(formals, Pchar, 10) Darray(formals, Pchar, 10)
Darray(actuals, Pchar, 10) Darray(actuals, Pchar, 10)
short i, j, k, nline, parstack; short i, j, k, nline, parstack;

43
src/frontend/numparam/spicenum.c

@ -22,6 +22,7 @@ Todo:
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#ifdef __TURBOC__ #ifdef __TURBOC__
#include <process.h> /* exit() */ #include <process.h> /* exit() */
#endif #endif
@ -30,7 +31,7 @@ Todo:
#include "numparam.h" #include "numparam.h"
/* Uncomment this line to allow debug tracing */ /* Uncomment this line to allow debug tracing */
/*#define TRACE_NUMPARAMS */
/* #define TRACE_NUMPARAMS */
/* the nupa_signal arguments sent from Spice: /* the nupa_signal arguments sent from Spice:
@ -55,6 +56,7 @@ Todo:
#define PlaceHold 1000000000L #define PlaceHold 1000000000L
Intern long placeholder= 0; Intern long placeholder= 0;
#ifdef NOT_REQUIRED /* SJB - not required as front-end now does stripping */
Intern Intern
Func short stripcomment( Pchar s) Func short stripcomment( Pchar s)
/* allow end-of-line comments in Spice, like C++ */ /* allow end-of-line comments in Spice, like C++ */
@ -82,6 +84,7 @@ Begin
EndIf EndIf
return i /* i>=0 if comment stripped at that position */ return i /* i>=0 if comment stripped at that position */
EndFunc EndFunc
#endif /* NOT_REQUIRED */
Intern Intern
Proc stripsomespace(Pchar s, Bool incontrol) Proc stripsomespace(Pchar s, Bool incontrol)
@ -106,7 +109,7 @@ Proc partition(Pchar t)
/* the Basic preprocessor doesnt understand multiple cmd/line */ /* the Basic preprocessor doesnt understand multiple cmd/line */
/* bug: strip trailing spaces */ /* bug: strip trailing spaces */
Begin Begin
Str(Llen,u);
Strbig(Llen,u);
short i,lt,state; short i,lt,state;
char c; char c;
cadd(u,Intro); cadd(u,Intro);
@ -140,7 +143,7 @@ Func short stripbraces( Pchar s)
/* puts the funny placeholders. returns the number of {...} substitutions */ /* puts the funny placeholders. returns the number of {...} substitutions */
Begin Begin
short n,i,nest,ls,j; short n,i,nest,ls,j;
Str(Llen,t);
Strbig(Llen,t);
n=0; ls=length(s); n=0; ls=length(s);
i=0; i=0;
While i<ls Do While i<ls Do
@ -183,7 +186,7 @@ Begin
Bool found; Bool found;
h=0; h=0;
ls=length(s); ls=length(s);
k=ls; found=False;
k=ls; found=False;
While (k>=0) And (Not found) Do /* skip space, then non-space */ While (k>=0) And (Not found) Do /* skip space, then non-space */
While (k>=0) And (s[k]<=' ') Do Dec(k) Done; While (k>=0) And (s[k]<=' ') Do Dec(k) Done;
h=k+1; /* at h: space */ h=k+1; /* at h: space */
@ -212,7 +215,7 @@ Begin
Done Done
found= (getidtype(dico, name) == 'U'); found= (getidtype(dico, name) == 'U');
EndIf EndIf
Done
Done
If found And (h<ls) Then If found And (h<ls) Then
pscopy(s,s,1,h) pscopy(s,s,1,h)
EndIf EndIf
@ -223,7 +226,7 @@ Intern
Proc modernizeex( Pchar s) Proc modernizeex( Pchar s)
/* old style expressions &(..) and &id --> new style with braces. */ /* old style expressions &(..) and &id --> new style with braces. */
Begin Begin
Str(250,t);
Strbig(Llen,t);
short i,state, ls; short i,state, ls;
char c,d; char c,d;
i=0; state=0; i=0; state=0;
@ -264,7 +267,7 @@ Func char transform(tdico * dico, Pchar s, Bool nostripping, Pchar u)
* any + line is copied as-is. * any + line is copied as-is.
* any & or .param line is commented-out. * any & or .param line is commented-out.
* any .subckt line has params section stripped off * any .subckt line has params section stripped off
* any X line loses its arguments after circuit name
* any X line loses its arguments after sub-circuit name
* any &id or &() or {} inside line gets a 10-digit substitute. * any &id or &() or {} inside line gets a 10-digit substitute.
* *
* strip the new syntax off the codeline s, and * strip the new syntax off the codeline s, and
@ -282,10 +285,10 @@ Func char transform(tdico * dico, Pchar s, Bool nostripping, Pchar u)
* 'B' netlist (or .model ?) line that had Braces killed * 'B' netlist (or .model ?) line that had Braces killed
*/ */
Begin Begin
Str(Llen,t);
Strbig(Llen,t);
char category; char category;
short i,k, a,n; short i,k, a,n;
i=stripcomment(s);
/* i=stripcomment(s); sjb - not required now that front-end does stripping */
stripsomespace(s, nostripping); stripsomespace(s, nostripping);
modernizeex(s); /* required for stripbraces count */ modernizeex(s); /* required for stripbraces count */
scopy(u,""); scopy(u,"");
@ -321,7 +324,7 @@ Begin
category='P'; category='P';
ElsIf upcase(s[0])=='X' Then /* strip actual parameters */ ElsIf upcase(s[0])=='X' Then /* strip actual parameters */
i=findsubname(dico, s); /* i= index following last identifier in s */ i=findsubname(dico, s); /* i= index following last identifier in s */
pscopy(s,s,1,i);
/* pscopy(s,s,1,i); sjb - this is already done by findsubname() */
category='X' category='X'
ElsIf s[0]=='+' Then /* continuation line */ ElsIf s[0]=='+' Then /* continuation line */
category='+' category='+'
@ -367,7 +370,7 @@ Intern tdico * dico=Null;
Intern Intern
Proc putlogfile(char c, int num, Pchar t) Proc putlogfile(char c, int num, Pchar t)
Begin Begin
Str(Llen, u);
Strbig(Llen, u);
Str(20,fname); Str(20,fname);
If dologfile Then If dologfile Then
If(logfile == Null) Then If(logfile == Null) Then
@ -437,6 +440,14 @@ Begin
placeholder= 0; placeholder= 0;
/* release symbol table data */ /* release symbol table data */
EndProc EndProc
/* SJB - Scan the line for subcircuits */
Proc nupa_scan(Pchar s, int linenum)
Begin
If spos(".SUBCKT",s) ==1 Then
defsubckt( dico, s, linenum, 'U' );
EndIf
EndProc
Func Pchar nupa_copy(Pchar s, int linenum) Func Pchar nupa_copy(Pchar s, int linenum)
/* returns a copy (not quite) of s in freshly allocated memory. /* returns a copy (not quite) of s in freshly allocated memory.
@ -452,8 +463,8 @@ Func Pchar nupa_copy(Pchar s, int linenum)
- substitute placeholders for all {..} --> 10-digit numeric values. - substitute placeholders for all {..} --> 10-digit numeric values.
*/ */
Begin Begin
Str(250,u);
Str(250,keywd);
Strbig(Llen,u);
Strbig(Llen,keywd);
Pchar t; Pchar t;
short i,ls; short i,ls;
char c,d; char c,d;
@ -514,7 +525,7 @@ Begin
#ifdef TRACE_NUMPARAMS #ifdef TRACE_NUMPARAMS
printf("** SJB - in nupa_eval()\n"); printf("** SJB - in nupa_eval()\n");
printf("** SJB - processing line %3d: %s\n",linenum,s); printf("** SJB - processing line %3d: %s\n",linenum,s);
printf("** SJB - category '%c'\n");
printf("** SJB - category '%c'\n",c);
#endif /* TRACE_NUMPARAMS */ #endif /* TRACE_NUMPARAMS */
If c=='P' Then /* evaluate parameters */ If c=='P' Then /* evaluate parameters */
nupa_assignment( dico, dico->refptr[linenum] , 'N'); nupa_assignment( dico, dico->refptr[linenum] , 'N');
@ -561,8 +572,10 @@ Begin
return 1 return 1
EndFunc EndFunc
#ifdef USING_NUPATEST
/* This is use only by the nupatest program */
Func tdico * nupa_fetchinstance(void) Func tdico * nupa_fetchinstance(void)
Begin Begin
return dico return dico
EndFunc EndFunc
#endif /* USING_NUPATEST */

18
src/frontend/numparam/washprog.c

@ -118,7 +118,7 @@ EndProc
Proc saddn( Pchar s, Pchar t, short n) Proc saddn( Pchar s, Pchar t, short n)
Begin Begin
Str(250,u);
Strbig(Llen,u);
short lt= length(t); short lt= length(t);
If lt<= n Then If lt<= n Then
sadd(s,t) sadd(s,t)
@ -367,7 +367,7 @@ Begin
Word j,k,dk,ls, lst, lmt, jmax, pj; Word j,k,dk,ls, lst, lmt, jmax, pj;
Bool ok; Bool ok;
char arg; char arg;
Str(250,u);
Strbig(Llen,u);
Str(40,st); Str(40,st);
/* returns >0 If comparison Ok == length of compared Pchar */ /* returns >0 If comparison Ok == length of compared Pchar */
/*-StartProc-*/ k=0; /*-StartProc-*/ k=0;
@ -441,7 +441,7 @@ Begin
Word j,k,ps,ls; Word j,k,ps,ls;
Bool ok; Bool ok;
char endc; char endc;
Str(250,u);
Strbig(Llen,u);
/* returns >0 if comparison Ok = length of compared string */ /* returns >0 if comparison Ok = length of compared string */
/* char comparison, s may have wildcard regions with "æ" BUT 1 valid End */ /* char comparison, s may have wildcard regions with "æ" BUT 1 valid End */
/*-StartProc-*/ /*-StartProc-*/
@ -578,7 +578,7 @@ EndFunc
Func Bool getSubList(Pchar slist) Func Bool getSubList(Pchar slist)
/* read the search and substitution rule list */ /* read the search and substitution rule list */
Begin Begin
Str(250,s);
Strbig(Llen,s);
Pfile f; Pfile f;
Bool done, ok; Bool done, ok;
/*-StartProc-*/ /*-StartProc-*/
@ -629,7 +629,7 @@ EndFunc
*/ */
Bool washmore= True; /* flag that activates the postprocessor */ Bool washmore= True; /* flag that activates the postprocessor */
Str(250,obf); /* output buffer */
Strbig(Llen,obf); /* output buffer */
short iobf=0; /* its index */ short iobf=0; /* its index */
short wstate=0; /* output state machine */ short wstate=0; /* output state machine */
@ -723,7 +723,7 @@ Proc translate(Pchar bf); /* recursion */
Proc echoOut(Pchar r, char isWild, string mac[] ) Proc echoOut(Pchar r, char isWild, string mac[] )
Begin Begin
short u; short u;
Str(250,s);
Strbig(Llen,s);
/*-StartProc-*/ /*-StartProc-*/
If isWild !=0 Then If isWild !=0 Then
u= cpos(isWild,r) u= cpos(isWild,r)
@ -779,7 +779,7 @@ Begin
char c; char c;
short i,j; short i,j;
Bool escape; Bool escape;
Str(250,s);
Strbig(Llen,s);
/*-StartProc-*/ /*-StartProc-*/
escape=False; escape=False;
For i=0; i<length(r); Inc(i) Do For i=0; i<length(r); Inc(i) Do
@ -799,7 +799,7 @@ EndProc
Proc translate(Pchar bff) Proc translate(Pchar bff)
Begin /*light version, inside recursion only */ Begin /*light version, inside recursion only */
Bool done; Bool done;
Str(250,bf);
Strbig(Llen,bf);
Darray(mac, string, nargs) Darray(mac, string, nargs)
Bool ok; Bool ok;
short i,sm; short i,sm;
@ -855,7 +855,7 @@ Proc translator( Pchar fname)
BUG: is very slow. BUG: is very slow.
*/ */
Begin Begin
Str(250, outname); Str(250,bf);
Strbig(Llen, outname); Strbig(Llen,bf);
Bool done; Bool done;
Darray( mac, string, nargs) Darray( mac, string, nargs)
Pfile fin; Pfile fin;

54
src/frontend/numparam/xpressn.c

@ -12,8 +12,9 @@
/************ keywords ************/ /************ keywords ************/
Intern Str(Llen, keys); /*all my keywords*/
Intern Str(Llen, fmath); /* all math functions */
/* SJB - 150 chars is ample for this - see initkeys() */
Intern Str(150, keys); /*all my keywords*/
Intern Str(150, fmath); /* all math functions */
Intern Intern
Proc initkeys(void) Proc initkeys(void)
@ -54,7 +55,7 @@ Intern
Func Bool message( tdico * dic, Pchar s) Func Bool message( tdico * dic, Pchar s)
/* record 'dic' should know about source file and line */ /* record 'dic' should know about source file and line */
Begin Begin
Str(250,t);
Strbig(Llen,t);
Inc( dic->errcount); Inc( dic->errcount);
If (dic->srcfile != Null) And NotZ(dic->srcfile[0]) Then If (dic->srcfile != Null) And NotZ(dic->srcfile[0]) Then
scopy(t, dic->srcfile); cadd(t,':') scopy(t, dic->srcfile); cadd(t,':')
@ -166,7 +167,7 @@ Begin
ok=False; ok=False;
i=d->nbd+1; i=d->nbd+1;
While (Not ok) And (i>1) Do While (Not ok) And (i>1) Do
Dec(i);
Dec(i);
ok= steq(d->dat[i].nom, s); ok= steq(d->dat[i].nom, s);
Done Done
If Not ok Then If Not ok Then
@ -194,7 +195,7 @@ Begin
Bool err= *perr; Bool err= *perr;
Word k; Word k;
double u; double u;
Str(Llen, s);
Strbig(Llen, s);
k=entrynb(dico,t); /*no keyword*/ k=entrynb(dico,t); /*no keyword*/
/*dbg -- If k<=0 Then ws("Dico num lookup fails. ") EndIf */ /*dbg -- If k<=0 Then ws("Dico num lookup fails. ") EndIf */
While (k>0) And (dico->dat[k].tp=='P') Do While (k>0) And (dico->dat[k].tp=='P') Do
@ -269,7 +270,7 @@ Begin
short i; short i;
char c; char c;
Bool err, warn; Bool err, warn;
Str(Llen,v);
Strbig(Llen,v);
i=attrib(dico,t,op); i=attrib(dico,t,op);
err=False; err=False;
If i<=0 Then If i<=0 Then
@ -369,7 +370,7 @@ Begin
if jumped, return index to existing function if jumped, return index to existing function
*/ */
short i,j; short i,j;
Str(Llen, v);
Strbig(Llen, v);
i=attrib(dico,t,' '); j=0; i=attrib(dico,t,' '); j=0;
If i<=0 Then If i<=0 Then
err=message( dico," Symbol table overflow") err=message( dico," Symbol table overflow")
@ -501,7 +502,7 @@ Begin
short ls; short ls;
char c; char c;
Bool ok; Bool ok;
Str(Llen, t);
Strbig(Llen, t);
ls=length(s); ls=length(s);
x=0.0; x=0.0;
Repeat Repeat
@ -546,7 +547,7 @@ Begin
short k,err; short k,err;
char d; char d;
Str(20, t); Str(20, t);
Str(Llen, v);
Strbig(Llen, v);
double u; double u;
k=i; k=i;
Repeat Repeat
@ -614,7 +615,7 @@ Begin
Byte level= *plevel; Byte level= *plevel;
Bool error= *perror; Bool error= *perror;
char c,d; char c,d;
Str(Llen, v);
Strbig(Llen, v);
c=s[i-1]; c=s[i-1];
If i<ls Then If i<ls Then
d=s[i] d=s[i]
@ -775,13 +776,13 @@ Begin
Cconst(nprece,9) /*maximal nb of precedence levels*/ Cconst(nprece,9) /*maximal nb of precedence levels*/
Bool error= *perror; Bool error= *perror;
Byte state,oldstate, topop,ustack, level, kw, fu; Byte state,oldstate, topop,ustack, level, kw, fu;
double u,v;
double u=0.0,v;
double accu[nprece+1]; double accu[nprece+1];
char oper[nprece+1]; char oper[nprece+1];
char uop[nprece+1]; char uop[nprece+1];
short i,k,ls,natom, arg2; short i,k,ls,natom, arg2;
char c,d; char c,d;
Str(Llen, t);
Strbig(Llen, t);
Bool ok; Bool ok;
For i=0; i<=nprece; Inc(i) Do For i=0; i<=nprece; Inc(i) Do
accu[i]=0.0; oper[i]=' ' accu[i]=0.0; oper[i]=' '
@ -912,7 +913,7 @@ Begin
astronomic=False; astronomic=False;
If ax<1e-30 Then If ax<1e-30 Then
isint=True; isint=True;
ElsIf ax<32000 Then /*detect integers*/ rx=round(x);
ElsIf ax<32000 Then /*detect integers*/ rx=np_round(x);
dx=(x-rx)/ax; dx=(x-rx)/ax;
isint=(absf(dx)<1e-6); isint=(absf(dx)<1e-6);
EndIf EndIf
@ -936,12 +937,12 @@ Func Bool evaluate(
Byte mode) Byte mode)
Begin Begin
/* transform t to result q. mode 0: expression, mode 1: simple variable */ /* transform t to result q. mode 0: expression, mode 1: simple variable */
double u;
double u=0.0;
short k,j,lq; short k,j,lq;
char dt,fmt; char dt,fmt;
Bool numeric, done, nolookup; Bool numeric, done, nolookup;
Bool err; Bool err;
Str(Llen, v);
Strbig(Llen, v);
scopy(q,""); scopy(q,"");
numeric=False; err=False; numeric=False; err=False;
If mode==1 Then /*string?*/ If mode==1 Then /*string?*/
@ -1006,8 +1007,8 @@ Begin
short i,k,ls,level,nd, nnest; short i,k,ls,level,nd, nnest;
Bool spice3; Bool spice3;
char c,d; char c,d;
Str(Llen, q);
Str(Llen, t);
Strbig(Llen, q);
Strbig(Llen, t);
Str(20, u); Str(20, u);
spice3= cpos('3', dico->option) >0; /* we had -3 on the command line */ spice3= cpos('3', dico->option) >0; /* we had -3 on the command line */
i=0; ls=length(s); i=0; ls=length(s);
@ -1190,8 +1191,8 @@ Func Bool nupa_substitute( tdico *dico, Pchar s, Pchar r, Bool err)
Begin Begin
short i,k,ls,level, nnest, ir; short i,k,ls,level, nnest, ir;
char c,d; char c,d;
Str(Llen, q);
Str(Llen, t);
Strbig(Llen, q);
Strbig(Llen, t);
i=0; i=0;
ls=length(s); ls=length(s);
err=False; err=False;
@ -1367,8 +1368,8 @@ Func Bool nupa_assignment( tdico *dico, Pchar s, char mode)
*/ */
Begin Begin
/* s has the format: ident = expression; ident= expression ... */ /* s has the format: ident = expression; ident= expression ... */
Str(Llen, t);
Str(Llen,u);
Strbig(Llen, t);
Strbig(Llen,u);
short i,j, ls; short i,j, ls;
Byte key; Byte key;
Bool error, err; Bool error, err;
@ -1422,12 +1423,13 @@ Func Bool nupa_subcktcall( tdico *dico, Pchar s, Pchar x, Bool err)
x= a matching subckt call line, with actual params x= a matching subckt call line, with actual params
*/ */
Begin Begin
short n,m,i,j,k,g,h, narg, ls, nest;
Str(Llen,t);
Str(Llen,u);
Str(Llen,v);
Str(Llen,idlist);
short n,m,i,j,k,g,h, narg=0, ls, nest;
Strbig(Llen,t);
Strbig(Llen,u);
Strbig(Llen,v);
Strbig(Llen,idlist);
Str(80,subname); Str(80,subname);
/***** first, analyze the subckt definition line */ /***** first, analyze the subckt definition line */
n=0; /* number of parameters if any */ n=0; /* number of parameters if any */
ls=length(s); ls=length(s);

17
src/frontend/plotting/x11.c

@ -709,8 +709,6 @@ zoomin(GRAPH *graph)
char buf[BSIZE_SP]; char buf[BSIZE_SP];
char buf2[128]; char buf2[128];
char *t; char *t;
wordlist *wl;
int dummy;
Window rootwindow, childwindow; Window rootwindow, childwindow;
int rootx, rooty; int rootx, rooty;
@ -790,11 +788,16 @@ zoomin(GRAPH *graph)
/* don't use the following if using GNU Readline - AV */ /* don't use the following if using GNU Readline - AV */
#ifndef HAVE_GNUREADLINE #ifndef HAVE_GNUREADLINE
/* hack for Gordon Jacobs */
/* add to history list if plothistory is set */
if (cp_getvar("plothistory", VT_BOOL, (char *) &dummy)) {
wl = cp_parse(buf);
(void) cp_addhistent(cp_event++, wl);
{
wordlist *wl;
int dummy;
/* hack for Gordon Jacobs */
/* add to history list if plothistory is set */
if (cp_getvar("plothistory", VT_BOOL, (char *) &dummy)) {
wl = cp_parse(buf);
(void) cp_addhistent(cp_event++, wl);
}
} }
#endif /* HAVE_GNUREADLINE */ #endif /* HAVE_GNUREADLINE */

10
src/frontend/resource.c

@ -19,6 +19,14 @@ Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
#ifdef HAVE__MEMAVL #ifdef HAVE__MEMAVL
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
/*
* The ngspice.h file included above defines BOOLEAN (via bool.h) and this
* clashes with the definition obtained from windows.h (via winnt.h).
* However, BOOLEAN is not used by this file so we can work round this problem
* by undefining BOOLEAN before including windows.h
* SJB - April 2005
*/
#undef BOOLEAN
#include <windows.h> #include <windows.h>
#endif #endif
@ -199,7 +207,7 @@ printres(char *name)
# else # else
# ifdef HAVE_FTIME # ifdef HAVE_FTIME
struct timeb timenow; struct timeb timenow;
int sec, msec;
// int sec, msec; sjb
ftime(&timenow); ftime(&timenow);
timediff(&timenow, &timebegin, &total, &totalu); timediff(&timenow, &timebegin, &total, &totalu);
// totalu /= 1000; hvogt // totalu /= 1000; hvogt

12
src/frontend/spiceif.c

@ -702,8 +702,11 @@ parmtovar(IFvalue *pv, IFparm *opt)
return (vv); return (vv);
} }
/* Extract the IFparm structure from the device. If isdev is TRUE, then get
* the DEVmodQuest, otherwise get the DEVquest.
/* Extract the parameter (IFparm structure) from the device or device's model.
* If do_mode is TRUE then look in the device's parameters
* If do_mode is FALSE then look in the device model's parameters
* If inout equals 1 then look only for parameters with the IF_SET type flag
* if inout equals 0 then look only for parameters with the IF_ASK type flag
*/ */
static IFparm * static IFparm *
@ -711,9 +714,6 @@ parmlookup(IFdevice *dev, GENinstance **inptr, char *param, int do_model, int in
{ {
int i; int i;
/* fprintf(cp_err, "Called: parmlookup(%x, %c, %s)\n",
dev, isdev, param); */
/* First try the device questions... */ /* First try the device questions... */
if (!do_model && dev->numInstanceParms) { if (!do_model && dev->numInstanceParms) {
for (i = 0; i < *(dev->numInstanceParms); i++) { for (i = 0; i < *(dev->numInstanceParms); i++) {
@ -723,7 +723,7 @@ parmlookup(IFdevice *dev, GENinstance **inptr, char *param, int do_model, int in
continue; continue;
else if ((((dev->instanceParms[i].dataType & IF_SET) && inout == 1) else if ((((dev->instanceParms[i].dataType & IF_SET) && inout == 1)
|| ((dev->instanceParms[i].dataType & IF_ASK) && inout == 0)) || ((dev->instanceParms[i].dataType & IF_ASK) && inout == 0))
&& eq(dev->instanceParms[i].keyword, param))
&& cieq(dev->instanceParms[i].keyword, param))
{ {
if (dev->instanceParms[i].dataType & IF_REDUNDANT) if (dev->instanceParms[i].dataType & IF_REDUNDANT)
i -= 1; i -= 1;

78
src/frontend/subckt.c

@ -61,7 +61,7 @@ Modified: 2000 AlansFixes
#include "variable.h" #include "variable.h"
/* Uncomment to turn on tracing for the Numparam */ /* Uncomment to turn on tracing for the Numparam */
//#define TRACE_NUMPARAMS
/*#define TRACE_NUMPARAMS*/
#ifdef NUMPARAMS #ifdef NUMPARAMS
#define NUPADECKCOPY 0 #define NUPADECKCOPY 0
@ -71,6 +71,7 @@ Modified: 2000 AlansFixes
extern char * nupa_copy(char *s, int linenum); extern char * nupa_copy(char *s, int linenum);
extern int nupa_eval(char *s, int linenum); extern int nupa_eval(char *s, int linenum);
extern int nupa_signal(int sig, char *info); extern int nupa_signal(int sig, char *info);
extern void nupa_scan(char * s, int linenum); // sjb
#endif #endif
/* ----- static declarations ----- */ /* ----- static declarations ----- */
@ -117,13 +118,13 @@ struct subs {
static wordlist *modnames, *submod; static wordlist *modnames, *submod;
static struct subs *subs = NULL; static struct subs *subs = NULL;
static bool nobjthack = FALSE; static bool nobjthack = FALSE;
static char start[32], sbend[32], invoke[32], model[32];
#ifdef NUMPARAMS #ifdef NUMPARAMS
static char NumParams='y';
/* flag indicating use of the experimental numparams library */
static bool use_numparams = FALSE;
#endif #endif
static char start[32], sbend[32], invoke[32], model[32];
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
/* inp_subcktexpand is the top level function which translates */ /* inp_subcktexpand is the top level function which translates */
/* .subckts into mainlined code. Note that there are two things */ /* .subckts into mainlined code. Note that there are two things */
@ -147,7 +148,7 @@ inp_subcktexpand(struct line *deck)
char *s; char *s;
#ifdef NUMPARAMS #ifdef NUMPARAMS
int ok; int ok;
#endif
#endif /* NUMPARAMS */
wordlist *wl; wordlist *wl;
modnames = NULL; modnames = NULL;
@ -162,10 +163,12 @@ inp_subcktexpand(struct line *deck)
if(!cp_getvar("modelline", VT_STRING, model)) if(!cp_getvar("modelline", VT_STRING, model))
(void) strcpy(model, ".model"); (void) strcpy(model, ".model");
(void) cp_getvar("nobjthack", VT_BOOL, (char *) &nobjthack); (void) cp_getvar("nobjthack", VT_BOOL, (char *) &nobjthack);
#ifdef NUMPARAMS #ifdef NUMPARAMS
(void) cp_getvar("numparams", VT_BOOL, (char *) &use_numparams);
/* deck has .control sections already removed, but not comments */ /* deck has .control sections already removed, but not comments */
if ( NumParams == 'y' ) {
if ( use_numparams ) {
#ifdef TRACE_NUMPARAMS #ifdef TRACE_NUMPARAMS
printf("Numparams is processing this deck:\n"); printf("Numparams is processing this deck:\n");
@ -174,9 +177,14 @@ inp_subcktexpand(struct line *deck)
printf("%3d:%s\n",c->li_linenum, c->li_line); printf("%3d:%s\n",c->li_linenum, c->li_line);
c= c->li_next; c= c->li_next;
} }
#endif
#endif /* TRACE_NUMPARAMS */
ok = nupa_signal( NUPADECKCOPY, NULL);
ok = nupa_signal( NUPADECKCOPY, NULL);
c=deck;
while ( c != NULL) { /* first Numparam pass */
nupa_scan(c->li_line, c->li_linenum);
c= c->li_next;
}
c=deck; c=deck;
while ( c != NULL) { /* first Numparam pass */ while ( c != NULL) { /* first Numparam pass */
c->li_line = nupa_copy(c->li_line, c->li_linenum); c->li_line = nupa_copy(c->li_line, c->li_linenum);
@ -190,10 +198,10 @@ inp_subcktexpand(struct line *deck)
printf("%3d:%s\n",c->li_linenum, c->li_line); printf("%3d:%s\n",c->li_linenum, c->li_line);
c= c->li_next; c= c->li_next;
} }
#endif
#endif /* TRACE_NUMPARAMS */
}
#endif
}
#endif /* NUMPARAMS */
/* Get all the model names so we can deal with BJTs, etc. /* Get all the model names so we can deal with BJTs, etc.
* Stick all the model names into the doubly-linked wordlist modnames. * Stick all the model names into the doubly-linked wordlist modnames.
@ -217,7 +225,7 @@ inp_subcktexpand(struct line *deck)
for(w = modnames; w; w = w->wl_next) for(w = modnames; w; w = w->wl_next)
printf("%s\n",w->wl_word); printf("%s\n",w->wl_word);
} }
#endif
#endif /* TRACE */
/* Let's do a few cleanup things... Get rid of ( ) around node /* Let's do a few cleanup things... Get rid of ( ) around node
@ -228,7 +236,7 @@ inp_subcktexpand(struct line *deck)
#ifdef TRACE #ifdef TRACE
/* SDB debug statement */ /* SDB debug statement */
printf("In inp_subcktexpand, found a .subckt: %s\n", c->li_line); printf("In inp_subcktexpand, found a .subckt: %s\n", c->li_line);
#endif
#endif /* TRACE */
for (s = c->li_line; *s && (*s != '('); s++) /* Iterate charwise along line until ( is found */ for (s = c->li_line; *s && (*s != '('); s++) /* Iterate charwise along line until ( is found */
; ;
if (*s) { if (*s) {
@ -264,7 +272,7 @@ inp_subcktexpand(struct line *deck)
#ifdef TRACE #ifdef TRACE
/* SDB debug statement */ /* SDB debug statement */
printf("In inp_subcktexpand, about to call doit.\n"); printf("In inp_subcktexpand, about to call doit.\n");
#endif
#endif /* TRACE */
ll = doit(deck); ll = doit(deck);
// SJB: free up the modnames linked list now we are done with it // SJB: free up the modnames linked list now we are done with it
@ -276,13 +284,17 @@ inp_subcktexpand(struct line *deck)
/* Now check to see if there are still subckt instances undefined... */ /* Now check to see if there are still subckt instances undefined... */
if (ll!=NULL) for (c = ll; c; c = c->li_next) if (ll!=NULL) for (c = ll; c; c = c->li_next)
if (ciprefix(invoke, c->li_line)) { if (ciprefix(invoke, c->li_line)) {
fprintf(cp_err, "Error: unknown subckt: %s\n",
c->li_line);
fprintf(cp_err, "Error: unknown subckt: %s\n", c->li_line);
#ifdef NUMPARAMS
if ( use_numparams ) {
ok= ok && nupa_signal(NUPAEVALDONE, NULL);
}
#endif /* NUMPARAMS */
return NULL; return NULL;
} }
#ifdef NUMPARAMS #ifdef NUMPARAMS
if ( NumParams == 'y' ) {
if ( use_numparams ) {
/* the NUMPARAM final line translation pass */ /* the NUMPARAM final line translation pass */
ok= ok && nupa_signal(NUPASUBDONE, NULL); ok= ok && nupa_signal(NUPASUBDONE, NULL);
c= ll; c= ll;
@ -297,10 +309,10 @@ inp_subcktexpand(struct line *deck)
printf("%3d:%s\n",c->li_linenum, c->li_line); printf("%3d:%s\n",c->li_linenum, c->li_line);
c= c->li_next; c= c->li_next;
} }
#endif
#endif /* TRACE_NUMPARAMS */
ok= ok && nupa_signal(NUPAEVALDONE, NULL); ok= ok && nupa_signal(NUPAEVALDONE, NULL);
} }
#endif
#endif /* NUMPARAMS */
return (ll); /* return the spliced deck. */ return (ll); /* return the spliced deck. */
} }
@ -322,7 +334,7 @@ doit(struct line *deck)
struct line *c, *last, *lc, *lcc; struct line *c, *last, *lc, *lcc;
#ifdef NUMPARAMS #ifdef NUMPARAMS
struct line *savenext; struct line *savenext;
#endif
#endif /* NUMPARAMS */
struct subs *sss = (struct subs *) NULL, *ks; /* *sss and *ks temporarily hold decks to substitute */ struct subs *sss = (struct subs *) NULL, *ks; /* *sss and *ks temporarily hold decks to substitute */
char *s, *t, *scname, *subname; char *s, *t, *scname, *subname;
int nest, numpasses = MAXNEST, i; int nest, numpasses = MAXNEST, i;
@ -388,8 +400,8 @@ doit(struct line *deck)
if (!lcc) /* if lcc is null, then no .ends was found. */ if (!lcc) /* if lcc is null, then no .ends was found. */
lcc = last; lcc = last;
#ifdef NUMPARAMS #ifdef NUMPARAMS
if ( NumParams != 'y' )
#endif
if ( use_numparams==FALSE )
#endif /* NUMPARAMS */
lcc->li_next = NULL; /* shouldn't we free some memory here????? */ lcc->li_next = NULL; /* shouldn't we free some memory here????? */
/* At this point, last points to the .subckt card, and lcc points to the .ends card */ /* At this point, last points to the .subckt card, and lcc points to the .ends card */
@ -424,7 +436,7 @@ doit(struct line *deck)
#ifdef NUMPARAMS #ifdef NUMPARAMS
/*gp */ c->li_next = NULL; /* Numparam needs line c */ /*gp */ c->li_next = NULL; /* Numparam needs line c */
c->li_line[0] = '*'; /* comment it out */ c->li_line[0] = '*'; /* comment it out */
#endif
#endif /* NUMPARAMS */
} }
else { /* line is neither .ends nor .subckt. */ else { /* line is neither .ends nor .subckt. */
/* make lc point to this card, and advance last to next card. */ /* make lc point to this card, and advance last to next card. */
@ -534,12 +546,12 @@ doit(struct line *deck)
/* Now splice the decks together. */ /* Now splice the decks together. */
#ifdef NUMPARAMS #ifdef NUMPARAMS
savenext = c->li_next; savenext = c->li_next;
if ( NumParams != 'y') {
/* old style: c will drop a dangling pointer: memory leak */
if (lc)
lc->li_next = lcc;
else
deck = lcc;
if ( use_numparams==FALSE ) {
/* old style: c will drop a dangling pointer: memory leak */
if (lc)
lc->li_next = lcc;
else
deck = lcc;
} else { } else {
/* ifdef NUMPARAMS, keep the invoke line as a comment */ /* ifdef NUMPARAMS, keep the invoke line as a comment */
c->li_next = lcc; c->li_next = lcc;
@ -550,13 +562,13 @@ doit(struct line *deck)
lc->li_next = lcc; lc->li_next = lcc;
else else
deck = lcc; deck = lcc;
#endif
#endif /* NUMPARAMS */
while (lcc->li_next != NULL) while (lcc->li_next != NULL)
lcc = lcc->li_next; lcc = lcc->li_next;
lcc->li_next = c->li_next; lcc->li_next = c->li_next;
#ifdef NUMPARAMS #ifdef NUMPARAMS
lcc->li_next = savenext; lcc->li_next = savenext;
#endif
#endif /* NUMPARAMS */
c = lcc->li_next; c = lcc->li_next;
lc = lcc; lc = lcc;
tfree(tofree); tfree(tofree);

Loading…
Cancel
Save