/********** 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 #define HAVE_DECL_BASENAME 1 #endif #ifdef HAVE_LIBIBERTY_H /* asprintf etc. */ #include #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 #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 -- .[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 */ 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 */ if ( !*y ) { keep_char1 = *t; *t = '\0'; if ( lib_name != NULL && strcmp( lib_name, s ) == 0 ) read_line = TRUE; *t = keep_char1; } /* .lib */ 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> ] */ /* < xx and yy > have been dealt with before */ /* */ } 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 MNAME */ /* 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; }