ngspice/src/frontend/inpcom.c

3431 lines
108 KiB
C

/**********
Copyright 1990 Regents of the University of California. All rights reserved.
Author: 1985 Wayne A. Christopher
**********/
/*
* For dealing with spice input decks and command scripts
*/
/* h_vogt 20 April 2008
* For xspice and num_pram compatibility .cmodel added
* .cmodel will be replaced by .model in inp_fix_param_values()
* and then the entire line is skipped (will not be changed by this function).
* Usage of numparam requires {} around the parameters in the .cmodel line.
*/
/*
* 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.
* Note requirement for $ to be followed by a space. This is to avoid conflict
* with use in front of a variable.
* 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
* Fixed memory leaks in inp_readall() when first(?) line of input begins with a '@'.
* Fixed memory leaks in inp_readall() when .include lines have errors
* Fixed crash where a NULL pointer gets freed in inp_readall()
*/
#include "ngspice.h"
#ifdef HAVE_LIBGEN_H /* dirname */
#include <libgen.h>
#define HAVE_DECL_BASENAME 1
#endif
#ifdef HAVE_LIBIBERTY_H /* asprintf etc. */
#include <libiberty.h>
#undef AND /* obsolete macro in ansidecl.h */
#endif
#include "cpdefs.h"
#include "ftedefs.h"
#include "dvec.h"
#include "fteinp.h"
#include "inpcom.h"
#include "variable.h"
#include "../misc/util.h" /* dirname() */
#include "../misc/stringutil.h"
#include <wordlist.h>
#ifdef XSPICE
/* gtri - add - 12/12/90 - wbk - include new stuff */
#include "ipctiein.h"
#include "enh.h"
/* gtri - end - 12/12/90 */
#endif
/* SJB - Uncomment this line for debug tracing */
/*#define TRACE*/
/* uncomment this line for getting deck output after all
manipulations into debug-out.txt */
/* #define OUTDECK */
#ifdef HAS_WINDOWS
void winmessage(char* new_msg);
#endif
/* globals -- wanted to avoid complicating inp_readall interface */
static char *library_file[1000];
static char *library_name[1000][1000];
struct line *library_ll_ptr[1000][1000];
struct line *libraries[1000];
int num_libraries;
int num_lib_names[1000];
static char *global;
static char *subckt_w_params[1000];
static int num_subckt_w_params;
static char *func_names[1000];
static char *func_params[1000][1000];
static char *func_macro[5000];
static int num_functions;
static int num_parameters[1000];
/* Collect information for dynamic allocation of numparam arrays */
/* number of lines in input deck */
int dynmaxline; /* inpcom.c 1529 */
/* max. line length in input deck */
int dynLlen; /* inpcom.c 1526 */
/* number of lines in deck after expansion */
int dynMaxckt = 0; /* subckt.c 307 */
/* number of parameter substitutions */
long dynsubst; /* spicenum.c 221 */
/* static declarations */
static char * readline(FILE *fd);
static int get_number_terminals( char *c );
static void inp_stripcomments_deck(struct line *deck);
static void inp_stripcomments_line(char * s);
static void inp_fix_for_numparam(struct line *deck);
static void inp_remove_excess_ws(struct line *deck);
static void inp_determine_libraries(struct line *deck, char *lib_name);
static void inp_init_lib_data();
static void inp_grab_func(struct line *deck);
static void inp_fix_inst_calls_for_numparam( struct line *deck);
static void inp_expand_macros_in_func();
static void inp_expand_macros_in_deck( struct line *deck );
static void inp_fix_param_values( struct line *deck );
static void inp_reorder_params( struct line *deck, struct line *list_head, struct line *end );
static int inp_split_multi_param_lines( struct line *deck, int line_number );
static void inp_sort_params( struct line *start_card, struct line *end_card, struct line *card_bf_start, struct line *s_c, struct line *e_c );
static char* inp_remove_ws( char *s );
/*-------------------------------------------------------------------------*
* This routine reads a line (of arbitrary length), up to a '\n' or 'EOF' *
* and returns a pointer to the resulting null terminated string. *
* The '\n' if found, is included in the returned string. *
* From: jason@ucbopal.BERKELEY.EDU (Jason Venner) *
* Newsgroups: net.sources *
*-------------------------------------------------------------------------*/
#define STRGROW 256
static char *
readline(FILE *fd)
{
int c;
int memlen;
char *strptr;
int strlen;
strptr = NULL;
strlen = 0;
memlen = STRGROW;
strptr = tmalloc(memlen);
memlen -= 1; /* Save constant -1's in while loop */
while((c = getc(fd)) != EOF) {
if (strlen == 0 && (c == '\t' || c == ' ')) /* Leading spaces away */
continue;
strptr[strlen] = c;
strlen++;
if( strlen >= memlen ) {
memlen += STRGROW;
if( !(strptr = trealloc(strptr, memlen + 1))) {
return (NULL);
}
}
if (c == '\n') {
break;
}
}
if (!strlen) {
tfree(strptr);
return (NULL);
}
// strptr[strlen] = '\0';
/* Trim the string */
strptr = trealloc(strptr, strlen + 1);
strptr[strlen] = '\0';
return (strptr);
}
/*-------------------------------------------------------------------------*
* Look up the variable sourcepath and try everything in the list in order *
* if the file isn't in . and it isn't an abs path name. *
*-------------------------------------------------------------------------*/
FILE *
inp_pathopen(char *name, char *mode)
{
FILE *fp;
char buf[BSIZE_SP];
struct variable *v;
/* If this is an abs pathname, or there is no sourcepath var, just
* do an fopen.
*/
if (index(name, DIR_TERM)
|| !cp_getvar("sourcepath", VT_LIST, (char *) &v))
return (fopen(name, mode));
while (v) {
switch (v->va_type) {
case VT_STRING:
cp_wstrip(v->va_string);
(void) sprintf(buf, "%s%s%s", v->va_string, DIR_PATHSEP, name);
break;
case VT_NUM:
(void) sprintf(buf, "%d%s%s", v->va_num, DIR_PATHSEP, name);
break;
case VT_REAL: /* This is foolish */
(void) sprintf(buf, "%g%s%s", v->va_real, DIR_PATHSEP, name);
break;
}
if ((fp = fopen(buf, mode)))
return (fp);
v = v->va_next;
}
return (NULL);
}
/* replace " gnd " by " 0 "
and then remove excessive white spaces */
static void
inp_fix_gnd_name( struct line *deck ) {
struct line *c = deck;
char *gnd;
bool found_gnd = FALSE;
while ( c != NULL ) {
gnd = c->li_line;
if ( *gnd == '*' ) { c = c->li_next; continue; }
/* replace " gnd " by " 0 " */
while ( (gnd = strstr( gnd, "gnd " ) ) ) {
if ( isspace(*(gnd-1)) ) {
memcpy( gnd, "0 ", 4 );
}
gnd += 4;
found_gnd = TRUE;
}
/* remove white spaces after replacement, retain " 0 " */
if (found_gnd)
c->li_line = inp_remove_ws(c->li_line);
c = c->li_next;
found_gnd = FALSE;
}
}
static struct line*
create_new_card( char *card_str, int *line_number ) {
char *str = strdup(card_str);
struct line *newcard = alloc(struct line);
newcard->li_line = str;
newcard->li_linenum = *line_number;
newcard->li_error = NULL;
newcard->li_actual = NULL;
*line_number = *line_number + 1;
return newcard;
}
static void
inp_chk_for_multi_in_vcvs( struct line *deck, int *line_number ) {
struct line *c, *a_card, *model_card, *next_card;
char *line, *bool_ptr, *str_ptr1, *str_ptr2, keep, *comma_ptr, *xy_values1[5], *xy_values2[5];
char *node_str, *ctrl_node_str, *xy_str1, *model_name, *fcn_name;
char big_buf[1000];
int xy_count1 = 0, xy_count2 = 0;
for ( c = deck; c != NULL; c = c->li_next ) {
str_ptr1 = line = c->li_line;
if ( *line == 'e' ) {
if ( (bool_ptr = strstr( line, "nand(" )) ||
(bool_ptr = strstr( line, "and(" )) ||
(bool_ptr = strstr( line, "nor(" )) ||
(bool_ptr = strstr( line, "or(" )) ) {
while ( !isspace(*str_ptr1) ) str_ptr1++;
keep = *str_ptr1; *str_ptr1 = '\0';
model_name = strdup(line); *str_ptr1 = keep;
str_ptr2 = bool_ptr - 1;
while ( isspace(*str_ptr1) ) str_ptr1++;
while ( isspace(*str_ptr2) ) str_ptr2--;
str_ptr2++;
keep = *str_ptr2; *str_ptr2 = '\0';
node_str = strdup(str_ptr1); *str_ptr2 = keep;
str_ptr1 = bool_ptr + 1;
while ( *str_ptr1 != '(' ) str_ptr1++;
*str_ptr1 = '\0'; fcn_name = strdup(bool_ptr);
*str_ptr1 = '(';
str_ptr1 = strstr( str_ptr1, ")" );
str_ptr1++;
comma_ptr = str_ptr2 = strstr( line, "," );
str_ptr2--;
while( isspace(*str_ptr2) ) str_ptr2--;
while ( isspace(*str_ptr1) ) str_ptr1++;
if ( *str_ptr2 == '}' ) {
while ( *str_ptr2 != '{' ) str_ptr2--;
xy_str1 = str_ptr2; str_ptr2--;
while ( isspace(*str_ptr2) ) str_ptr2--;
str_ptr2++;
} else {
while ( !isspace(*str_ptr2) ) str_ptr2--;
xy_str1 = str_ptr2 + 1;
while ( isspace(*str_ptr2) ) str_ptr2--;
str_ptr2++;
}
keep = *str_ptr2; *str_ptr2 = '\0';
ctrl_node_str = strdup(str_ptr1); *str_ptr2 = keep;
str_ptr1 = comma_ptr + 1;
while ( isspace(*str_ptr1) ) str_ptr1++;
if ( *str_ptr1 == '{' ) {
while ( *str_ptr1 != '}' ) str_ptr1++;
str_ptr1++;
} else {
while ( !isspace(*str_ptr1) ) str_ptr1++;
}
keep = *str_ptr1; *str_ptr1 = '\0';
xy_count1 = get_comma_separated_values( xy_values1, xy_str1 );
*str_ptr1 = keep;
while ( isspace(*str_ptr1) ) str_ptr1++;
xy_count2 = get_comma_separated_values( xy_values2, str_ptr1 );
// place restrictions on only having 2 point values; this can change later
if ( xy_count1 != 2 && xy_count2 != 2 ) {
fprintf(stderr,"ERROR: only expecting 2 pair values for multi-input vcvs!\n");
}
sprintf( big_buf, "%s %%vd[ %s ] %%vd( %s ) %s", model_name, ctrl_node_str, node_str, model_name );
a_card = create_new_card( big_buf, line_number );
*a_card->li_line = 'a';
sprintf( big_buf, ".model %s multi_input_pwl ( x = [%s %s] y = [%s %s] model = \"%s\" )", model_name, xy_values1[0], xy_values2[0],
xy_values1[1], xy_values2[1], fcn_name );
model_card = create_new_card( big_buf, line_number );
tfree(model_name); tfree(node_str); tfree(fcn_name); tfree(ctrl_node_str);
tfree(xy_values1[0]); tfree(xy_values1[1]); tfree(xy_values2[0]); tfree(xy_values2[1]);
*c->li_line = '*';
next_card = c->li_next;
c->li_next = a_card;
a_card->li_next = model_card;
model_card->li_next = next_card;
}
}
}
}
static void
inp_add_control_section( struct line *deck, int *line_number ) {
struct line *c, *newcard, *prev_card = NULL;
bool found_control = FALSE, found_run = FALSE;
bool found_end = FALSE;
char *op_line = NULL, rawfile[1000], *line;
for ( c = deck; c != NULL; c = c->li_next ) {
if ( *c->li_line == '*' ) continue;
if ( ciprefix( ".op ", c->li_line ) ) {
*c->li_line = '*';
op_line = c->li_line + 1;
}
if ( ciprefix( ".end", c->li_line ) ) found_end = TRUE;
if ( found_control && ciprefix( "run", c->li_line ) ) found_run = TRUE;
if ( ciprefix( ".control", c->li_line ) ) found_control = TRUE;
if ( ciprefix( ".endc", c->li_line ) ) {
found_control = FALSE;
if ( !found_run ) {
newcard = create_new_card( "run", line_number );
prev_card->li_next = newcard;
newcard->li_next = c;
prev_card = newcard;
found_run = TRUE;
}
if ( cp_getvar( "rawfile", VT_STRING, rawfile ) ) {
line = tmalloc( strlen("write") + strlen(rawfile) + 2 );
sprintf(line, "write %s", rawfile);
newcard = create_new_card( line, line_number );
prev_card->li_next = newcard;
newcard->li_next = c;
prev_card = newcard;
tfree(line);
}
}
prev_card = c;
}
// check if need to add control section
if ( !found_run && found_end ) {
prev_card = deck->li_next;
newcard = create_new_card( ".endc", line_number );
deck->li_next = newcard;
newcard->li_next = prev_card;
if ( cp_getvar( "rawfile", VT_STRING, rawfile ) ) {
line = tmalloc( strlen("write") + strlen(rawfile) + 2 );
sprintf(line, "write %s", rawfile);
prev_card = deck->li_next;
newcard = create_new_card( line, line_number );
deck->li_next = newcard;
newcard->li_next = prev_card;
tfree(line);
}
if ( op_line != NULL ) {
prev_card = deck->li_next;
newcard = create_new_card( op_line, line_number );
deck->li_next = newcard;
newcard->li_next = prev_card;
}
prev_card = deck->li_next;
newcard = create_new_card( "run", line_number );
deck->li_next = newcard;
newcard->li_next = prev_card;
prev_card = deck->li_next;
newcard = create_new_card( ".control", line_number );
deck->li_next = newcard;
newcard->li_next = prev_card;
}
}
// look for shell-style end-of-line continuation '\\'
static bool
chk_for_line_continuation( char *line ) {
char *ptr = line + strlen(line) - 1;
if ( *line != '*' && *line != '$' ) {
while ( ptr >= line && *ptr && isspace(*ptr) ) ptr--;
if ( (ptr-1) >= line && *ptr == '\\' && *(ptr-1) && *(ptr-1) == '\\' ) {
*ptr = ' '; *(ptr-1) = ' ';
return TRUE;
}
}
return FALSE;
}
//
// change .macro --> .subckt
// .eom --> .ends
// .subckt (1 2 3) --> .subckt 1 2 3
// v(1,11) --> 0
// x1 (1 2 3) --> x1 1 2 3
// .param func1(x,y) = {x*y} --> .func func1(x,y) {x*y}
//
static void
inp_fix_macro_param_func_paren_io( struct line *begin_card ) {
struct line *card;
char *str_ptr, *new_str, *open_paren_ptr, *search_ptr, *fcn_name;
bool is_func = FALSE;
for ( card = begin_card; card != NULL; card = card->li_next ) {
if ( *card->li_line == '*' ) continue;
// zero out any voltage node references on .param lines
if ( ciprefix( ".param", card->li_line ) ) {
search_ptr = card->li_line;
while( ( open_paren_ptr = strstr( search_ptr, "(" ) ) ) {
fcn_name = open_paren_ptr - 1;
while ( *fcn_name != '\0' && fcn_name != search_ptr && (isalnum(*fcn_name) || *fcn_name == '_' ) ) fcn_name--;
if ( fcn_name != search_ptr ) fcn_name++;
*open_paren_ptr = '\0';
if ( strcmp( fcn_name, "v" ) == 0 ) {
*open_paren_ptr = ' ';
*fcn_name = '0';
fcn_name++;
while ( *fcn_name != ')' ) { *fcn_name = ' '; fcn_name++; }
*fcn_name = ' ';
}
else {
*open_paren_ptr = '(';
}
search_ptr = open_paren_ptr + 1;
}
}
if ( ciprefix( ".macro", card->li_line ) || ciprefix( ".eom", card->li_line ) ) {
str_ptr = card->li_line;
while( !isspace(*str_ptr) ) str_ptr++;
if ( ciprefix( ".macro", card->li_line ) ) {
new_str = tmalloc( strlen(".subckt") + strlen(str_ptr) + 1 );
sprintf( new_str, ".subckt%s", str_ptr );
} else {
new_str = tmalloc( strlen(".ends") + strlen(str_ptr) + 1 );
sprintf( new_str, ".ends%s", str_ptr );
}
tfree( card->li_line );
card->li_line = new_str;
}
if ( ciprefix( ".subckt", card->li_line ) || ciprefix( "x", card->li_line ) ) {
str_ptr = card->li_line;
while( !isspace(*str_ptr) ) str_ptr++; // skip over .subckt, instance name
while( isspace(*str_ptr) ) str_ptr++;
if ( ciprefix( ".subckt", card->li_line ) ) {
while( !isspace(*str_ptr) ) str_ptr++; // skip over subckt name
while( isspace(*str_ptr) ) str_ptr++;
}
if ( *str_ptr == '(' ) {
*str_ptr = ' ';
while ( *str_ptr && *str_ptr != '\0' ) {
if ( *str_ptr == ')' ) { *str_ptr = ' '; break; }
str_ptr++;
}
}
}
is_func = FALSE;
if ( ciprefix( ".param", card->li_line ) ) {
str_ptr = card->li_line;
while ( !isspace( *str_ptr ) ) str_ptr++; // skip over .param
while ( isspace( *str_ptr ) ) str_ptr++;
while ( !isspace( *str_ptr ) && *str_ptr != '=' ) {
if ( *str_ptr == '(' ) is_func = TRUE;
str_ptr++;
}
if ( is_func ) {
if ( ( str_ptr = strstr( card->li_line, "=" ) ) ) *str_ptr = ' ';
str_ptr = card->li_line + 1;
*str_ptr = 'f'; *(str_ptr+1) = 'u'; *(str_ptr+2) = 'n';
*(str_ptr+3) = 'c'; *(str_ptr+4) = ' ';
}
}
}
}
static char *
get_instance_subckt( char *line )
{
char *equal_ptr = NULL, *end_ptr = line + strlen(line) - 1, *inst_name_ptr = NULL, *inst_name = NULL;
char keep = ' ';
// see if instance has parameters
if ( ( equal_ptr = strstr( line, "=" ) ) ) {
end_ptr = equal_ptr - 1;
while ( isspace(*end_ptr) ) end_ptr--;
while ( !isspace(*end_ptr) ) end_ptr--;
while ( isspace(*end_ptr) ) end_ptr--;
end_ptr++;
keep = *end_ptr;
*end_ptr = '\0';
}
inst_name_ptr = end_ptr;
while ( !isspace(*inst_name_ptr) ) inst_name_ptr--;
inst_name_ptr++;
inst_name = strdup(inst_name_ptr);
if ( equal_ptr ) *end_ptr = keep;
return inst_name;
}
static char*
get_subckt_model_name( char *line )
{
char *name = line, *end_ptr = NULL, *subckt_name;
char keep;
while ( !isspace( *name ) ) name++; // eat .subckt|.model
while ( isspace( *name ) ) name++;
end_ptr = name;
while ( !isspace( *end_ptr ) ) end_ptr++;
keep = *end_ptr;
*end_ptr = '\0';
subckt_name = strdup(name);
*end_ptr = keep;
return subckt_name;
}
static char*
get_model_name( char *line, int num_terminals )
{
char *beg_ptr = line, *end_ptr, keep, *model_name = NULL;
int i = 0;
while ( !isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++; /* eat device name */
while ( isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++;
for ( i = 0; i < num_terminals; i++ ) { /* skip the terminals */
while ( !isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++;
while ( isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++;
}
if ( *line == 'r' ) { /* special dealing for r models */
if((*beg_ptr=='+') || (*beg_ptr=='-') || isdigit(*beg_ptr)) { /* looking for a value before model */
while ( !isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++; /* skip the value */
while ( isspace( *beg_ptr ) && *beg_ptr != '\0' ) beg_ptr++;
}
}
end_ptr = beg_ptr;
while ( *end_ptr != '\0' && !isspace( *end_ptr ) ) end_ptr++;
keep = *end_ptr;
*end_ptr = '\0';
model_name = strdup( beg_ptr );
*end_ptr = keep;
return model_name;
}
static char *
get_adevice_model_name( char *line )
{
char *model_name, *ptr_end = line + strlen(line), *ptr_beg, keep;
while ( isspace( *(ptr_end-1) ) ) ptr_end--;
ptr_beg = ptr_end - 1;
while ( !isspace(*ptr_beg) ) ptr_beg--;
ptr_beg++;
keep = *ptr_end; *ptr_end = '\0';
model_name = strdup(ptr_beg);
*ptr_end = keep;
return model_name;
}
static void
get_subckts_for_subckt( struct line *start_card, char *subckt_name,
char *used_subckt_names[], int *num_used_subckt_names,
char *used_model_names[], int *num_used_model_names,
bool has_models )
{
struct line *card;
char *line = NULL, *curr_subckt_name, *inst_subckt_name, *model_name, *new_names[100];
bool found_subckt = FALSE, have_subckt = FALSE, found_model = FALSE;
int i, num_terminals = 0, tmp_cnt = 0;
for ( card = start_card; card != NULL; card = card->li_next ) {
line = card->li_line;
if ( *line == '*' ) continue;
if ( ( ciprefix( ".ends", line ) || ciprefix( ".eom", line ) ) && found_subckt )
break;
if ( ciprefix( ".subckt", line ) || ciprefix( ".macro", line ) ) {
curr_subckt_name = get_subckt_model_name( line );
if ( strcmp( curr_subckt_name, subckt_name ) == 0 ) {
found_subckt = TRUE;
}
tfree(curr_subckt_name);
}
if ( found_subckt ) {
if ( *line == 'x' ) {
inst_subckt_name = get_instance_subckt( line );
have_subckt = FALSE;
for ( i = 0; i < *num_used_subckt_names; i++ )
if ( strcmp( used_subckt_names[i], inst_subckt_name ) == 0 )
have_subckt = TRUE;
if ( !have_subckt ) {
new_names[tmp_cnt++] = used_subckt_names[*num_used_subckt_names] = inst_subckt_name;
*num_used_subckt_names = *num_used_subckt_names + 1;
}
else tfree( inst_subckt_name );
}
else if ( *line == 'a' ) {
model_name = get_adevice_model_name( line );
found_model = FALSE;
for ( i = 0; i < *num_used_model_names; i++ )
if ( strcmp( used_model_names[i], model_name ) == 0 ) found_model = TRUE;
if ( !found_model ) {
used_model_names[*num_used_model_names] = model_name;
*num_used_model_names = *num_used_model_names + 1;
}
else tfree( model_name );
}
else if ( has_models ) {
num_terminals = get_number_terminals( line );
if ( num_terminals != 0 ) {
model_name = get_model_name( line, num_terminals );
if ( isalpha( *model_name ) ) {
found_model = FALSE;
for ( i = 0; i < *num_used_model_names; i++ )
if ( strcmp( used_model_names[i], model_name ) == 0 ) found_model = TRUE;
if ( !found_model ) {
used_model_names[*num_used_model_names] = model_name;
*num_used_model_names = *num_used_model_names + 1;
}
else tfree( model_name );
}
else {
tfree( model_name );
}
}
}
}
}
// now make recursive call on instances just found above
for ( i = 0; i < tmp_cnt; i++ )
get_subckts_for_subckt( start_card, new_names[i], used_subckt_names, num_used_subckt_names,
used_model_names, num_used_model_names, has_models );
}
/*
check if current token matches model bin name -- <token>.[0-9]+
*/
static bool
model_bin_match( char* token, char* model_name )
{
char* dot_char;
bool flag = FALSE;
if ( strncmp( model_name, token, strlen(token) ) == 0 ) {
if ( (dot_char = strstr( model_name, "." )) ) {
flag = TRUE;
dot_char++;
while( *dot_char != '\0' ) {
if ( !isdigit( *dot_char ) ) {
flag = FALSE;
break;
}
dot_char++;
}
}
}
return flag;
}
/*
iterate through the deck and comment out unused subckts, models
(don't want to waste time processing everything)
also comment out .param lines with no parameters defined
*/
static void
comment_out_unused_subckt_models( struct line *start_card )
{
struct line *card;
char *used_subckt_names[1000], *used_model_names[1000], *line = NULL, *subckt_name, *model_name;
int num_used_subckt_names = 0, num_used_model_names = 0, i = 0, num_terminals = 0, tmp_cnt = 0;
bool processing_subckt = FALSE, found_subckt = FALSE, remove_subckt = FALSE, found_model = FALSE, has_models = FALSE;
for ( card = start_card; card != NULL; card = card->li_next ) {
if ( ciprefix( ".model", card->li_line ) ) has_models = TRUE;
if ( ciprefix( ".cmodel", card->li_line ) ) has_models = TRUE;
if ( ciprefix( ".param", card->li_line ) && !strstr( card->li_line, "=" ) ) *card->li_line = '*';
}
for ( card = start_card; card != NULL; card = card->li_next ) {
line = card->li_line;
if ( *line == '*' ) continue;
if ( ciprefix( ".subckt", line ) || ciprefix( ".macro", line ) ) processing_subckt = TRUE;
if ( ciprefix( ".ends", line ) || ciprefix( ".eom", line ) ) processing_subckt = FALSE;
if ( !processing_subckt ) {
if ( *line == 'x' ) {
subckt_name = get_instance_subckt( line );
found_subckt = FALSE;
for ( i = 0; i < num_used_subckt_names; i++ )
if ( strcmp( used_subckt_names[i], subckt_name ) == 0 ) found_subckt = TRUE;
if ( !found_subckt ) {
used_subckt_names[num_used_subckt_names++] = subckt_name;
tmp_cnt++;
}
else tfree( subckt_name );
}
else if ( *line == 'a' ) {
model_name = get_adevice_model_name( line );
found_model = FALSE;
for ( i = 0; i < num_used_model_names; i++ )
if ( strcmp( used_model_names[i], model_name ) == 0 ) found_model = TRUE;
if ( !found_model ) used_model_names[num_used_model_names++] = model_name;
else tfree( model_name );
}
else if ( has_models ) {
/* This is a preliminary version, until we have found a reliable
method to detect the model name out of the input line (Many
options have to be taken into account.). */
num_terminals = get_number_terminals( line );
if ( num_terminals != 0 ) {
bool model_ok = FALSE;
char *tmp_name, *tmp_name1;
tmp_name = tmp_name1 = model_name = get_model_name( line, num_terminals );
/* first character of model name is character from alphabet */
if ( isalpha( *model_name ) ) model_ok = TRUE;
/* first character is digit, second is alpha, third is digit,
e.g. 1N4002 */
else if ((strlen(model_name) > 2) && isdigit(*tmp_name) && isalpha(*(++tmp_name)) &&
isdigit(*(++tmp_name))) model_ok = TRUE;
/* first character is is digit, second is alpha, third is alpha, fourth is digit
e.g. 2SK456 */
else if ((strlen(model_name) > 3) && isdigit(*tmp_name1) && isalpha(*(++tmp_name1)) &&
isalpha(*(++tmp_name1)) && isdigit(*(++tmp_name1))) model_ok = TRUE;
/* Check if model has already been recognized, if not, add its name to
list used_model_names[i] */
if (model_ok) {
found_model = FALSE;
for ( i = 0; i < num_used_model_names; i++ )
if ( strcmp( used_model_names[i], model_name ) == 0 ) found_model = TRUE;
if ( !found_model ) used_model_names[num_used_model_names++] = model_name;
else tfree( model_name );
} else {
tfree( model_name );
}
}
} /* if ( has_models ) */
} /* if ( !processing_subckt ) */
} /* for loop through all cards */
for ( i = 0; i < tmp_cnt; i++ )
get_subckts_for_subckt( start_card, used_subckt_names[i], used_subckt_names,
&num_used_subckt_names, used_model_names, &num_used_model_names, has_models );
/* comment out any unused subckts */
for ( card = start_card; card != NULL; card = card->li_next ) {
line = card->li_line;
if ( *line == '*' ) continue;
if ( ciprefix( ".subckt", line ) || ciprefix( ".macro", line ) ) {
subckt_name = get_subckt_model_name( line );
remove_subckt = TRUE;
for ( i = 0; i < num_used_subckt_names; i++ )
if ( strcmp( used_subckt_names[i], subckt_name ) == 0 ) remove_subckt = FALSE;
tfree(subckt_name);
}
if ( ciprefix( ".ends", line ) || ciprefix( ".eom", line ) ) {
if ( remove_subckt ) *line = '*';
remove_subckt = FALSE;
}
if ( remove_subckt ) *line = '*';
else if ( has_models && (ciprefix( ".model", line ) || ciprefix( ".cmodel", line )) ) {
model_name = get_subckt_model_name( line );
found_model = FALSE;
for ( i = 0; i < num_used_model_names; i++ )
if ( strcmp( used_model_names[i], model_name ) == 0 || model_bin_match( used_model_names[i], model_name ) ) found_model = TRUE;
if ( !found_model ) *line = '*';
tfree(model_name);
}
}
for ( i = 0; i < num_used_subckt_names; i++ ) tfree(used_subckt_names[i]);
for ( i = 0; i < num_used_model_names; i++ ) tfree(used_model_names[i]);
}
static char*
inp_fix_ternary_operator_str( char *line )
{
char *conditional, *if_str, *else_str, *question, *colon, keep, *str_ptr, *str_ptr2, *new_str;
char *paren_ptr = NULL, *end_str = NULL, *beg_str = NULL;
int count = 0;
if ( !strstr( line, "?" ) && !strstr( line, ":" ) ) return line;
str_ptr = line;
if ( ciprefix( ".param", line ) || ciprefix( ".func", line ) || ciprefix( ".meas", line ) ) {
str_ptr = line;
if ( ciprefix( ".param", line ) || ciprefix( ".meas", line ) ) str_ptr = strstr( line, "=" );
else str_ptr = strstr( line, ")" );
str_ptr++;
while( isspace(*str_ptr) ) str_ptr++;
if ( *str_ptr == '{' ) { str_ptr++; while( isspace(*str_ptr) ) str_ptr++; }
question = strstr( str_ptr, "?" );
paren_ptr = strstr( str_ptr, "(" );
if ( paren_ptr != NULL && paren_ptr < question ) {
str_ptr = question;
while ( *str_ptr != '(' ) str_ptr--;
*str_ptr = '\0';
beg_str = strdup(line);
*str_ptr = '(';
str_ptr++;
paren_ptr = NULL;
}
else {
keep = *str_ptr; *str_ptr = '\0';
beg_str = strdup(line);
*str_ptr = keep;
}
}
// get conditional
str_ptr2 = question = strstr( str_ptr, "?" );
str_ptr2--;
while ( isspace(*str_ptr2) ) str_ptr2--;
if ( *str_ptr2 == ')' ) {
while ( *str_ptr != '(' ) str_ptr--;
}
str_ptr2++; keep = *str_ptr2; *str_ptr2 = '\0';
conditional = strdup(str_ptr);
*str_ptr2 = keep;
// get if
str_ptr = question + 1;
while ( isspace(*str_ptr) ) str_ptr++;
if ( *str_ptr == '(' ) {
// find closing paren
count = 1;
str_ptr2 = str_ptr + 1;
while ( count != 0 && *str_ptr2 != '\0' ) {
str_ptr2++;
if ( *str_ptr2 == '(' ) count++;
if ( *str_ptr2 == ')' ) count--;
}
if ( count != 0 ) {
fprintf(stderr, "ERROR: problem parsing 'if' of ternary string %s!\n", line);
exit (-1);
}
colon = str_ptr2 + 1;
while ( *colon != ':' && *colon != '\0' ) colon++;
if ( *colon != ':' ) {
fprintf(stderr,"ERROR: problem parsing ternary string (finding ':') %s!\n", line);
exit(-1);
}
}
else if ( ( colon = strstr( str_ptr, ":" ) ) ) {
str_ptr2 = colon - 1;
while ( isspace(*str_ptr2) ) str_ptr2--;
}
else {
fprintf(stderr,"ERROR: problem parsing ternary string (missing ':') %s!\n", line);
exit(-1);
}
str_ptr2++; keep = *str_ptr2; *str_ptr2 = '\0';
if_str = inp_fix_ternary_operator_str(strdup(str_ptr));
*str_ptr2 = keep;
// get else
str_ptr = colon + 1;
while ( isspace(*str_ptr) ) str_ptr++;
if ( paren_ptr != NULL ) {
// find end paren ')'
bool found_paren = FALSE;
count = 0; str_ptr2 = str_ptr;
while ( *str_ptr2 != '\0' ) {
if ( *str_ptr2 == '(' ) { count++; found_paren = TRUE; }
if ( *str_ptr2 == ')' ) count--;
str_ptr2++;
if ( found_paren && count == 0 ) break;
}
if ( found_paren && count != 0 ) {
fprintf( stderr, "ERROR: problem parsing ternary line %s!\n", line );
exit(-1);
}
keep = *str_ptr2;
*str_ptr2 = '\0';
else_str = inp_fix_ternary_operator_str(strdup(str_ptr));
if ( keep != '}' ) {
end_str = inp_fix_ternary_operator_str(strdup(str_ptr2+1));
} else {
*str_ptr2 = keep;
end_str = strdup(str_ptr2);
}
*str_ptr2 = keep;
}
else {
if ( ( str_ptr2 = strstr( str_ptr, "}" ) ) ) {
*str_ptr2 = '\0';
else_str = inp_fix_ternary_operator_str(strdup(str_ptr));
*str_ptr2 = '}';
end_str = strdup(str_ptr2);
} else {
else_str = inp_fix_ternary_operator_str(strdup(str_ptr));
}
}
if ( end_str != NULL ) {
if ( beg_str != NULL ) {
new_str = tmalloc( strlen(beg_str) + strlen("ternary_fcn") + strlen(conditional) + strlen(if_str) + strlen(else_str) + strlen(end_str) + 5 );
sprintf( new_str, "%sternary_fcn(%s,%s,%s)%s", beg_str, conditional, if_str, else_str, end_str );
} else {
new_str = tmalloc( strlen("ternary_fcn") + strlen(conditional) + strlen(if_str) + strlen(else_str) + strlen(end_str) + 5 );
sprintf( new_str, "ternary_fcn(%s,%s,%s)%s", conditional, if_str, else_str, end_str );
}
}
else {
if ( beg_str != NULL ) {
new_str = tmalloc( strlen(beg_str) + strlen("ternary_fcn") + strlen(conditional) + strlen(if_str) + strlen(else_str) + 5 );
sprintf( new_str, "%sternary_fcn(%s,%s,%s)", beg_str, conditional, if_str, else_str );
} else {
new_str = tmalloc( strlen("ternary_fcn") + strlen(conditional) + strlen(if_str) + strlen(else_str) + 5 );
sprintf( new_str, "ternary_fcn(%s,%s,%s)", conditional, if_str, else_str );
}
}
tfree(line);
tfree(conditional); tfree(if_str); tfree(else_str);
if ( beg_str != NULL ) tfree(beg_str);
if ( end_str != NULL ) tfree(end_str);
return new_str;
}
static void
inp_fix_ternary_operator( struct line *start_card )
{
struct line *card;
char *line;
for ( card = start_card; card != NULL; card = card->li_next ) {
line = card->li_line;
if ( *line == '*' ) continue;
if ( strstr( line, "?" ) && strstr( line, ":" ) ) {
card->li_line = inp_fix_ternary_operator_str( line );
}
}
}
/*-------------------------------------------------------------------------
* Read the entire input file and return a pointer to the first line of
* the linked list of 'card' records in data. The pointer is stored in
* *data.
*-------------------------------------------------------------------------*/
void
inp_readall(FILE *fp, struct line **data, int call_depth, char *dir_name)
{
struct line *end = NULL, *cc = NULL, *prev = NULL, *working, *newcard, *start_lib, *global_card, *tmp_ptr = NULL, *tmp_ptr2 = NULL;
char *buffer = NULL, *s, *t, *y, *z, c;
/* segfault fix */
#ifdef XSPICE
char big_buff[5000];
int line_count = 0;
Ipc_Status_t ipc_status;
char ipc_buffer[1025]; /* Had better be big enough */
int ipc_len;
#endif
char *copys=NULL, big_buff2[5000];
char *global_copy = NULL, keep_char;
int line_number = 1; /* sjb - renamed to avoid confusion with struct line */
int line_number_orig = 1, line_number_lib = 1, line_number_inc = 1;
int no_braces = 0; /* number of '{' */
FILE *newfp;
#if defined(TRACE) || defined(OUTDECK)
FILE *fdo;
#endif
struct line *tmp_ptr1 = NULL;
int i, j;
bool found_library, found_lib_name, found_end = FALSE, shell_eol_continuation = FALSE;
bool dir_name_flag = FALSE;
struct variable *v;
char *s_ptr, *s_lower;
/* Must set this to NULL or non-tilde includes segfault. -- Tim Molteno */
/* copys = NULL; */ /* This caused a parse error with gcc 2.96. Why??? */
if ( call_depth == 0 ) {
num_subckt_w_params = 0;
num_libraries = 0;
num_functions = 0;
global = NULL;
found_end = FALSE;
}
/* gtri - modify - 12/12/90 - wbk - read from mailbox if ipc enabled */
#ifdef XSPICE
/* First read in all lines & put them in the struct cc */
while (1) {
/* If IPC is not enabled, do equivalent of what SPICE did before */
if(! g_ipc.enabled) {
if ( call_depth == 0 && line_count == 0 ) {
line_count++;
if ( fgets( big_buff, 5000, fp ) ) {
/* buffer = tmalloc( strlen(big_buff) + 1 );
strcpy(buffer, big_buff); */
buffer = copy(big_buff);
}
}
else {
buffer = readline(fp);
if(! buffer) {
break;
}
}
}
else {
/* else, get the line from the ipc channel. */
/* We assume that newlines are not sent by the client */
/* so we add them here */
ipc_status = ipc_get_line(ipc_buffer, &ipc_len, IPC_WAIT);
if(ipc_status == IPC_STATUS_END_OF_DECK) {
buffer = NULL;
break;
}
else if(ipc_status == IPC_STATUS_OK) {
buffer = (void *) tmalloc(strlen(ipc_buffer) + 3);
strcpy(buffer, ipc_buffer);
strcat(buffer, "\n");
}
else { /* No good way to report this so just die */
exit(1);
}
}
/* gtri - end - 12/12/90 */
#else
while ((buffer = readline(fp))) {
#endif
#ifdef TRACE
/* SDB debug statement */
printf ("in inp_readall, just read %s", buffer);
#endif
if ( !buffer ) {
continue;
}
/* OK -- now we have loaded the next line into 'buffer'. Process it. */
/* If input line is blank, ignore it & continue looping. */
if ( (strcmp(buffer,"\n") == 0) || (strcmp(buffer,"\r\n") == 0) ) {
if ( call_depth != 0 || (call_depth == 0 && cc != NULL) ) {
line_number_orig++;
tfree(buffer); /* was allocated by readline() */
continue;
}
}
if (*buffer == '@') {
tfree(buffer); /* was allocated by readline() */
break;
}
/* now handle .lib statements */
if (ciprefix(".lib", buffer)) {
for ( s = buffer; *s && !isspace(*s); s++ ); /* skip over .lib */
while ( isspace(*s) || isquote(*s) ) s++; /* advance past space chars */
if ( !*s ) { /* if at end of line, error */
fprintf(cp_err, "Error: .lib filename missing\n");
tfree(buffer); /* was allocated by readline() */
continue;
} /* Now s points to first char after .lib */
for ( t = s; *t && !isspace(*t) && !isquote(*t); t++ ); /* skip to end of word */
y = t;
while ( isspace(*y) || isquote(*y) ) y++; /* advance past space chars */
// check if rest of line commented out
if ( *y && *y != '$' ) { /* .lib <file name> <lib name> */
for ( z = y; *z && !isspace(*z) && !isquote(*z); z++ );
c = *t;
*t = '\0';
*z = '\0';
if ( *s == '~' ) {
copys = cp_tildexpand(s); /* allocates memory, but can also return NULL */
if( copys != NULL ) {
s = copys; /* reuse s, but remember, buffer still points to allocated memory */
}
}
/* lower case the file name for later string compares */
/* s_ptr = strdup(s); */
s_lower = strdup(s);
for(s_ptr = s_lower; *s_ptr && (*s_ptr != '\n'); s_ptr++) *s_ptr = tolower(*s_ptr);
found_library = FALSE;
for ( i = 0; i < num_libraries; i++ ) {
if ( strcmp( library_file[i], s_lower ) == 0 ) {
found_library = TRUE;
break;
}
}
if ( found_library ) {
if(copys) tfree(copys); /* allocated by the cp_tildexpand() above */
} else {
if ( dir_name != NULL ) sprintf( big_buff2, "%s/%s", dir_name, s );
else sprintf( big_buff2, "./%s", s );
dir_name_flag = FALSE;
if ( !( newfp = inp_pathopen( s, "r" ) ) ) {
dir_name_flag = TRUE;
if ( !( newfp = inp_pathopen( big_buff2, "r" ) ) ) {
perror(s);
if(copys) tfree(copys); /* allocated by the cp_tildexpand() above */
tfree(buffer); /* allocated by readline() above */
continue;
}
}
if(copys) tfree(copys); /* allocated by the cp_tildexpand() above */
library_file[num_libraries++] = strdup(s_lower);
if ( dir_name_flag == FALSE ) {
char *s_dup = strdup(s);
inp_readall(newfp, &libraries[num_libraries-1], call_depth+1, dirname(s_dup));
tfree(s_dup);
}
else
inp_readall(newfp, &libraries[num_libraries-1], call_depth+1, dir_name);
fclose(newfp);
}
*t = c;
tfree(s_lower);
/* Make the .lib a comment */
*buffer = '*';
}
} /* end of .lib handling */
/* now handle .include statements */
if (ciprefix(".include", buffer) || ciprefix(".inc", buffer)) {
for (s = buffer; *s && !isspace(*s); s++) /* advance past non-space chars */
;
while (isspace(*s) || isquote(*s)) /* now advance past space chars */
s++;
if (!*s) { /* if at end of line, error */
fprintf(cp_err, "Error: .include filename missing\n");
tfree(buffer); /* was allocated by readline() */
continue;
} /* Now s points to first char after .include */
for (t = s; *t && !isspace(*t) && !isquote(*t); t++) /* now advance past non-space chars */
;
*t = '\0'; /* place \0 and end of file name in buffer */
if (*s == '~') {
copys = cp_tildexpand(s); /* allocates memory, but can also return NULL */
if(copys != NULL) {
s = copys; /* reuse s, but remember, buffer still points to allocated memory */
}
}
/* open file specified by .include statement */
if ( dir_name != NULL ) sprintf( big_buff2, "%s/%s", dir_name, s );
else sprintf( big_buff2, "./%s", s );
dir_name_flag = FALSE;
if (!(newfp = inp_pathopen(s, "r"))) {
dir_name_flag = TRUE;
if ( !( newfp = inp_pathopen( big_buff2, "r" ) ) ) {
perror(s);
if(copys) {
tfree(copys); /* allocated by the cp_tildexpand() above */
}
tfree(buffer); /* allocated by readline() above */
continue;
}
}
if(copys) {
tfree(copys); /* allocated by the cp_tildexpand() above */
}
if ( dir_name_flag == FALSE ) {
char *s_dup = strdup(s);
inp_readall(newfp, &newcard, call_depth+1, dirname(s_dup)); /* read stuff in include file into netlist */
tfree(s_dup);
}
else
inp_readall(newfp, &newcard, call_depth+1, dir_name); /* read stuff in include file into netlist */
(void) fclose(newfp);
/* Make the .include a comment */
*buffer = '*';
/* now check if this is the first pass (i.e. end points to null) */
if (end) { /* end already exists */
end->li_next = alloc(struct line); /* create next card */
end = end->li_next; /* make end point to next card */
} else {
end = cc = alloc(struct line); /* create the deck & end. cc will
point to beginning of deck, end to
the end */
}
/* now fill out rest of struct end. */
end->li_next = NULL;
end->li_error = NULL;
end->li_actual = NULL;
end->li_line = copy(buffer);
end->li_linenum = end->li_linenum_orig = line_number++;
if (newcard) {
end->li_next = newcard;
/* Renumber the lines */
line_number_inc = 1;
for (end = newcard; end && end->li_next; end = end->li_next) {
end->li_linenum = line_number++;
end->li_linenum_orig = line_number_inc++;
}
end->li_linenum = line_number++; /* SJB - renumber the last line */
end->li_linenum_orig = line_number_inc++; /* SJB - renumber the last line */
}
/* Fix the buffer up a bit. */
(void) strncpy(buffer + 1, "end of:", 7);
} /* end of .include handling */
/* loop through 'buffer' until end is reached. Then test for
premature end. If premature end is reached, spew
error and zap the line. */
if ( !ciprefix( "write", buffer ) ) { // exclude 'write' command so filename case preserved
for (s = buffer; *s && (*s != '\n') && (*s != '\0'); s++) *s = tolower(*s);
if (!*s) {
//fprintf(cp_err, "Warning: premature EOF\n");
}
*s = '\0'; /* Zap the newline. */
if((s-1) >= buffer && *(s-1) == '\r') /* Zop the carriage return under windows */
*(s-1) = '\0';
}
if (ciprefix(".end", buffer) && strlen(buffer) == 4 ) {
found_end = TRUE;
*buffer = '*';
}
if ( ciprefix( ".global", buffer ) ) {
for ( s = buffer; *s && !isspace(*s); s++ );
if ( global == NULL ) {
global = strdup(buffer);
} else {
global_copy = tmalloc( strlen(global) + strlen(s) + 1 );
sprintf( global_copy, "%s%s", global, s );
tfree(global);
global = global_copy;
}
*buffer = '*';
}
if ( shell_eol_continuation ) {
char *new_buffer = tmalloc( strlen(buffer) + 2);
sprintf( new_buffer, "+%s", buffer );
tfree(buffer);
buffer = new_buffer;
}
shell_eol_continuation = chk_for_line_continuation( buffer );
/* now check if this is the first pass (i.e. end points to null) */
if (end) { /* end already exists */
end->li_next = alloc(struct line); /* create next card */
end = end->li_next; /* point to next card */
} else { /* End doesn't exist. Create it. */
end = cc = alloc(struct line); /* note that cc points to beginning
of deck, end to the end */
}
/* now put buffer into li */
end->li_next = NULL;
end->li_error = NULL;
end->li_actual = NULL;
end->li_line = copy(buffer);
end->li_linenum = line_number++;
end->li_linenum_orig = line_number_orig++;
tfree(buffer);
} /* end while ((buffer = readline(fp))) */
if (!end) { /* No stuff here */
*data = NULL;
return;
}
if ( call_depth == 0 && found_end == TRUE) {
if ( global == NULL ) {
global = tmalloc( strlen(".global gnd") + 1 );
sprintf( global, ".global gnd" );
}
global_card = alloc(struct line);
global_card->li_error = NULL;
global_card->li_actual = NULL;
global_card->li_line = global;
global_card->li_linenum = 1;
prev = cc->li_next;
cc->li_next = global_card;
global_card->li_next = prev;
inp_init_lib_data();
inp_determine_libraries(cc, NULL);
}
/*
add libraries
*/
found_lib_name = FALSE;
if ( call_depth == 0 ) {
for( i = 0; i < num_libraries; i++ ) {
working = libraries[i];
while ( working ) {
buffer = working->li_line;
if ( found_lib_name && ciprefix(".endl", buffer) ) {
/* Make the .endl a comment */
*buffer = '*';
found_lib_name = FALSE;
/* set pointer and continue to avoid deleting below */
tmp_ptr2 = working->li_next;
working->li_next = tmp_ptr;
working = tmp_ptr2;
/* end = working;
* working = working->li_next;
* end->li_next = NULL; */
continue;
} /* for ... */
if ( ciprefix(".lib", buffer) ) {
if ( found_lib_name == TRUE ) {
fprintf( stderr, "ERROR: .lib is missing .endl!\n" );
#ifdef HAS_WINDOWS
winmessage("Fatal error in SPICE");
#endif
exit(-1);
}
for ( s = buffer; *s && !isspace(*s); s++ ); /* skip over .lib */
while ( isspace(*s) || isquote(*s) ) s++; /* advance past space chars */
for ( t = s; *t && !isspace(*t) && !isquote(*t); t++ ); /* skip to end of word */
keep_char = *t;
*t = '\0';
/* see if library we want to copy */
found_lib_name = FALSE;
for( j = 0; j < num_lib_names[i]; j++ ) {
if ( strcmp( library_name[i][j], s ) == 0 ) {
found_lib_name = TRUE;
start_lib = working;
/* make the .lib a comment */
*buffer = '*';
tmp_ptr = library_ll_ptr[i][j]->li_next;
library_ll_ptr[i][j]->li_next = working;
/* renumber lines */
line_number_lib = 1;
for ( start_lib = working; !ciprefix(".endl", start_lib->li_line); start_lib = start_lib->li_next ) {
start_lib->li_linenum = line_number++;
start_lib->li_linenum_orig = line_number_lib++;
}
start_lib->li_linenum = line_number++; // renumber endl line
start_lib->li_linenum_orig = line_number_lib++;
break;
}
}
*t = keep_char;
}
prev = working;
working = working->li_next;
if ( found_lib_name == FALSE ) {
tfree(prev->li_line);
tfree(prev);
}
} /* end while */
} /* end for */
if ( found_end == TRUE ) {
end->li_next = alloc(struct line); /* create next card */
end = end->li_next; /* point to next card */
buffer = tmalloc( strlen( ".end" ) + 1 );
sprintf( buffer, ".end" );
/* now put buffer into li */
end->li_next = NULL;
end->li_error = NULL;
end->li_actual = NULL;
end->li_line = buffer;
end->li_linenum = end->li_linenum_orig = line_number++;
end->li_linenum_orig = line_number_orig++;
}
}
/* Now clean up li: remove comments & stitch together continuation lines. */
working = cc->li_next; /* cc points to head of deck. Start with the
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) {
for (s = working->li_line; (c = *s) && c <= ' '; s++)
;
#ifdef TRACE
/* SDB debug statement */
printf("In inp_readall, processing linked list element line = %d, s = %s . . . \n", working->li_linenum,s);
#endif
switch (c) {
case '#':
case '$':
case '*':
case '\0':
/* this used to be commented out. Why? */
/* prev = NULL; */
working = working->li_next; /* for these chars, go to next card */
break;
case '+': /* handle continuation */
if (!prev) {
working->li_error = copy(
"Illegal continuation line: ignored.");
working = working->li_next;
break;
}
/* create buffer and write last and current line into it. */
buffer = tmalloc(strlen(prev->li_line) + strlen(s) + 2);
(void) sprintf(buffer, "%s %s", prev->li_line, s + 1);
s = prev->li_line;
prev->li_line = buffer;
prev->li_next = working->li_next;
working->li_next = NULL;
if (prev->li_actual) {
for (end = prev->li_actual; end->li_next; end = end->li_next)
;
end->li_next = working;
tfree(s);
} else {
newcard = alloc(struct line);
newcard->li_linenum = prev->li_linenum;
newcard->li_line = s;
newcard->li_next = working;
newcard->li_error = NULL;
newcard->li_actual = NULL;
prev->li_actual = newcard;
}
working = prev->li_next;
break;
default: /* regular one-line card */
prev = working;
working = working->li_next;
break;
}
}
working = cc->li_next;
inp_fix_for_numparam(working);
inp_remove_excess_ws(working);
if ( call_depth == 0 ) {
comment_out_unused_subckt_models(working);
line_number = inp_split_multi_param_lines(working, line_number);
inp_fix_macro_param_func_paren_io(working);
inp_fix_ternary_operator(working);
inp_grab_func(working);
inp_expand_macros_in_func();
inp_expand_macros_in_deck(working);
inp_fix_param_values(working);
/* get end card as last card in list; end card pntr does not appear to always
be correct at this point */
for(newcard = working; newcard != NULL; newcard = newcard->li_next)
end = newcard;
inp_reorder_params(working, cc, end);
inp_fix_inst_calls_for_numparam(working);
inp_fix_gnd_name(working);
inp_chk_for_multi_in_vcvs(working, &line_number);
if (cp_getvar("addcontrol", VT_BOOL, (char *) &v))
inp_add_control_section(working, &line_number);
}
*data = cc;
/* get max. line length and number of lines in input deck,
and renumber the lines,
count the number of '{' per line as an upper estimate of the number
of parameter substitutions in a line*/
dynmaxline = 0;
dynLlen = 0;
for(tmp_ptr1 = cc; tmp_ptr1 != NULL; tmp_ptr1 = tmp_ptr1->li_next) {
char *s;
int braces_per_line = 0;
/* count number of lines */
dynmaxline++;
/* renumber the lines of the processed input deck */
tmp_ptr1->li_linenum = dynmaxline;
if (dynLlen < strlen(tmp_ptr1->li_line))
dynLlen = strlen(tmp_ptr1->li_line);
/* count '{' */
for (s = tmp_ptr1->li_line; *s; s++)
if (*s == '{') braces_per_line++;
if (no_braces < braces_per_line) no_braces = braces_per_line;
}
#if defined(TRACE) || defined(OUTDECK)
/*debug: print into file*/
fdo = fopen("debug-out.txt", "w");
for(tmp_ptr1 = cc; tmp_ptr1 != NULL; tmp_ptr1 = tmp_ptr1->li_next)
fprintf(fdo, "%d %d %s\n", tmp_ptr1->li_linenum_orig, tmp_ptr1->li_linenum, tmp_ptr1->li_line);
(void) fclose(fdo);
fprintf(stdout, "max line length %d, max subst. per line %d, number of lines %d\n",
dynLlen, no_braces, dynmaxline);
#endif
/* max line length increased by maximum number of parameter substitutions per line
times parameter string length (25) */
dynLlen += no_braces * 25;
/* several times a string of length dynLlen is used for messages, thus give it a
minimum length */
if (dynLlen < 512) dynLlen = 512;
return;
}
/*-------------------------------------------------------------------------*
* *
*-------------------------------------------------------------------------*/
void
inp_casefix(char *string)
{
#ifdef HAVE_CTYPE_H
if (string)
while (*string) {
/* Let's make this really idiot-proof. */
#ifdef HAS_ASCII
*string = strip(*string);
#endif
if (*string == '"') {
*string++ = ' ';
while (*string && *string != '"')
string++;
if (*string == '"')
*string = ' ';
}
if (!isspace(*string) && !isprint(*string))
*string = '_';
if (isupper(*string))
*string = tolower(*string);
string++;
}
return;
#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 double characters '$ ' or '//' 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 comments */
while((c=*d)!='\0') {
d++;
if (*d==';') {
break;
} else if ((c=='$') && (*d==' ')) {
d--; /* move d back to first comment character */
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 */
}
static void
inp_change_quotes( char *s )
{
bool first_quote = FALSE;
while ( *s ) {
if ( *s == '\'' ) {
if ( first_quote == FALSE ) {
*s = '{';
first_quote = TRUE;
} else {
*s = '}';
first_quote = FALSE;
}
}
s++;
}
}
static char*
inp_fix_subckt( char *s )
{
struct line *head=NULL, *newcard=NULL, *start_card=NULL, *end_card=NULL, *prev_card=NULL, *c=NULL;
char *equal = strstr( s, "=" );
char *beg, *buffer, *ptr1, *ptr2, *str, *new_str = NULL;
char keep;
int num_params = 0, i = 0;
if ( !strstr( s, "params:" ) && equal != NULL ) {
/* get subckt name (ptr1 will point to name) */
for ( ptr1 = s; *ptr1 && !isspace(*ptr1); ptr1++ );
while ( isspace(*ptr1) ) ptr1++;
for ( ptr2 = ptr1; *ptr2 && !isspace(*ptr2) && !isquote(*ptr2); ptr2++ );
keep = *ptr2;
*ptr2 = '\0';
subckt_w_params[num_subckt_w_params++] = strdup(ptr1);
*ptr2 = keep;
/* go to beginning of first parameter word */
/* s will contain only subckt definition */
/* beg will point to start of param list */
for ( beg = equal-1; *beg && isspace(*beg); beg-- );
for ( ; *beg && !isspace(*beg); beg-- );
*beg = '\0';
beg++;
head = alloc(struct line);
/* create list of parameters that need to get sorted */
while ( ( ptr1 = strstr( beg, "=" ) ) ) {
ptr2 = ptr1+1;
ptr1--;
while ( isspace(*ptr1) ) ptr1--;
while ( !isspace(*ptr1) && *ptr1 != '\0' ) ptr1--;
ptr1++; /* ptr1 points to beginning of parameter */
while ( isspace(*ptr2) ) ptr2++;
while ( *ptr2 && !isspace(*ptr2) ) ptr2++; /* ptr2 points to end of parameter */
keep = *ptr2;
*ptr2 = '\0';
beg = ptr2+1;
newcard = alloc(struct line);
str = strdup(ptr1);
newcard->li_line = str;
newcard->li_next = NULL;
if ( start_card == NULL ) head->li_next = start_card = newcard;
else prev_card->li_next = newcard;
prev_card = end_card = newcard;
num_params++;
}
/* now sort parameters in order of dependencies */
inp_sort_params( start_card, end_card, head, start_card, end_card );
/* create new ordered parameter string for subckt call */
c=head->li_next;
tfree(head);
for( i = 0; i < num_params && c!= NULL; i++ ) {
if ( new_str == NULL ) new_str = strdup(c->li_line);
else {
str = new_str;
new_str = tmalloc( strlen(str) + strlen(c->li_line) + 2 );
sprintf( new_str, "%s %s", str, c->li_line );
tfree(str);
}
tfree(c->li_line);
head = c;
c = c->li_next;
tfree(head);
}
/* create buffer and insert params: */
buffer = tmalloc( strlen(s) + 9 + strlen(new_str) + 1 );
sprintf( buffer, "%s params: %s", s, new_str );
tfree(s); tfree(new_str);
s = buffer;
}
return s;
}
static char*
inp_remove_ws( char *s )
{
char *big_buff;
int big_buff_index = 0;
char *buffer, *curr;
bool is_expression = FALSE;
big_buff = tmalloc( strlen(s) + 1 );
curr = s;
while ( *curr != '\0' ) {
if ( *curr == '{' ) is_expression = TRUE;
if ( *curr == '}' ) is_expression = FALSE;
big_buff[big_buff_index++] = *curr;
if ( *curr == '=' || (is_expression && (is_arith_char(*curr) || *curr == ',')) ) {
curr++;
while ( isspace(*curr) ) curr++;
if ( *curr == '{' ) is_expression = TRUE;
if ( *curr == '}' ) is_expression = FALSE;
big_buff[big_buff_index++] = *curr;
}
curr++;
if ( isspace(*curr) ) {
while ( isspace(*curr) ) curr++;
if ( is_expression ) {
if ( *curr != '=' && !is_arith_char(*curr) && *curr != ',' ) big_buff[big_buff_index++] = ' ';
} else {
if ( *curr != '=' ) big_buff[big_buff_index++] = ' ';
}
}
}
// big_buff[big_buff_index++] = *curr;
big_buff[big_buff_index] = '\0';
buffer = copy(big_buff);
tfree(s);
tfree(big_buff);
return buffer;
}
static void
inp_fix_for_numparam(struct line *deck)
{
bool found_control = FALSE;
struct line *c=deck;
while( c!=NULL) {
if ( ciprefix( ".modif", c->li_line ) ) *c->li_line = '*';
if ( ciprefix( "*lib", c->li_line ) ) {
c = c->li_next;
continue;
}
/* exclude plot line between .control and .endc from getting quotes changed */
if ( ciprefix( ".control", c->li_line ) ) found_control = TRUE;
if ( ciprefix( ".endc", c->li_line ) ) found_control = FALSE;
if ((found_control) && (ciprefix( "plot", c->li_line ))) {
c = c->li_next;
continue;
}
if ( !ciprefix( "*lib", c->li_line ) && !ciprefix( "*inc", c->li_line ) )
inp_change_quotes(c->li_line);
if ( ciprefix( ".subckt", c->li_line ) ) {
c->li_line = inp_fix_subckt(c->li_line);
}
c = c->li_next;
}
}
static void
inp_remove_excess_ws(struct line *deck )
{
struct line *c = deck;
while ( c != NULL ) {
if ( *c->li_line == '*' ) { c = c->li_next; continue; }
c->li_line = inp_remove_ws(c->li_line); /* freed in fcn */
c = c->li_next;
}
}
static void
inp_determine_libraries( struct line *deck, char *lib_name )
{
struct line *c = deck;
char *line, *s, *t, *y, *z, *copys, keep_char1, keep_char2;
int i, j;
bool found_lib_name = FALSE;
bool read_line = FALSE;
if ( lib_name == NULL ) read_line = TRUE;
while ( c != NULL ) {
line = c->li_line;
if ( ciprefix( ".endl", line ) && lib_name != NULL ) read_line = FALSE;
if ( ciprefix( "*lib", line ) || ciprefix( ".lib", line ) ) {
for ( s = line; *s && !isspace(*s); s++);
while ( isspace(*s) || isquote(*s) ) s++;
for ( t = s; *t && !isspace(*t) && !isquote(*t); t++ );
y = t;
while ( isspace(*y) || isquote(*y) ) y++;
/* .lib <lib name> */
if ( !*y ) {
keep_char1 = *t;
*t = '\0';
if ( lib_name != NULL && strcmp( lib_name, s ) == 0 ) read_line = TRUE;
*t = keep_char1;
}
/* .lib <file name> <lib name> */
else if ( read_line == TRUE ) {
for ( z = y; *z && !isspace(*z) && !isquote(*z); z++ );
keep_char1 = *t; keep_char2 = *z;
*t = '\0'; *z = '\0';
if ( *s == '~' ) {
copys = cp_tildexpand(s);
if ( copys != NULL ) {
s = copys;
}
}
for ( i = 0; i < num_libraries; i++ )
if ( strcmp( library_file[i], s ) == 0 ) {
found_lib_name = FALSE;
for ( j = 0; j < num_lib_names[i] && found_lib_name == FALSE; j++ )
if ( strcmp( library_name[i][j], y ) == 0 ) found_lib_name = TRUE;
if ( found_lib_name == FALSE ) {
library_ll_ptr[i][num_lib_names[i]] = c;
library_name[i][num_lib_names[i]++] = strdup(y);
/* see if other libraries referenced */
inp_determine_libraries( libraries[i], y );
}
}
*line = '*'; /* comment out .lib line */
*t = keep_char1; *z = keep_char2;
}
}
c = c->li_next;
}
}
static void
inp_init_lib_data()
{
int i;
for ( i = 0; i < num_libraries; i++ )
num_lib_names[i] = 0;
}
static char*
inp_get_subckt_name( char *s )
{
char *end_ptr = strstr( s, "=" );
char *subckt_name, *subckt_name_copy;
char keep;
if ( end_ptr != NULL ) {
end_ptr--;
while ( isspace(*end_ptr) ) end_ptr--;
for( ;*end_ptr && !isspace(*end_ptr); end_ptr--);
} else {
end_ptr = s + strlen(s);
}
subckt_name = end_ptr;
while ( isspace( *subckt_name ) ) subckt_name--;
for( ; !isspace(*subckt_name); subckt_name-- );
subckt_name++;
keep = *end_ptr;
*end_ptr = '\0';
subckt_name_copy = strdup( subckt_name );
*end_ptr = keep;
return subckt_name_copy;
}
static int
inp_get_params( char *line, char *param_names[], char *param_values[] )
{
char *equal_ptr = strstr( line, "=" );
char *end, *name, *value;
int num_params = 0;
char tmp_str[1000];
char keep;
bool is_expression = FALSE;
while ( ( equal_ptr = strstr( line, "=" ) ) ) {
// check for equality '=='
if ( *(equal_ptr+1) == '=' ) { line = equal_ptr+2; continue; }
// check for '!=', '<=', '>='
if ( *(equal_ptr-1) == '!' || *(equal_ptr-1) == '<' || *(equal_ptr-1) == '>' ) { line = equal_ptr+1; continue; }
is_expression = FALSE;
/* get parameter name */
name = equal_ptr - 1;
while ( *name && isspace(*name) ) name--;
end = name + 1;
while ( *name && !isspace(*name) ) name--;
name++;
keep = *end;
*end = '\0';
param_names[num_params++] = strdup(name);
*end = keep;
/* get parameter value */
value = equal_ptr + 1;
while ( *value && isspace(*value) ) value++;
if ( *value == '{' ) is_expression = TRUE;
end = value;
if ( is_expression ) {
while ( *end && *end != '}' ) end++;
} else {
while ( *end && !isspace(*end) ) end++;
}
if ( is_expression ) end++;
keep = *end;
*end = '\0';
if ( *value != '{' &&
!( isdigit( *value ) || ( *value == '.' && isdigit(*(value+1)) ) ) ) {
sprintf( tmp_str, "{%s}", value );
value = tmp_str;
}
param_values[num_params-1] = strdup(value);
*end = keep;
line = end;
}
return num_params;
}
static char*
inp_fix_inst_line( char *inst_line,
int num_subckt_params, char *subckt_param_names[], char *subckt_param_values[],
int num_inst_params, char *inst_param_names[], char *inst_param_values[] )
{
char *end = strstr( inst_line, "=" ), *inst_name, *inst_name_end = inst_line;
char *curr_line = inst_line, *new_line = NULL;
char keep;
int i, j;
while ( !isspace(*inst_name_end) ) inst_name_end++;
keep = *inst_name_end;
*inst_name_end = '\0';
inst_name = strdup( inst_line );
*inst_name_end = keep;
if ( end != NULL ) {
end--;
while ( isspace( *end ) ) end--;
while ( !isspace( *end ) ) end--;
*end = '\0';
}
for ( i = 0; i < num_subckt_params; i++ ) {
for ( j = 0; j < num_inst_params; j++ ) {
if ( strcmp( subckt_param_names[i], inst_param_names[j] ) == 0 ) {
tfree( subckt_param_values[i] );
subckt_param_values[i] = strdup( inst_param_values[j] );
}
}
}
for ( i = 0; i < num_subckt_params; i++ ) {
new_line = tmalloc( strlen( curr_line ) + strlen( subckt_param_values[i] ) + 2 );
sprintf( new_line, "%s %s", curr_line, subckt_param_values[i] );
tfree( curr_line );
tfree( subckt_param_names[i] );
tfree( subckt_param_values[i] );
curr_line = new_line;
}
for ( i = 0; i < num_inst_params; i++ ) {
tfree( inst_param_names[i] );
tfree( inst_param_values[i] );
}
tfree( inst_name );
return curr_line;
}
static bool
found_mult_param( int num_params, char *param_names[] )
{
bool found_mult = FALSE;
int i;
for ( i = 0; i < num_params; i++ ) {
if ( strcmp( param_names[i], "m" ) == 0 ) {
found_mult = TRUE;
}
}
return found_mult;
}
static int
inp_fix_subckt_multiplier( struct line *subckt_card,
int num_subckt_params, char *subckt_param_names[], char *subckt_param_values[] )
{
struct line *card;
char *new_str;
subckt_param_names[num_subckt_params] = strdup("m");
subckt_param_values[num_subckt_params] = strdup("1");
num_subckt_params = num_subckt_params + 1;
if ( !strstr( subckt_card->li_line, "params:" ) ) {
new_str = tmalloc( strlen( subckt_card->li_line ) + 13 );
sprintf( new_str, "%s params: m=1", subckt_card->li_line );
subckt_w_params[num_subckt_w_params++] = get_subckt_model_name( subckt_card->li_line );
} else {
new_str = tmalloc( strlen( subckt_card->li_line ) + 5 );
sprintf( new_str, "%s m=1", subckt_card->li_line );
}
tfree( subckt_card->li_line );
subckt_card->li_line = new_str;
for ( card = subckt_card->li_next;
card != NULL && !ciprefix( ".ends", card->li_line );
card = card->li_next ) {
new_str = tmalloc( strlen( card->li_line ) + 7 );
sprintf( new_str, "%s m={m}", card->li_line );
tfree( card->li_line );
card->li_line = new_str;
}
return num_subckt_params;
}
static void
inp_fix_inst_calls_for_numparam(struct line *deck)
{
struct line *c = deck;
struct line *d, *p = NULL;
char *inst_line;
char *subckt_line;
char *subckt_name;
char *subckt_param_names[1000];
char *subckt_param_values[1000];
char *inst_param_names[1000];
char *inst_param_values[1000];
char name_w_space[1000];
int num_subckt_params = 0;
int num_inst_params = 0;
int i,j,k;
bool flag = FALSE;
bool found_subckt = FALSE;
bool found_param_match = FALSE;
// first iterate through instances and find occurences where 'm' multiplier needs to be
// added to the subcircuit -- subsequent instances will then need this parameter as well
for ( c = deck; c != NULL; c = c->li_next ) {
inst_line = c->li_line;
if ( *inst_line == '*' ) { continue; }
if ( ciprefix( "x", inst_line ) ) {
num_inst_params = inp_get_params( inst_line, inst_param_names, inst_param_values );
subckt_name = inp_get_subckt_name( inst_line );
if ( found_mult_param( num_inst_params, inst_param_names ) ) {
flag = FALSE;
// iterate through the deck to find the subckt (last one defined wins)
d = deck;
while ( d != NULL ) {
subckt_line = d->li_line;
if ( ciprefix( ".subckt", subckt_line ) ) {
for ( ; *subckt_line && !isspace(*subckt_line); subckt_line++ );
while ( isspace(*subckt_line) ) subckt_line++;
sprintf( name_w_space, "%s ", subckt_name );
if ( strncmp( subckt_line, name_w_space, strlen(name_w_space) ) == 0 ) {
p = d;
flag = TRUE;
}
}
d = d->li_next;
}
if ( flag ) {
num_subckt_params = inp_get_params( p->li_line, subckt_param_names, subckt_param_values );
if ( num_subckt_params == 0 || !found_mult_param( num_subckt_params, subckt_param_names ) ) {
inp_fix_subckt_multiplier( p, num_subckt_params, subckt_param_names, subckt_param_values );
}
}
}
tfree(subckt_name );
if ( flag )
for (i=0; i < num_subckt_params; i++) {
tfree(subckt_param_names[i]);
tfree(subckt_param_values[i]);
}
for (i=0; i < num_inst_params; i++) {
tfree(inst_param_names[i]);
tfree(inst_param_values[i]);
}
}
}
c = deck;
while ( c != NULL ) {
inst_line = c->li_line;
if ( *inst_line == '*' ) { c = c->li_next; continue; }
if ( ciprefix( "x", inst_line ) ) {
subckt_name = inp_get_subckt_name( inst_line );
for ( i = 0; i < num_subckt_w_params; i++ ) {
if ( strcmp( subckt_w_params[i], subckt_name ) == 0 ) {
sprintf( name_w_space, "%s ", subckt_name );
/* find .subckt line */
found_subckt = FALSE;
d = deck;
while ( d != NULL ) {
subckt_line = d->li_line;
if ( ciprefix( ".subckt", subckt_line ) ) {
for ( ; *subckt_line && !isspace(*subckt_line); subckt_line++ );
while ( isspace(*subckt_line) ) subckt_line++;
if ( strncmp( subckt_line, name_w_space, strlen(name_w_space) ) == 0 ) {
num_subckt_params = inp_get_params( subckt_line, subckt_param_names, subckt_param_values );
num_inst_params = inp_get_params( inst_line, inst_param_names, inst_param_values );
// make sure that if have inst params that one matches subckt
found_param_match = FALSE;
if ( num_inst_params == 0 ) found_param_match = TRUE;
else {
for ( j = 0; j < num_inst_params; j++ ) {
for ( k = 0; k < num_subckt_params; k++ ) {
if ( strcmp( subckt_param_names[k], inst_param_names[j] ) == 0 ) {
found_param_match = TRUE;
break;
}
}
if ( found_param_match ) break;
}
}
if ( !found_param_match ) {
// comment out .subckt and continue
while ( d != NULL && !ciprefix( ".ends", d->li_line ) ) { *(d->li_line) = '*'; d = d->li_next; }
*(d->li_line) = '*'; d = d->li_next; continue;
}
c->li_line = inp_fix_inst_line( inst_line, num_subckt_params, subckt_param_names, subckt_param_values, num_inst_params, inst_param_names, inst_param_values );
found_subckt = TRUE;
for (i=0; i < num_subckt_params; i++) {
tfree(subckt_param_names[i]);
tfree(subckt_param_values[i]);
}
for (i=0; i < num_inst_params; i++) {
tfree(inst_param_names[i]);
tfree(inst_param_values[i]);
}
}
}
if ( found_subckt ) break;
d = d->li_next;
}
break;
}
}
tfree(subckt_name);
}
c = c->li_next;
}
}
static void
inp_get_func_from_line( char *line )
{
char *ptr, *end;
char keep;
char temp_buf[5000];
int num_params = 0;
int str_len = 0;
int i = 0;
/* get function name */
while ( !isspace( *line ) ) line++;
while ( isspace( *line ) ) line++;
end = line;
while ( !isspace( *end ) && *end != '(' ) end++;
keep = *end;
*end = '\0';
/* see if already encountered this function */
for ( i = 0; i < num_functions; i++ )
if ( strcmp( func_names[i], line ) == 0 ) break;
func_names[num_functions++] = strdup(line);
*end = keep;
num_params = 0;
/* get function parameters */
while ( *end != '(' ) end++;
while ( *end != ')' ) {
end++;
ptr = end;
while ( isspace( *ptr ) ) ptr++;
end = ptr;
while ( !isspace( *end ) && *end != ',' && *end != ')' ) end++;
keep = *end;
*end = '\0';
func_params[num_functions-1][num_params++] = strdup(ptr);
*end = keep;
}
num_parameters[num_functions-1] = num_params;
/* get function macro */
str_len = 0;
while ( *end != '{' ) end++;
end++;
while ( *end != '}' ) {
while ( isspace( *end ) ) end++;
if ( *end != '}' ) temp_buf[str_len++] = *end;
end++;
}
temp_buf[str_len++] = '\0';
func_macro[num_functions-1] = strdup(temp_buf);
}
//
// only grab global functions; skip subckt functions
//
static void
inp_grab_func( struct line *deck )
{
struct line *c = deck;
bool is_subckt = FALSE;
while ( c != NULL ) {
if ( *c->li_line == '*' ) { c = c->li_next; continue; }
if ( ciprefix( ".subckt", c->li_line ) ) is_subckt = TRUE;
if ( ciprefix( ".ends", c->li_line ) ) is_subckt = FALSE;
if ( !is_subckt && ciprefix( ".func", c->li_line ) ) {
inp_get_func_from_line( c->li_line );
*c->li_line = '*';
}
c = c->li_next;
}
}
static void
inp_grab_subckt_func( struct line *subckt )
{
struct line *c = subckt;
while ( !ciprefix( ".ends", c->li_line ) ) {
if ( ciprefix( ".func", c->li_line ) ) {
inp_get_func_from_line( c->li_line );
*c->li_line = '*';
}
c = c->li_next;
}
}
static char*
inp_do_macro_param_replace( int fcn_number, char *params[] )
{
char *param_ptr, *curr_ptr, *new_str, *curr_str = NULL, *search_ptr;
char keep, before, after;
int i;
for ( i = 0; i < num_parameters[fcn_number]; i++ ) {
if ( curr_str == NULL )
search_ptr = curr_ptr = func_macro[fcn_number];
else {
search_ptr = curr_ptr = curr_str;
curr_str = NULL;
}
while ( ( param_ptr = strstr( search_ptr, func_params[fcn_number][i] ) ) ) {
/* make sure actually have the parameter name */
before = *(param_ptr-1);
after = *(param_ptr+strlen(func_params[fcn_number][i]));
if ( !(is_arith_char(before) || isspace(before) || before == ',' || before == '=' || (param_ptr-1) < curr_ptr ) ||
!(is_arith_char(after) || isspace(after) || after == ',' || after == '=' || after == '\0' ) ) {
search_ptr = param_ptr + 1;
continue;
}
keep = *param_ptr;
*param_ptr = '\0';
if ( curr_str != NULL ) {
if ( str_has_arith_char( params[i] ) ) {
new_str = tmalloc( strlen(curr_str) + strlen(curr_ptr) + strlen(params[i]) + 3 );
sprintf( new_str, "%s%s(%s)", curr_str, curr_ptr, params[i] );
} else {
new_str = tmalloc( strlen(curr_str) + strlen(curr_ptr) + strlen(params[i]) + 1 );
sprintf( new_str, "%s%s%s", curr_str, curr_ptr, params[i] );
}
tfree( curr_str );
} else {
if ( str_has_arith_char( params[i] ) ) {
new_str = tmalloc( strlen(curr_ptr) + strlen(params[i]) + 3 );
sprintf( new_str, "%s(%s)", curr_ptr, params[i] );
} else {
new_str = tmalloc( strlen(curr_ptr) + strlen(params[i]) + 1 );
sprintf( new_str, "%s%s", curr_ptr, params[i] );
}
}
curr_str = new_str;
*param_ptr = keep;
search_ptr = curr_ptr = param_ptr + strlen(func_params[fcn_number][i]);
}
if ( param_ptr == NULL ) {
if ( curr_str == NULL ) {
curr_str = curr_ptr;
} else {
new_str = tmalloc( strlen(curr_str) + strlen(curr_ptr) + 1 );
sprintf( new_str, "%s%s", curr_str, curr_ptr );
tfree(curr_str);
curr_str = new_str;
}
}
}
return curr_str;
}
static char*
inp_expand_macro_in_str( char *str )
{
int i;
char *c;
char *open_paren_ptr, *close_paren_ptr, *fcn_name, *comma_ptr, *params[1000];
char *curr_ptr, *new_str, *macro_str, *curr_str = NULL;
int num_parens, num_params;
char *orig_ptr = str, *search_ptr = str, *orig_str = strdup(str);
char keep;
while ( ( open_paren_ptr = strstr( search_ptr, "(" ) ) ) {
fcn_name = open_paren_ptr - 1;
while ( fcn_name != search_ptr && (isalnum(*fcn_name) || *fcn_name == '_') ) fcn_name--;
if ( fcn_name != search_ptr ) fcn_name++;
*open_paren_ptr = '\0';
close_paren_ptr = NULL;
if ( open_paren_ptr != fcn_name ) {
for ( i = 0; i < num_functions; i++ ) {
if ( strcmp( func_names[i], fcn_name ) == 0 ) {
/* find the closing paren */
close_paren_ptr = NULL;
num_parens = 0;
for ( c = open_paren_ptr + 1; *c && *c != '\0'; c++ ) {
if ( *c == '(' ) num_parens++;
if ( *c == ')' ) {
if ( num_parens != 0 ) num_parens--;
else {
close_paren_ptr = c;
break;
}
}
}
if ( close_paren_ptr == NULL ) {
fprintf( stderr, "ERROR: did not find closing parenthesis for function call in str: %s\n", orig_str );
#ifdef HAS_WINDOWS
winmessage("Fatal error in SPICE");
#endif
exit( -1 );
}
*close_paren_ptr = '\0';
/* get the parameters */
curr_ptr = open_paren_ptr+1;
while ( isspace(*curr_ptr) ) curr_ptr++;
num_params = 0;
if ( ciprefix( "v(", curr_ptr ) ) {
// look for any commas and change to ' '
char *str_ptr = curr_ptr;
while ( *str_ptr != '\0' && *str_ptr != ')' ) { if ( *str_ptr == ',' || *str_ptr == '(' ) *str_ptr = ' '; str_ptr++; }
if ( *str_ptr == ')' ) *str_ptr = ' ';
}
while ( ( comma_ptr = strstr( curr_ptr, "," ) ) ) {
while ( isspace(*curr_ptr) ) curr_ptr++;
*comma_ptr = '\0';
params[num_params++] = inp_expand_macro_in_str( strdup( curr_ptr ) );
*comma_ptr = ',';
curr_ptr = comma_ptr+1;
}
while ( isspace(*curr_ptr) ) curr_ptr++;
/* get the last parameter */
params[num_params++] = inp_expand_macro_in_str( strdup( curr_ptr ) );
if ( num_parameters[i] != num_params ) {
fprintf( stderr, "ERROR: parameter mismatch for function call in str: %s\n", orig_ptr );
#ifdef HAS_WINDOWS
winmessage("Fatal error in SPICE");
#endif
exit( -1 );
}
macro_str = inp_do_macro_param_replace( i, params );
keep = *fcn_name;
*fcn_name = '\0';
if ( curr_str == NULL ) {
new_str = tmalloc( strlen(str) + strlen(macro_str) + strlen(close_paren_ptr+1) + 3 );
sprintf( new_str, "%s(%s)", str, macro_str );
curr_str = new_str;
} else {
new_str = tmalloc( strlen(curr_str) + strlen(str) + strlen(macro_str) + strlen(close_paren_ptr+1) + 3 );
sprintf( new_str, "%s%s(%s)", curr_str, str, macro_str );
tfree(curr_str);
curr_str = new_str;
}
*fcn_name = keep;
*close_paren_ptr = ')';
search_ptr = str = close_paren_ptr+1;
break;
} /* if strcmp */
} /* for loop over function names */
}
*open_paren_ptr = '(';
search_ptr = open_paren_ptr + 1;
}
if ( curr_str == NULL ) {
curr_str = orig_ptr;
}
else {
if ( str != NULL ) {
new_str = tmalloc( strlen(curr_str) + strlen(str) + 1 );
sprintf( new_str, "%s%s", curr_str, str );
tfree(curr_str);
curr_str = new_str;
}
tfree(orig_ptr);
}
tfree(orig_str);
return curr_str;
}
static void
inp_expand_macros_in_func()
{
int i;
for ( i = 0; i < num_functions; i++ ) {
func_macro[i] = inp_expand_macro_in_str( func_macro[i] );
}
}
static void
inp_expand_macros_in_deck( struct line *deck )
{
struct line *c = deck;
int prev_num_functions = 0, i, j;
while ( c != NULL ) {
if ( *c->li_line == '*' ) { c = c->li_next; continue; }
if ( ciprefix( ".subckt", c->li_line ) ) {
prev_num_functions = num_functions;
inp_grab_subckt_func( c );
if ( prev_num_functions != num_functions ) inp_expand_macros_in_func();
}
if ( ciprefix( ".ends", c->li_line ) ) {
if ( prev_num_functions != num_functions ) {
for ( i = prev_num_functions; i < num_functions; i++ ) {
tfree( func_names[i] );
tfree( func_macro[i] );
for ( j = 0; j < num_parameters[i]; j++ ) tfree( func_params[i][j] );
num_functions = prev_num_functions;
}
}
}
if ( *c->li_line != '*' ) c->li_line = inp_expand_macro_in_str( c->li_line );
c = c->li_next;
}
}
static void
inp_fix_param_values( struct line *deck )
{
struct line *c = deck;
char *line, *beg_of_str, *end_of_str, *old_str, *equal_ptr, *new_str;
char *vec_str, *natok, *buffer, *newvec, *whereisgt;
bool control_section = FALSE, has_paren = FALSE;
int n = 0;
wordlist *wl, *nwl;
while ( c != NULL ) {
line = c->li_line;
if ( *line == '*' || (ciprefix( ".param", line ) && strstr( line, "{" )) ) { c = c->li_next; continue; }
if ( ciprefix( ".control", line ) ) { control_section = TRUE; c = c->li_next; continue; }
if ( ciprefix( ".endc", line ) ) { control_section = FALSE; c = c->li_next; continue; }
if ( control_section || ciprefix( ".option", line ) ) { c = c->li_next; continue; } /* no handling of params in "option" lines */
if ( ciprefix( "set", line ) ) { c = c->li_next; continue; } /* no handling of params in "set" lines */
if ( *line == 'b' ) { c = c->li_next; continue; } /* no handling of params in B source lines */
/* for xspice .cmodel: replace .cmodel with .model and skip entire line) */
if ( ciprefix( ".cmodel", line ) ) {
*(++line) = 'm';
*(++line) = 'o';
*(++line) = 'd';
*(++line) = 'e';
*(++line) = 'l';
*(++line) = ' ';
c = c->li_next; continue; }
/* exclude CIDER models */
if ( ciprefix( ".model", line ) && ( strstr(line, "numos") || strstr(line, "numd") || strstr(line, "nbjt") ||
strstr(line, "nbjt2") || strstr(line, "numd2") ) )
{ c = c->li_next; continue; }
/* exclude CIDER devices with ic.file parameter */
if ( strstr(line, "ic.file")) { c = c->li_next; continue; }
while ( ( equal_ptr = strstr( line, "=" ) ) ) {
// skip over equality '=='
if ( *(equal_ptr+1) == '=' ) { line += 2; continue; }
// check for '!=', '<=', '>='
if ( *(equal_ptr-1) == '!' || *(equal_ptr-1) == '<' || *(equal_ptr-1) == '>' ) { line += 1; continue; }
beg_of_str = equal_ptr + 1;
while ( isspace(*beg_of_str) ) beg_of_str++;
/* all cases where no {} have to be put around selected token */
if ( isdigit(*beg_of_str) || *beg_of_str == '{' || *beg_of_str == '.' ||
*beg_of_str == '"' || ( *beg_of_str == '-' && isdigit(*(beg_of_str+1)) ) ||
ciprefix("true", beg_of_str) || ciprefix("false", beg_of_str) ) {
line = equal_ptr + 1;
} else if (*beg_of_str == '[') {
/* A vector following the '=' token: code to put curly brackets around all params
inside a pair of square brackets */
end_of_str = beg_of_str;
n = 0;
while (*end_of_str != ']') {
end_of_str++;
n++;
}
vec_str = tmalloc(n); /* string xx yyy from vector [xx yyy] */
*vec_str = '\0';
strncat(vec_str, beg_of_str + 1, n - 1);
/* work on vector elements inside [] */
nwl = NULL;
for (;;) {
natok = gettok(&vec_str);
if (!natok) break;
wl = alloc(struct wordlist);
buffer = tmalloc(strlen(natok) + 4);
if ( isdigit(*natok) || *natok == '{' || *natok == '.' ||
*natok == '"' || ( *natok == '-' && isdigit(*(natok+1)) ) ||
ciprefix("true", natok) || ciprefix("false", natok) ||
eq(natok, "<") || eq(natok, ">")) {
(void) sprintf(buffer, "%s", natok);
/* A complex value found inside a vector [< x1 y1> <x2 y2>] */
/* < xx and yy > have been dealt with before */
/* <xx */
} else if (*natok == '<') {
if (isdigit(*(natok + 1)) || ( *(natok + 1) == '-' &&
isdigit(*(natok + 2)) )) {
(void) sprintf(buffer, "%s", natok);
} else {
*natok = '{';
(void) sprintf(buffer, "<%s}", natok);
}
/* yy> */
} else if (strchr(natok, '>')) {
if (isdigit(*natok) || ( *natok == '-' && isdigit(*(natok+1))) ) {
(void) sprintf(buffer, "%s", natok);
} else {
whereisgt = strchr(natok, '>');
*whereisgt = '}';
(void) sprintf(buffer, "{%s>", natok);
}
/* all other tokens */
} else {
(void) sprintf(buffer, "{%s}", natok);
}
tfree(natok);
wl->wl_word = copy(buffer);
tfree(buffer);
wl->wl_next = nwl;
if (nwl)
nwl->wl_prev = wl;
nwl = wl;
}
nwl = wl_reverse(nwl);
/* new vector elements */
newvec = wl_flatten(nwl);
wl_free(nwl);
/* insert new vector into actual line */
*equal_ptr = '\0';
new_str = tmalloc( strlen(c->li_line) + strlen(newvec) + strlen(end_of_str+1) + 5 );
sprintf( new_str, "%s=[%s] %s", c->li_line, newvec, end_of_str+1 );
tfree(newvec);
old_str = c->li_line;
c->li_line = new_str;
line = new_str + strlen(old_str) + 1;
tfree(old_str);
} else if (*beg_of_str == '<') {
/* A complex value following the '=' token: code to put curly brackets around all params
inside a pair < > */
end_of_str = beg_of_str;
n = 0;
while (*end_of_str != '>') {
end_of_str++;
n++;
}
vec_str = tmalloc(n); /* string xx yyy from vector [xx yyy] */
*vec_str = '\0';
strncat(vec_str, beg_of_str + 1, n - 1);
/* work on tokens inside <> */
nwl = NULL;
for (;;) {
natok = gettok(&vec_str);
if (!natok) break;
wl = alloc(struct wordlist);
buffer = tmalloc(strlen(natok) + 4);
if ( isdigit(*natok) || *natok == '{' || *natok == '.' ||
*natok == '"' || ( *natok == '-' && isdigit(*(natok+1)) ) ||
ciprefix("true", natok) || ciprefix("false", natok)) {
(void) sprintf(buffer, "%s", natok);
} else {
(void) sprintf(buffer, "{%s}", natok);
}
tfree(natok);
wl->wl_word = copy(buffer);
tfree(buffer);
wl->wl_next = nwl;
if (nwl)
nwl->wl_prev = wl;
nwl = wl;
}
nwl = wl_reverse(nwl);
/* new elements of complex variable */
newvec = wl_flatten(nwl);
wl_free(nwl);
/* insert new complex value into actual line */
*equal_ptr = '\0';
new_str = tmalloc( strlen(c->li_line) + strlen(newvec) + strlen(end_of_str+1) + 5 );
sprintf( new_str, "%s=<%s> %s", c->li_line, newvec, end_of_str+1 );
tfree(newvec);
old_str = c->li_line;
c->li_line = new_str;
line = new_str + strlen(old_str) + 1;
tfree(old_str);
} else {
/* put {} around token to be accepted as numparam */
end_of_str = beg_of_str;
while ( *end_of_str != '\0' && (!isspace(*end_of_str) || has_paren) ) {
if ( *end_of_str == '(' ) has_paren = TRUE;
if ( *end_of_str == ')' ) has_paren = FALSE;
end_of_str++;
}
*equal_ptr = '\0';
if ( *end_of_str == '\0' ) {
new_str = tmalloc( strlen(c->li_line) + strlen(beg_of_str) + 4 );
sprintf( new_str, "%s={%s}", c->li_line, beg_of_str );
} else {
*end_of_str = '\0';
new_str = tmalloc( strlen(c->li_line) + strlen(beg_of_str) + strlen(end_of_str+1) + 5 );
sprintf( new_str, "%s={%s} %s", c->li_line, beg_of_str, end_of_str+1 );
}
old_str = c->li_line;
c->li_line = new_str;
line = new_str + strlen(old_str) + 1;
tfree(old_str);
}
}
c = c->li_next;
}
}
static char*
get_param_name( char *line )
{
char *name, *equal_ptr, *beg;
char keep;
if ( ( equal_ptr = strstr( line, "=" ) ) )
{
equal_ptr--;
while ( isspace(*equal_ptr) ) equal_ptr--;
equal_ptr++;
beg = equal_ptr-1;
while ( !isspace(*beg) && beg != line ) beg--;
if ( beg != line ) beg++;
keep = *equal_ptr;
*equal_ptr = '\0';
name = strdup(beg);
*equal_ptr = keep;
}
else
{
fprintf( stderr, "ERROR: could not find '=' on parameter line '%s'!\n", line );
#ifdef HAS_WINDOWS
winmessage("Fatal error in SPICE");
#endif
exit(-1);
}
return name;
}
static char*
get_param_str( char *line )
{
char *equal_ptr;
if ( ( equal_ptr = strstr( line, "=" ) ) ) {
equal_ptr++;
while ( isspace(*equal_ptr) ) equal_ptr++;
return equal_ptr;
}
return line;
}
static int
//inp_get_param_level( int param_num, char *depends_on[12000][100], char *param_names[12000], char *param_strs[12000], int total_params, int *level )
inp_get_param_level( int param_num, char ***depends_on, char **param_names, char **param_strs, int total_params, int *level )
{
int index1 = 0, comp_level = 0, temp_level = 0;
int index2 = 0;
if ( *(level+param_num) != -1 ) return *(level+param_num);
while ( depends_on[param_num][index1] != NULL )
{
index2 = 0;
while ( index2 <= total_params && param_names[index2] != depends_on[param_num][index1] ) index2++;
if ( index2 > total_params )
{
fprintf( stderr, "ERROR: unable to find dependency parameter for %s!\n", param_names[param_num] );
#ifdef HAS_WINDOWS
winmessage("Fatal error in SPICE");
#endif
exit( -1 );
}
temp_level = inp_get_param_level( index2, depends_on, param_names, param_strs, total_params, level );
temp_level++;
if ( temp_level > comp_level ) comp_level = temp_level;
index1++;
}
*(level+param_num) = comp_level;
return comp_level;
}
static int
get_number_terminals( char *c )
{
int i, j, k;
char *name[12];
char nam_buf[33];
bool area_found = FALSE;
switch (*c) {
case 'r': case 'c': case 'l': case 'k': case 'f': case 'h': case 'b':
case 'v': case 'i': case 'w': case 'd':
return 2;
break;
case 'u': case 'j': case 'z':
return 3;
break;
case 't': case 'o': case 'g': case 'e': case 's': case 'y':
return 4;
break;
case 'm': /* recognition of 4, 5, 6, or 7 nodes for SOI devices needed */
i = 0;
/* find the first token with "off" or "=" in the line*/
while ( (i < 20) && (*c != '\0') ) {
char *inst = gettok_instance(&c);
strncpy(nam_buf, inst, 32);
txfree(inst);
if (strstr(nam_buf, "off") || strstr(nam_buf, "=")) break;
i++;
}
return i-2;
break;
case 'p': /* recognition of up to 100 cpl nodes */
i = j = 0;
/* find the last token in the line*/
while ( (i < 100) && (*c != '\0') ) {
strncpy(nam_buf, gettok_instance(&c), 32);
if (strstr(nam_buf, "=")) j++;
i++;
}
if (i == 100) return 0;
return i-j-2;
break;
case 'q': /* recognition of 3/4 terminal bjt's needed */
/* QXXXXXXX NC NB NE <NS> MNAME <AREA> <OFF> <IC=VBE, VCE> <TEMP=T> */
/* 12 tokens maximum */
i = j = 0;
while ( (i < 12) && (*c != '\0') ) {
char* comma;
name[i] = gettok_instance(&c);
if (strstr(name[i], "off") || strstr(name[i], "=")) j++;
/* If we have IC=VBE, VCE instead of IC=VBE,VCE we need to inc j */
if ((comma = strstr(name[i], ",")) && ( *(++comma) == '\0')) j++;
/* If we have IC=VBE , VCE ("," is a token) we need to inc j */
if (eq(name[i], ",")) j++;
i++;
}
i--;
area_found = FALSE;
for (k = i; k > i-j-1; k--) {
bool only_digits = TRUE;
char* nametmp = name[k];
/* MNAME has to contain at least one alpha character. AREA may be assumed
if we have a token with only digits, and where the previous token does not
end with a ',' */
while (*nametmp) {
if (isalpha(*nametmp) || (*nametmp == ',')) only_digits = FALSE;
nametmp++;
}
if (only_digits && (strstr(name[k-1],",") == NULL)) area_found = TRUE;
}
if (area_found) {
return i-j-2;
} else {
return i-j-1;
}
break;
default:
return 0;
break;
}
}
/* sort parameters based on parameter dependencies */
static void
inp_sort_params( struct line *start_card, struct line *end_card, struct line *card_bf_start, struct line *s_c, struct line *e_c )
{
char *param_name = NULL, *param_str = NULL, *param_ptr = NULL;
int i, j, num_params = 0, ind = 0, max_level = 0, num_terminals = 0;
bool in_control = FALSE;
bool found_in_list = FALSE;
struct line *ptr;
char *curr_line;
char *str_ptr, *beg, *end, *new_str;
int skipped = 0;
int arr_size = 12000;
int *level;
int *param_skip;
char **param_names;
char **param_strs;
char ***depends_on;
struct line **ptr_array;
struct line **ptr_array_ordered;
if ( start_card == NULL ) return;
/* determine the number of lines with .param */
ptr = start_card;
while ( ptr != NULL )
{
if ( strstr( ptr->li_line, "=" ) ) {
num_params++;
}
ptr = ptr->li_next;
}
arr_size = num_params;
num_params = 0; /* This is just to keep the code in row 2907ff. */
// dynamic memory allocation
level = (int *) tmalloc(arr_size*sizeof(int));
param_skip = (int *) tmalloc(arr_size*sizeof(int));
param_names = (char **) tmalloc(arr_size*sizeof(char*));
param_strs = (char **) tmalloc(arr_size*sizeof(char*));
/* array[row][column] -> depends_on[array_size][100] */
/* rows */
depends_on = (char ***) tmalloc(sizeof(char **) * arr_size);
/* columns */
for (i = 0; i < arr_size; i++)
{
depends_on[i] = (char **) tmalloc(sizeof(char *) * 100);
}
ptr_array = (struct line **) tmalloc(arr_size*sizeof(struct line *));
ptr_array_ordered = (struct line **) tmalloc(arr_size*sizeof(struct line *));
ptr = start_card;
while ( ptr != NULL )
{
// ignore .param lines without '='
if ( strstr( ptr->li_line, "=" ) ) {
depends_on[num_params][0] = NULL;
level[num_params] = -1;
param_names[num_params] = get_param_name( ptr->li_line ); /* strdup in fcn */
param_strs[num_params] = strdup( get_param_str( ptr->li_line ) );
ptr_array[num_params++] = ptr;
}
ptr = ptr->li_next;
}
// look for duplicately defined parameters and mark earlier one to skip
// param list is ordered as defined in netlist
for ( i = 0; i < num_params; i++ )
param_skip[i] = 0;
for ( i = 0; i < num_params; i++ )
for ( j = num_params-1; j >= 0; j-- )
{
if ( i != j && i < j && strcmp( param_names[i], param_names[j] ) == 0 ) {
// skip earlier one in list
param_skip[i] = 1;
skipped++;
}
}
for ( i = 0; i < num_params; i++ )
{
if ( param_skip[i] == 1 ) continue;
param_name = param_names[i];
for ( j = 0; j < num_params; j++ )
{
if ( j == i ) continue;
param_str = param_strs[j];
while ( ( param_ptr = strstr( param_str, param_name ) ) )
{
if ( !isalnum( *(param_ptr-1) ) && *(param_ptr-1) != '_' &&
!isalnum( *(param_ptr+strlen(param_name)) ) && *(param_ptr+strlen(param_name)) != '_' )
{
ind = 0;
found_in_list = FALSE;
while ( depends_on[j][ind] != NULL ) {
if ( strcmp( param_name, depends_on[j][ind] ) == 0 ) { found_in_list = TRUE; break; }
ind++;
}
if ( !found_in_list ) {
depends_on[j][ind++] = param_name;
depends_on[j][ind] = NULL;
}
break;
}
param_str = param_ptr + strlen(param_name);
}
}
}
for ( i = 0; i < num_params; i++ )
{
level[i] = inp_get_param_level( i, depends_on, param_names, param_strs, num_params, level );
if ( level[i] > max_level ) max_level = level[i];
}
/* look for unquoted parameters and quote them */
ptr = s_c;
in_control = FALSE;
while ( ptr != NULL && ptr != e_c )
{
curr_line = ptr->li_line;
if ( ciprefix( ".control", curr_line ) ) { in_control = TRUE; ptr = ptr->li_next; continue; }
if ( ciprefix( ".endc", curr_line ) ) { in_control = FALSE; ptr = ptr->li_next; continue; }
if ( in_control || curr_line[0] == '.' || curr_line[0] == '*' ) { ptr = ptr->li_next; continue; }
num_terminals = get_number_terminals( curr_line );
if ( num_terminals <= 0 ) { ptr = ptr->li_next; continue; }
for ( i = 0; i < num_params; i++ )
{
str_ptr = curr_line;
for ( j = 0; j < num_terminals+1; j++ )
{
while ( !isspace(*str_ptr) && *str_ptr != '\0' ) str_ptr++;
while ( isspace(*str_ptr) && *str_ptr != '\0' ) str_ptr++;
}
while ( ( str_ptr = strstr( str_ptr, param_names[i] ) ) )
{
/* make sure actually have the parameter name */
char before = *(str_ptr-1);
char after = *(str_ptr+strlen(param_names[i]));
if ( !( is_arith_char(before) || isspace(before) || (str_ptr-1) < curr_line ) ||
!( is_arith_char(after) || isspace(after) || after == '\0' ) ) {
str_ptr = str_ptr + 1;
continue;
}
beg = str_ptr - 1;
end = str_ptr + strlen(param_names[i]);
if ( ( isspace(*beg) || *beg == '=' ) &&
( isspace(*end) || *end == '\0' ) )
{
*str_ptr = '\0';
if ( *end != '\0' )
{
new_str = tmalloc( strlen(curr_line) + strlen(param_names[i]) + strlen(end) + 3 );
sprintf( new_str, "%s{%s}%s", curr_line, param_names[i], end );
}
else
{
new_str = tmalloc( strlen(curr_line) + strlen(param_names[i]) + 3 );
sprintf( new_str, "%s{%s}", curr_line, param_names[i] );
}
str_ptr = new_str + strlen(curr_line) + strlen(param_names[i]);
tfree(ptr->li_line);
curr_line = ptr->li_line = new_str;
}
str_ptr++;
}
}
ptr = ptr->li_next;
}
ind = 0;
for ( i = 0; i <= max_level; i++ )
for ( j = num_params-1; j >= 0; j-- )
{
if ( level[j] == i ) {
if ( param_skip[j] == 0 ) {
ptr_array_ordered[ind++] = ptr_array[j];
}
}
}
num_params -= skipped;
if ( ind != num_params )
{
fprintf( stderr, "ERROR: found wrong number of parameters during levelization ( %d instead of %d parameter s)!\n", ind, num_params );
#ifdef HAS_WINDOWS
winmessage("Fatal error in SPICE");
#endif
exit(-1);
}
/* fix next ptrs */
ptr = card_bf_start->li_next;
card_bf_start->li_next = ptr_array_ordered[0];
ptr_array_ordered[num_params-1]->li_next = ptr;
for ( i = 0; i < num_params-1; i++ )
ptr_array_ordered[i]->li_next = ptr_array_ordered[i+1];
// clean up memory
for ( i = 0; i < num_params; i++ )
{
tfree( param_names[i] );
tfree( param_strs[i] );
}
tfree(level);
tfree(param_skip);
tfree(param_names);
tfree(param_strs);
for (i = 0; i< arr_size; i++)
tfree(depends_on[i]);
tfree(depends_on);
tfree(ptr_array);
tfree(ptr_array_ordered);
}
static void
inp_add_params_to_subckt( struct line *subckt_card )
{
struct line *card = subckt_card->li_next;
char *curr_line = card->li_line;
char *subckt_line = subckt_card->li_line;
char *new_line, *param_ptr, *subckt_name, *end_ptr;
char keep;
while ( card != NULL && ciprefix( ".param", curr_line ) ) {
param_ptr = strstr( curr_line, " " );
while ( isspace(*param_ptr) ) param_ptr++;
if ( !strstr( subckt_line, "params:" ) ) {
new_line = tmalloc( strlen(subckt_line) + strlen("params: ") + strlen(param_ptr) + 2 );
sprintf( new_line, "%s params: %s", subckt_line, param_ptr );
subckt_name = subckt_card->li_line;
while ( !isspace(*subckt_name) ) subckt_name++;
while ( isspace(*subckt_name) ) subckt_name++;
end_ptr = subckt_name;
while ( !isspace(*end_ptr) ) end_ptr++;
keep = *end_ptr;
*end_ptr = '\0';
subckt_w_params[num_subckt_w_params++] = strdup(subckt_name);
*end_ptr = keep;
} else {
new_line = tmalloc( strlen(subckt_line) + strlen(param_ptr) + 2 );
sprintf( new_line, "%s %s", subckt_line, param_ptr );
}
tfree( subckt_line );
subckt_card->li_line = subckt_line = new_line;
*curr_line = '*';
card = card->li_next;
curr_line = card->li_line;
}
}
static void
inp_reorder_params( struct line *deck, struct line *list_head, struct line *end )
{
struct line *c = deck, *subckt_card = NULL, *param_card = NULL, *prev_card = list_head;
struct line *subckt_param_card = NULL, *first_param_card = NULL, *first_subckt_param_card = NULL;
char *curr_line;
bool processing_subckt = FALSE;
/* move .param lines to beginning of deck */
while ( c != NULL ) {
curr_line = c->li_line;
if ( *curr_line == '*' ) { c = c->li_next; continue; }
if ( ciprefix( ".subckt", curr_line ) ) {
processing_subckt = TRUE;
subckt_card = c;
first_subckt_param_card = NULL;
}
if ( ciprefix( ".ends", curr_line ) && processing_subckt ) {
processing_subckt = FALSE;
if ( first_subckt_param_card != NULL ) {
inp_sort_params( first_subckt_param_card, subckt_param_card, subckt_card, subckt_card, c );
inp_add_params_to_subckt( subckt_card );
}
}
if ( ciprefix( ".param", curr_line ) ) {
if ( !processing_subckt ) {
if ( first_param_card == NULL ) {
first_param_card = c;
} else {
param_card->li_next = c;
}
param_card = c;
prev_card->li_next = c->li_next;
param_card->li_next = NULL;
c = prev_card;
} else {
if ( first_subckt_param_card == NULL ) {
first_subckt_param_card = c;
} else {
subckt_param_card->li_next = c;
}
subckt_param_card = c;
prev_card->li_next = c->li_next;
c = prev_card;
subckt_param_card->li_next = NULL;
}
}
prev_card = c;
c = c->li_next;
}
inp_sort_params( first_param_card, param_card, list_head, deck, end );
}
// iterate through deck and find lines with multiply defined parameters
//
// split line up into multiple lines and place those new lines immediately
// afetr the current multi-param line in the deck
static int
inp_split_multi_param_lines( struct line *deck, int line_num )
{
struct line *card = deck, *param_end = NULL, *param_beg = NULL, *prev = NULL, *tmp_ptr;
char *curr_line, *equal_ptr, *beg_param, *end_param, *new_line;
char *array[5000];
int counter = 0, i;
bool get_expression = FALSE, get_paren_expression = FALSE;
char keep;
while ( card != NULL )
{
curr_line = card->li_line;
if ( *curr_line == '*' ) { card = card->li_next; continue; }
if ( ciprefix( ".param", curr_line ) )
{
counter = 0;
while ( ( equal_ptr = strstr( curr_line, "=" ) ) ) {
// check for equality '=='
if ( *(equal_ptr+1) == '=' ) { curr_line = equal_ptr+2; continue; }
// check for '!=', '<=', '>='
if ( *(equal_ptr-1) == '!' || *(equal_ptr-1) == '<' || *(equal_ptr-1) == '>' ) { curr_line = equal_ptr+1; continue; }
counter++; curr_line = equal_ptr + 1;
}
if ( counter <= 1 ) { card = card->li_next; continue; }
// need to split multi param line
curr_line = card->li_line;
counter = 0;
while ( curr_line < card->li_line+strlen(card->li_line) && ( equal_ptr = strstr( curr_line, "=" ) ) )
{
// check for equality '=='
if ( *(equal_ptr+1) == '=' ) { curr_line = equal_ptr+2; continue; }
// check for '!=', '<=', '>='
if ( *(equal_ptr-1) == '!' || *(equal_ptr-1) == '<' || *(equal_ptr-1) == '>' ) { curr_line = equal_ptr+1; continue; }
beg_param = equal_ptr - 1;
end_param = equal_ptr + 1;
while ( isspace(*beg_param) ) beg_param--;
while ( !isspace(*beg_param) ) beg_param--;
while ( isspace(*end_param) ) end_param++;
while ( *end_param != '\0' && (!isspace(*end_param) || get_expression || get_paren_expression) ) {
if ( *end_param == '{' ) get_expression = TRUE;
if ( *end_param == '(' ) get_paren_expression = TRUE;
if ( *end_param == '}' ) get_expression = FALSE;
if ( *end_param == ')' ) get_paren_expression = FALSE;
end_param++;
}
beg_param++;
keep = *end_param;
*end_param = '\0';
new_line = tmalloc( strlen(".param ") + strlen(beg_param) + 1 );
sprintf( new_line, ".param %s", beg_param );
array[counter++] = new_line;
*end_param = keep;
curr_line = end_param;
}
tmp_ptr = card->li_next;
for ( i = 0; i < counter; i++ )
{
if ( param_end )
{
param_end->li_next = alloc(struct line);
param_end = param_end->li_next;
}
else
{
param_end = param_beg = alloc(struct line);
}
param_end->li_next = NULL;
param_end->li_error = NULL;
param_end->li_actual = NULL;
param_end->li_line = array[i];
param_end->li_linenum = line_num++;
}
// comment out current multi-param line
*(card->li_line) = '*';
// insert new param lines immediately after current line
tmp_ptr = card->li_next;
card->li_next = param_beg;
param_end->li_next = tmp_ptr;
// point 'card' pointer to last in scalar list
card = param_end;
param_beg = param_end = NULL;
} // if ( ciprefix( ".param", curr_line ) )
prev = card;
card = card->li_next;
} // while ( card != NULL )
if ( param_end )
{
prev->li_next = param_beg;
prev = param_end;
}
return line_num;
}